From 34dbdead1e26567a3262979e38e9272b1e5d23af Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 18 Nov 2010 13:45:40 +0000 Subject: fd.o #31720: use TpDBusDaemon to export channel objects --- src/sip-media-channel.c | 6 +++--- src/sip-text-channel.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sip-media-channel.c b/src/sip-media-channel.c index c6d6df5..27c4a3d 100644 --- a/src/sip-media-channel.c +++ b/src/sip-media-channel.c @@ -189,7 +189,7 @@ tpsip_media_channel_constructed (GObject *obj) TpBaseConnection *conn = (TpBaseConnection *)(priv->conn); GObjectClass *parent_object_class = G_OBJECT_CLASS (tpsip_media_channel_parent_class); - DBusGConnection *bus; + TpDBusDaemon *bus; TpHandleRepoIface *contact_repo; TpIntSet *set; @@ -203,10 +203,10 @@ tpsip_media_channel_constructed (GObject *obj) tp_handle_ref (contact_repo, priv->handle); /* register object on the bus */ - bus = tp_get_bus (); + bus = tp_base_connection_get_dbus_daemon (conn); DEBUG("registering object to dbus path=%s", priv->object_path); - dbus_g_connection_register_g_object (bus, priv->object_path, obj); + tp_dbus_daemon_register_object (bus, priv->object_path, obj); /* initialize group mixin */ tp_group_mixin_init (obj, diff --git a/src/sip-text-channel.c b/src/sip-text-channel.c index 23159fd..49e0b5a 100644 --- a/src/sip-text-channel.c +++ b/src/sip-text-channel.c @@ -161,7 +161,7 @@ tpsip_text_channel_constructed (GObject *obj) TpsipTextChannelPrivate *priv; TpBaseConnection *base_conn; TpHandleRepoIface *contact_handles; - DBusGConnection *bus; + TpDBusDaemon *bus; TpChannelTextMessageType types[] = { TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL, }; @@ -201,8 +201,8 @@ tpsip_text_channel_constructed (GObject *obj) TP_DELIVERY_REPORTING_SUPPORT_FLAG_RECEIVE_SUCCESSES, supported_content_types); - bus = tp_get_bus(); - dbus_g_connection_register_g_object(bus, priv->object_path, obj); + bus = tp_base_connection_get_dbus_daemon (base_conn); + tp_dbus_daemon_register_object (bus, priv->object_path, obj); } -- cgit v1.2.3 From 522e78d6eb471503f8cb5e955ba3bb5aae843292 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 18 Nov 2010 13:46:06 +0000 Subject: fd.o #31720: use TpDBusDaemon to export media sessions --- src/sip-media-channel.c | 2 ++ src/sip-media-session.c | 27 ++++++++++++++++++++++----- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/sip-media-channel.c b/src/sip-media-channel.c index 27c4a3d..8f896ed 100644 --- a/src/sip-media-channel.c +++ b/src/sip-media-channel.c @@ -1622,6 +1622,8 @@ priv_create_session (TpsipMediaChannel *channel, NULL); session = g_object_new (TPSIP_TYPE_MEDIA_SESSION, + "dbus-daemon", + tp_base_connection_get_dbus_daemon (conn), "media-channel", channel, "object-path", object_path, "nua-handle", nh, diff --git a/src/sip-media-session.c b/src/sip-media-session.c index 73b7482..8159f18 100644 --- a/src/sip-media-session.c +++ b/src/sip-media-session.c @@ -72,6 +72,7 @@ enum enum { PROP_MEDIA_CHANNEL = 1, + PROP_DBUS_DAEMON, PROP_OBJECT_PATH, PROP_NUA_OP, PROP_PEER, @@ -122,6 +123,7 @@ typedef struct _TpsipMediaSessionPrivate TpsipMediaSessionPrivate; struct _TpsipMediaSessionPrivate { + TpDBusDaemon *dbus_daemon; TpsipMediaChannel *channel; /* see gobj. prop. 'media-channel' */ gchar *object_path; /* see gobj. prop. 'object-path' */ nua_handle_t *nua_op; /* see gobj. prop. 'nua-handle' */ @@ -192,14 +194,13 @@ tpsip_media_session_constructor (GType type, guint n_props, { GObject *obj; TpsipMediaSessionPrivate *priv; - DBusGConnection *bus; obj = G_OBJECT_CLASS (tpsip_media_session_parent_class)-> constructor (type, n_props, props); priv = TPSIP_MEDIA_SESSION_GET_PRIVATE (TPSIP_MEDIA_SESSION (obj)); - bus = tp_get_bus (); - dbus_g_connection_register_g_object (bus, priv->object_path, obj); + g_assert (TP_IS_DBUS_DAEMON (priv->dbus_daemon)); + tp_dbus_daemon_register_object (priv->dbus_daemon, priv->object_path, obj); return obj; } @@ -212,7 +213,11 @@ static void tpsip_media_session_get_property (GObject *object, TpsipMediaSession *session = TPSIP_MEDIA_SESSION (object); TpsipMediaSessionPrivate *priv = TPSIP_MEDIA_SESSION_GET_PRIVATE (session); - switch (property_id) { + switch (property_id) + { + case PROP_DBUS_DAEMON: + g_value_set_object (value, priv->dbus_daemon); + break; case PROP_MEDIA_CHANNEL: g_value_set_object (value, priv->channel); break; @@ -296,7 +301,12 @@ static void tpsip_media_session_set_property (GObject *object, TpsipMediaSession *session = TPSIP_MEDIA_SESSION (object); TpsipMediaSessionPrivate *priv = TPSIP_MEDIA_SESSION_GET_PRIVATE (session); - switch (property_id) { + switch (property_id) + { + case PROP_DBUS_DAEMON: + g_assert (priv->dbus_daemon == NULL); /* construct-only */ + priv->dbus_daemon = g_value_dup_object (value); + break; case PROP_MEDIA_CHANNEL: priv->channel = TPSIP_MEDIA_CHANNEL (g_value_get_object (value)); break; @@ -343,6 +353,11 @@ tpsip_media_session_class_init (TpsipMediaSessionClass *klass) object_class->dispose = tpsip_media_session_dispose; object_class->finalize = tpsip_media_session_finalize; + param_spec = g_param_spec_object ("dbus-daemon", "TpDBusDaemon", + "Connection to D-Bus.", 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_object ("media-channel", "TpsipMediaChannel object", "SIP media channel object that owns this media session object" " (not reference counted).", @@ -442,6 +457,8 @@ tpsip_media_session_dispose (GObject *object) if (priv->glare_timer_id) g_source_remove (priv->glare_timer_id); + tp_clear_object (&priv->dbus_daemon); + if (G_OBJECT_CLASS (tpsip_media_session_parent_class)->dispose) G_OBJECT_CLASS (tpsip_media_session_parent_class)->dispose (object); -- cgit v1.2.3 From 2003aeb88be1545739f92dcd3a4c99e7d0898d07 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 18 Nov 2010 13:46:18 +0000 Subject: fd.o #31720: use TpDBusDaemon to export media streams --- src/sip-media-session.c | 1 + src/sip-media-stream.c | 21 +++++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/sip-media-session.c b/src/sip-media-session.c index 8159f18..8272200 100644 --- a/src/sip-media-session.c +++ b/src/sip-media-session.c @@ -2135,6 +2135,7 @@ tpsip_media_session_add_stream (TpsipMediaSession *self, direction &= ~TP_MEDIA_STREAM_DIRECTION_RECEIVE; stream = g_object_new (TPSIP_TYPE_MEDIA_STREAM, + "dbus-daemon", priv->dbus_daemon, "media-session", self, "media-type", media_type, "object-path", object_path, diff --git a/src/sip-media-stream.c b/src/sip-media-stream.c index 37e14df..d3826bb 100644 --- a/src/sip-media-stream.c +++ b/src/sip-media-stream.c @@ -84,6 +84,7 @@ static guint signals[SIG_LAST_SIGNAL] = {0}; enum { PROP_MEDIA_SESSION = 1, + PROP_DBUS_DAEMON, PROP_OBJECT_PATH, PROP_ID, PROP_MEDIA_TYPE, @@ -105,6 +106,7 @@ typedef struct _TpsipMediaStreamPrivate TpsipMediaStreamPrivate; struct _TpsipMediaStreamPrivate { + TpDBusDaemon *dbus_daemon; TpsipMediaSession *session; /* see gobj. prop. 'media-session' */ gchar *object_path; /* see gobj. prop. 'object-path' */ guint id; /* see gobj. prop. 'id' */ @@ -200,7 +202,6 @@ tpsip_media_stream_constructed (GObject *obj) TPSIP_MEDIA_STREAM (obj)); GObjectClass *parent_object_class = G_OBJECT_CLASS (tpsip_media_stream_parent_class); - DBusGConnection *bus; /* call base class method */ if (parent_object_class->constructed != NULL) @@ -220,8 +221,8 @@ tpsip_media_stream_constructed (GObject *obj) } /* go for the bus */ - bus = tp_get_bus (); - dbus_g_connection_register_g_object (bus, priv->object_path, obj); + g_assert (TP_IS_DBUS_DAEMON (priv->dbus_daemon)); + tp_dbus_daemon_register_object (priv->dbus_daemon, priv->object_path, obj); } static void @@ -235,6 +236,9 @@ tpsip_media_stream_get_property (GObject *object, switch (property_id) { + case PROP_DBUS_DAEMON: + g_value_set_object (value, priv->dbus_daemon); + break; case PROP_MEDIA_SESSION: g_value_set_object (value, priv->session); break; @@ -288,6 +292,10 @@ tpsip_media_stream_set_property (GObject *object, switch (property_id) { + case PROP_DBUS_DAEMON: + g_assert (priv->dbus_daemon == NULL); /* construct-only */ + priv->dbus_daemon = g_value_dup_object (value); + break; case PROP_MEDIA_SESSION: priv->session = g_value_get_object (value); break; @@ -358,6 +366,11 @@ tpsip_media_stream_class_init (TpsipMediaStreamClass *klass) object_class->dispose = tpsip_media_stream_dispose; object_class->finalize = tpsip_media_stream_finalize; + param_spec = g_param_spec_object ("dbus-daemon", "TpDBusDaemon", + "Connection to D-Bus.", 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_object ("media-session", "TpsipMediaSession object", "SIP media session object that owns this media stream object.", TPSIP_TYPE_MEDIA_SESSION, @@ -516,7 +529,7 @@ tpsip_media_stream_dispose (GObject *object) priv->dispose_has_run = TRUE; - /* release any references held by the object here */ + tp_clear_object (&priv->dbus_daemon); if (G_OBJECT_CLASS (tpsip_media_stream_parent_class)->dispose) G_OBJECT_CLASS (tpsip_media_stream_parent_class)->dispose (object); -- cgit v1.2.3 From 6babff19f5dafab264a6d3cd5f3af602f7437464 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 18 Nov 2010 18:13:49 +0000 Subject: tests: import constants from Salut --- tests/twisted/constants.py | 400 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 400 insertions(+) create mode 100644 tests/twisted/constants.py diff --git a/tests/twisted/constants.py b/tests/twisted/constants.py new file mode 100644 index 0000000..1ffa4d8 --- /dev/null +++ b/tests/twisted/constants.py @@ -0,0 +1,400 @@ +""" +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_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.DRAFT" +CHANNEL_IFACE_CONFERENCE = CHANNEL + '.Interface.Conference' + +CHANNEL_TYPE_CALL = CHANNEL + ".Type.Call.DRAFT" +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.DRAFT" +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_VIDEO = CHANNEL_TYPE_CALL + '.InitialVideo' +CALL_MUTABLE_CONTENTS = CHANNEL_TYPE_CALL + '.MutableContents' + +CALL_CONTENT = 'org.freedesktop.Telepathy.Call.Content.DRAFT' +CALL_CONTENT_IFACE_MEDIA = \ + 'org.freedesktop.Telepathy.Call.Content.Interface.Media.DRAFT' + +CALL_CONTENT_CODECOFFER = \ + 'org.freedesktop.Telepathy.Call.Content.CodecOffer.DRAFT' + +CALL_STREAM = 'org.freedesktop.Telepathy.Call.Stream.DRAFT' +CALL_STREAM_IFACE_MEDIA = \ + 'org.freedesktop.Telepathy.Call.Stream.Interface.Media.DRAFT' + +CALL_STREAM_ENDPOINT = 'org.freedesktop.Telepathy.Call.Stream.Endpoint.DRAFT' + +CALL_MEDIA_TYPE_AUDIO = 0 +CALL_MEDIA_TYPE_VIDEO = 1 + +CALL_STREAM_TRANSPORT_RAW_UDP = 0 +CALL_STREAM_TRANSPORT_ICE = 1 +CALL_STREAM_TRANSPORT_GOOGLE = 2 + +CALL_STATE_UNKNOWN = 0 +CALL_STATE_PENDING_INITIATOR = 1 +CALL_STATE_PENDING_RECEIVER = 2 +CALL_STATE_ACCEPTED = 3 +CALL_STATE_ENDED = 4 + +CALL_MEMBER_FLAG_RINGING = 1 +CALL_MEMBER_FLAG_HELD = 2 + +CALL_DISPOSITION_NONE = 0 +CALL_DISPOSITION_EARLY_MEDIA = 1 +CALL_DISPOSITION_INITIAL = 2 + +CALL_SENDING_STATE_NONE = 0 +CALL_SENDING_STATE_PENDING_SEND = 1 +CALL_SENDING_STATE_SENDING = 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.DRAFT' +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.DRAFT' +CONN_IFACE_CONTACT_LIST = CONN + '.Interface.ContactList' +CONN_IFACE_CONTACT_GROUPS = CONN + '.Interface.ContactGroups' + +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' + +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 + +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' + +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 + +CALL_STATE_RINGING = 1 +CALL_STATE_QUEUED = 2 +CALL_STATE_HELD = 4 +CALL_STATE_FORWARDED = 8 + +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_MANDATORY = 1 + +# Channel_Type_ServerAuthentication +AUTH_TYPE_SASL = 0 +AUTH_TYPE_CAPTCHA = 1 + +# 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" +AUTH_INFO = CHANNEL_TYPE_SERVER_AUTHENTICATION + ".AuthenticationInformation" +SASL_AVAILABLE_MECHANISMS = CHANNEL_IFACE_SASL_AUTH + ".AvailableMechanisms" + +# Channel_Type_ServerTLSConnection +TLS_CERT_PATH = CHANNEL_TYPE_SERVER_TLS_CONNECTION + ".ServerCertificate" +TLS_HOSTNAME = CHANNEL_TYPE_SERVER_TLS_CONNECTION + ".Hostname" + +# 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 + +PROTOCOL = 'org.freedesktop.Telepathy.Protocol' +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 -- cgit v1.2.3 From 4af95b75fc17a251b167208131a51626ae15dec1 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 18 Nov 2010 18:14:25 +0000 Subject: tests: import servicetest.py from Salut and modernize tests to pass again --- tests/twisted/servicetest.py | 560 ++++++++++++++++++----------- tests/twisted/sofiatest.py | 3 +- tests/twisted/test-handle-normalisation.py | 39 +- tests/twisted/test-message.py | 6 +- 4 files changed, 371 insertions(+), 237 deletions(-) diff --git a/tests/twisted/servicetest.py b/tests/twisted/servicetest.py index ecb44f6..5685eec 100644 --- a/tests/twisted/servicetest.py +++ b/tests/twisted/servicetest.py @@ -6,54 +6,26 @@ 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 pprint -import traceback 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 TryNextHandler(Exception): - pass - -def lazy(func): - def handler(event, data): - if func(event, data): - return True - else: - raise TryNextHandler() - handler.__name__ = func.__name__ - return handler - -def match(type, **kw): - def decorate(func): - def handler(event, data, *extra, **extra_kw): - if event.type != type: - return False - - for key, value in kw.iteritems(): - if not hasattr(event, key): - return False - - if getattr(event, key) != value: - return False - - return func(event, data, *extra, **extra_kw) - - handler.__name__ = func.__name__ - return handler - - return decorate - class Event: def __init__(self, type, **kw): self.__dict__.update(kw) self.type = type + (self.subqueue, self.subtype) = type.split ("-", 1) def format_event(event): ret = ['- type %s' % event.type] @@ -68,113 +40,24 @@ def format_event(event): return ret -class EventTest: - """Somewhat odd event dispatcher for asynchronous tests. - - Callbacks are kept in a queue. Incoming events are passed to the first - callback. If the callback returns True, the callback is removed. If the - callback raises AssertionError, the test fails. If there are no more - callbacks, the test passes. The reactor is stopped when the test passes. - """ - - def __init__(self): - self.queue = [] - self.data = {'test': self} - self.timeout_delayed_call = reactor.callLater(5, self.timeout_cb) - #self.verbose = True - self.verbose = False - # ugh - self.stopping = False - - def timeout_cb(self): - print 'timed out waiting for events' - print self.queue[0] - self.fail() - - def fail(self): - # ugh; better way to stop the reactor and exit(1)? - import os - os._exit(1) - - def expect(self, f): - self.queue.append(f) - - def log(self, s): - if self.verbose: - print s - - def try_stop(self): - if self.stopping: - return True - - if not self.queue: - self.log('no handlers left; stopping') - self.stopping = True - reactor.stop() - return True - - return False - - def call_handlers(self, event): - self.log('trying %r' % self.queue[0]) - handler = self.queue.pop(0) - - try: - ret = handler(event, self.data) - if not ret: - self.queue.insert(0, handler) - except TryNextHandler, e: - if self.queue: - ret = self.call_handlers(event) - else: - ret = False - self.queue.insert(0, handler) - - return ret - - def handle_event(self, event): - if self.try_stop(): - return - - self.log('got event:') - self.log('- type: %s' % event.type) - map(self.log, format_event(event)) - - try: - ret = self.call_handlers(event) - except SystemExit, e: - if e.code: - print "Unsuccessful exit:", e - self.fail() - else: - self.queue[:] = [] - ret = True - except AssertionError, e: - print 'test failed:' - traceback.print_exc() - self.fail() - except (Exception, KeyboardInterrupt), e: - print 'error in handler:' - traceback.print_exc() - self.fail() - - if ret not in (True, False): - print ("warning: %s() returned something other than True or False" - % self.queue[0].__name__) - - if ret: - self.timeout_delayed_call.reset(5) - self.log('event handled') - else: - self.log('event not handled') - - self.log('') - self.try_stop() - 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: @@ -187,7 +70,11 @@ class EventPattern: except AttributeError: return False - return True + if self.predicate is None or self.predicate(event): + return True + + return False + class TimeoutError(Exception): pass @@ -200,6 +87,8 @@ class BaseEventQueue: def __init__(self, timeout=None): self.verbose = False + self.forbidden_events = set() + self.event_queues = {} if timeout is None: self.timeout = 5 @@ -210,16 +99,56 @@ class BaseEventQueue: 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 _check_forbidden(self, event): + for e in self.forbidden_events: + if e.match(event): + print "forbidden event occurred:" + for x in format_event(event): + print x + assert False + 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() - self.log('got event:') - map(self.log, format_event(event)) + event = self.wait([pattern.subqueue]) + self._check_forbidden(event) if pattern.match(event): - self.log('handled') + self.log('handled, took %0.3f ms' + % ((time.time() - t) * 1000.0) ) self.log('') return event @@ -227,16 +156,57 @@ class BaseEventQueue: 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: - event = self.wait() - self.log('got event:') - map(self.log, format_event(event)) + 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 pattern.match(event): - self.log('handled') + 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 @@ -249,9 +219,7 @@ class BaseEventQueue: def demand(self, type, **kw): pattern = EventPattern(type, **kw) - event = self.wait() - self.log('got event:') - map(self.log, format_event(event)) + event = self.wait([pattern.subqueue]) if pattern.match(event): self.log('handled') @@ -261,14 +229,34 @@ class BaseEventQueue: 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) - self.events = [] - def wait(self): + def wait(self, queues=None): stop = [False] def later(): @@ -276,58 +264,92 @@ class IteratingEventQueue(BaseEventQueue): delayed_call = reactor.callLater(self.timeout, later) - while (not self.events) and (not stop[0]): - reactor.iterate(0.1) + 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 self.events: + if qa: delayed_call.cancel() - return self.events.pop(0) + e = self.pop_next (qa[0]) + self.log_event (e) + return e else: raise TimeoutError - def append(self, event): - self.events.append(event) - - # compatibility - handle_event = append - class TestEventQueue(BaseEventQueue): def __init__(self, events): BaseEventQueue.__init__(self) - self.events = events + for e in events: + self.append (e) - def wait(self): - if self.events: - return self.events.pop(0) + 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('foo'), Event('bar')]) - assert queue.expect('foo').type == 'foo' - assert queue.expect('bar').type == 'bar' + 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('foo'), Event('bar')]) + queue = TestEventQueue([Event('test-foo'), + Event('test-bar')]) bar, foo = queue.expect_many( - EventPattern('bar'), - EventPattern('foo')) - assert bar.type == 'bar' - assert foo.type == 'foo' + 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, 'foo') + self.assertRaises(TimeoutError, queue.expect, 'test-foo') def test_demand(self): - queue = TestEventQueue([Event('foo'), Event('bar')]) - foo = queue.demand('foo') - assert foo.type == 'foo' + 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('foo'), Event('bar')]) - self.assertRaises(RuntimeError, queue.demand, 'bar') + 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 @@ -342,7 +364,10 @@ def unwrap(x): if isinstance(x, dict): return dict([(unwrap(k), unwrap(v)) for k, v in x.iteritems()]) - for t in [unicode, str, long, int, float, bool]: + if isinstance(x, dbus.Boolean): + return bool(x) + + for t in [unicode, str, long, int, float]: if isinstance(x, t): return t(x) @@ -353,21 +378,33 @@ def call_async(test, proxy, method, *args, **kw): resulting method return/error.""" def reply_func(*ret): - test.handle_event(Event('dbus-return', method=method, + test.append(Event('dbus-return', method=method, value=unwrap(ret))) def error_func(err): - test.handle_event(Event('dbus-error', method=method, error=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 + # 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, '/') + call_async( + q, dbus.Interface(root_object, 'org.freedesktop.DBus.Peer'), 'Ping') + q.expect('dbus-return', method='Ping') 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()]) @@ -381,8 +418,36 @@ class ProxyWrapper: return getattr(self.default_interface, name) -def prepare_test(event_func, name, proto, params): - bus = dbus.SessionBus() +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', + 'Presence', '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), + ])) + +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 make_connection(bus, event_func, name, proto, params): cm = bus.get_object( tp_name_prefix + '.ConnectionManager.%s' % name, tp_path_prefix + '/ConnectionManager/%s' % name) @@ -390,28 +455,25 @@ def prepare_test(event_func, name, proto, params): connection_name, connection_path = cm_iface.RequestConnection( proto, params) - conn = bus.get_object(connection_name, connection_path) - conn = ProxyWrapper(conn, tp_name_prefix + '.Connection', - dict([ - (name, tp_name_prefix + '.Connection.Interface.' + name) - for name in ['Aliasing', 'Avatars', 'Capabilities', 'Presence']] + - [('Peer', 'org.freedesktop.DBus.Peer')])) + conn = wrap_connection(bus.get_object(connection_name, connection_path)) bus.add_signal_receiver( lambda *args, **kw: event_func( Event('dbus-signal', - path=unwrap(kw['path'])[len(tp_path_prefix):], - signal=kw['member'], args=map(unwrap, args))), + path=unwrap(kw['path']), + signal=kw['member'], args=map(unwrap, args), + interface=kw['interface'])), None, # signal name None, # interface cm._named_service, path_keyword='path', member_keyword='member', + interface_keyword='interface', byte_arrays=True ) - return bus, conn + return conn def make_channel_proxy(conn, path, iface): bus = dbus.SessionBus() @@ -419,36 +481,39 @@ def make_channel_proxy(conn, path, iface): chan = dbus.Interface(chan, tp_name_prefix + '.' + iface) return chan -def load_event_handlers(): - path, _, _, _ = traceback.extract_stack()[0] - import compiler - import __main__ - ast = compiler.parseFile(path) - return [ - getattr(__main__, node.name) - for node in ast.node.asList() - if node.__class__ == compiler.ast.Function and - node.name.startswith('expect_')] - +# 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): + def __init__(self, queue=None, block_reading=False): self.queue = queue def dataReceived(self, data): if self.queue is not None: - self.queue.handle_event(Event('socket-data', protocol=self, + 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): + 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 = EventProtocol(self.queue) - self.queue.handle_event(Event('socket-connected', protocol=proto)) + proto = self._create_protocol() + self.queue.append(Event('socket-connected', protocol=proto)) return proto class EventProtocolClientFactory(EventProtocolFactory, ClientFactory): @@ -456,7 +521,7 @@ class EventProtocolClientFactory(EventProtocolFactory, ClientFactory): def watch_tube_signals(q, tube): def got_signal_cb(*args, **kwargs): - q.handle_event(Event('tube-signal', + q.append(Event('tube-signal', path=kwargs['path'], signal=kwargs['member'], args=map(unwrap, args), @@ -466,6 +531,81 @@ def watch_tube_signals(q, tube): 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 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): + f = self.patterns.get(s, lambda x: x) + self.fh.write(f(s)) + + sys.stdout = Colourer(sys.stdout, patterns) + return sys.stdout + if __name__ == '__main__': unittest.main() diff --git a/tests/twisted/sofiatest.py b/tests/twisted/sofiatest.py index 149cacb..2ec5b75 100644 --- a/tests/twisted/sofiatest.py +++ b/tests/twisted/sofiatest.py @@ -50,7 +50,8 @@ def prepare_test(event_func, register_cb, params=None): if params is not None: actual_params.update(params) - bus, conn = servicetest.prepare_test(event_func, + bus = dbus.SessionBus() + conn = servicetest.make_connection(bus, event_func, 'sofiasip', 'sip', actual_params) port = int(actual_params['port']) diff --git a/tests/twisted/test-handle-normalisation.py b/tests/twisted/test-handle-normalisation.py index cf741eb..710ed72 100644 --- a/tests/twisted/test-handle-normalisation.py +++ b/tests/twisted/test-handle-normalisation.py @@ -1,14 +1,16 @@ -from servicetest import unwrap, match -from sofiatest import go +from servicetest import assertEquals +from sofiatest import exec_test import dbus +import constants as cs -@match('dbus-signal', signal='StatusChanged', args=[1, 1]) -def expect_connecting(event, data): - return True +def test(q, bus, conn, sip): + conn.Connect() + 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]) -@match('dbus-signal', signal='StatusChanged', args=[0, 1]) -def expect_connected(event, data): tests = [ ('sip:test@localhost', 'sip:test@localhost'), ('test@localhost', 'sip:test@localhost'), ('test', 'sip:test@127.0.0.1'), @@ -29,22 +31,15 @@ def expect_connected(event, data): orig = [ x[0] for x in tests ] expect = [ x[1] for x in tests ] - handles = data['conn_iface'].RequestHandles(1, orig) - names = data['conn_iface'].InspectHandles(1, handles) + handles = conn.RequestHandles(1, orig) + names = conn.InspectHandles(1, handles) - for a,b in zip(expect, map(lambda x: str(unwrap(x)), names)): - if a != b: - print a, b - raise Exception("test failed") + for a,b in zip(expect, names): + assertEquals(a, b) - data['conn_iface'].Disconnect() - return True - -@match('dbus-signal', signal='StatusChanged', args=[2, 1]) -def expect_disconnected(event, data): - return True + conn.Disconnect() + q.expect('dbus-signal', signal='StatusChanged', + args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_REQUESTED]) if __name__ == '__main__': - go() - - + exec_test(test) diff --git a/tests/twisted/test-message.py b/tests/twisted/test-message.py index 0f0b61c..95ad159 100644 --- a/tests/twisted/test-message.py +++ b/tests/twisted/test-message.py @@ -135,8 +135,7 @@ def test(q, bus, conn, sip): send_message(sip, ua_via, 'How are you doing now, old pal?', sender=contact) - event = q.expect('dbus-signal', signal='Received') - assert tp_path_prefix + event.path == chan, (event.path, chan) + event = q.expect('dbus-signal', signal='Received', path=chan) assert event.args[5] == 'How are you doing now, old pal?' pending_msgs.append(tuple(event.args)) @@ -150,8 +149,7 @@ def test(q, bus, conn, sip): dbus.Interface(requested_obj, CHANNEL).Close() del requested_obj - event = q.expect('dbus-signal', signal='Closed') - assert tp_path_prefix + event.path == chan, (event.path, chan) + event = q.expect('dbus-signal', signal='Closed', path=chan) requested_obj, handle = test_new_channel (q, bus, conn, target_uri=contact, -- cgit v1.2.3 From b96c4501cca2edda99a94bf3a84165618d53cc92 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 18 Nov 2010 18:14:42 +0000 Subject: tpsip_connection_get_implemented_interfaces: add --- src/sip-connection.c | 19 +++++++++++++------ src/sip-connection.h | 2 ++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/sip-connection.c b/src/sip-connection.c index bc175cf..d383dfb 100644 --- a/src/sip-connection.c +++ b/src/sip-connection.c @@ -422,15 +422,22 @@ static void tpsip_connection_shut_down (TpBaseConnection *base); static gboolean tpsip_connection_start_connecting (TpBaseConnection *base, GError **error); +static const gchar *interfaces_always_present[] = { + TP_IFACE_CONNECTION_INTERFACE_REQUESTS, + TP_IFACE_CONNECTION_INTERFACE_CONTACTS, + TP_IFACE_CONNECTION_INTERFACE_ALIASING, + NULL }; + +const gchar ** +tpsip_connection_get_implemented_interfaces (void) +{ + /* we don't have any conditionally-implemented interfaces */ + return interfaces_always_present; +} + static void tpsip_connection_class_init (TpsipConnectionClass *klass) { - static const gchar *interfaces_always_present[] = { - TP_IFACE_CONNECTION_INTERFACE_REQUESTS, - TP_IFACE_CONNECTION_INTERFACE_CONTACTS, - TP_IFACE_CONNECTION_INTERFACE_ALIASING, - NULL }; - GObjectClass *object_class = G_OBJECT_CLASS (klass); TpBaseConnectionClass *base_class = (TpBaseConnectionClass *)klass; diff --git a/src/sip-connection.h b/src/sip-connection.h index ac025ca..2280be9 100644 --- a/src/sip-connection.h +++ b/src/sip-connection.h @@ -80,6 +80,8 @@ GType tpsip_connection_get_type (void) G_GNUC_CONST; void tpsip_connection_connect_auth_handler (TpsipConnection *self, TpsipEventTarget *target); +const gchar **tpsip_connection_get_implemented_interfaces (void); + G_END_DECLS #endif /* #ifndef __TPSIP_CONNECTION_H__*/ -- cgit v1.2.3 From 5c7b956faeffeda6b4ef2666750398fdb9bb0fba Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 18 Nov 2010 18:15:04 +0000 Subject: tpsip_normalize_contact: add connection-independent normalization --- src/sip-connection-helpers.c | 28 +++++++++++++++++++--------- src/sip-connection-helpers.h | 5 +++++ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/sip-connection-helpers.c b/src/sip-connection-helpers.c index 0fda819..8ad9b42 100644 --- a/src/sip-connection-helpers.c +++ b/src/sip-connection-helpers.c @@ -775,14 +775,11 @@ priv_lowercase_url_part (su_home_t *home, const char *src) #define TPSIP_RESERVED_CHARS_ALLOWED_IN_USERNAME "!*'()&=+$,;?/" gchar * -tpsip_handle_normalize (TpHandleRepoIface *repo, - const gchar *sipuri, - gpointer context, - GError **error) +tpsip_normalize_contact (const gchar *sipuri, + const url_t *base_url, + const gchar *transport, + GError **error) { - TpsipConnection *conn = TPSIP_CONNECTION (context); - TpsipConnectionPrivate *priv = TPSIP_CONNECTION_GET_PRIVATE (conn); - const url_t *base_url = priv->account_url; su_home_t home[1] = { SU_HOME_INIT(home) }; url_t *url; gchar *retval = NULL; @@ -827,8 +824,8 @@ tpsip_handle_normalize (TpHandleRepoIface *repo, { /* Set the scheme to SIP or SIPS accordingly to the connection's * transport preference */ - if (priv->transport != NULL - && g_ascii_strcasecmp (priv->transport, "tls") == 0) + if (transport != NULL + && g_ascii_strcasecmp (transport, "tls") == 0) { url->url_type = url_sips; url->url_scheme = "sips"; @@ -879,6 +876,19 @@ error: return retval; } +gchar * +tpsip_handle_normalize (TpHandleRepoIface *repo, + const gchar *sipuri, + gpointer context, + GError **error) +{ + TpsipConnection *conn = TPSIP_CONNECTION (context); + TpsipConnectionPrivate *priv = TPSIP_CONNECTION_GET_PRIVATE (conn); + + return tpsip_normalize_contact (sipuri, priv->account_url, priv->transport, + error); +} + static GQuark tpsip_handle_url_quark () { diff --git a/src/sip-connection-helpers.h b/src/sip-connection-helpers.h index 327c13c..4644908 100644 --- a/src/sip-connection-helpers.h +++ b/src/sip-connection-helpers.h @@ -66,6 +66,11 @@ gchar * tpsip_handle_normalize (TpHandleRepoIface *repo, gpointer context, GError **error); +gchar *tpsip_normalize_contact (const gchar *sipuri, + const url_t *base_url, + const gchar *transport, + GError **error); + const url_t* tpsip_conn_get_contact_url (TpsipConnection *conn, TpHandle handle); -- cgit v1.2.3 From c847c6bfd4e3d963fd7da2c481fc3078606a4ffe Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 18 Nov 2010 18:16:28 +0000 Subject: Port TpsipConnectionManager to use TpBaseProtocol write-mgr-file.c was completely replaced by the one from Gabble, with trivial changes in main(). --- src/Makefile.am | 2 + src/protocol.c | 515 +++++++++++++++++++++++++++++++++++++++++++ src/protocol.h | 75 +++++++ src/sip-connection-manager.c | 428 ++--------------------------------- src/write-mgr-file.c | 374 +++++++++++++++++++++++++------ 5 files changed, 920 insertions(+), 474 deletions(-) create mode 100644 src/protocol.c create mode 100644 src/protocol.h diff --git a/src/Makefile.am b/src/Makefile.am index 6684513..ca15a87 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -92,6 +92,8 @@ libtpsip_convenience_la_SOURCES = \ debug.c \ media-factory.h \ media-factory.c \ + protocol.h \ + protocol.c \ text-factory.h \ text-factory.c \ sip-connection-helpers.h \ diff --git a/src/protocol.c b/src/protocol.c new file mode 100644 index 0000000..1c8bb57 --- /dev/null +++ b/src/protocol.c @@ -0,0 +1,515 @@ +/* + * protocol.c - source for TpsipProtocol + * 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 "protocol.h" + +#include + +#include +#include +#include + +#include +#include + +#define DEBUG_FLAG TPSIP_DEBUG_CONNECTION +#include "debug.h" +#include "media-factory.h" +#include "sip-connection.h" +#include "sip-connection-helpers.h" +#include "text-factory.h" + +#define PROTOCOL_NAME "sip" +#define ICON_NAME "im-" PROTOCOL_NAME +#define VCARD_FIELD_NAME "x-" PROTOCOL_NAME +#define ENGLISH_NAME "SIP" + +G_DEFINE_TYPE (TpsipProtocol, + tpsip_protocol, + TP_TYPE_BASE_PROTOCOL) + +enum { + PROP_SOFIA_ROOT = 1, +}; + +struct _TpsipProtocolPrivate +{ + su_root_t *sofia_root; +}; + +/* Used in the otherwise-unused offset field of the TpCMParamSpec. The first + * one is nonzero to catch implicit zero-initialization. */ +enum { + PARAM_EASY = 1, + PARAM_SET_SEPARATELY +}; + +static TpCMParamSpec tpsip_params[] = { + /* Account (a sip: URI) + * + * FIXME: validate account SIP URI properly, using appropriate RFCs */ + { "account", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, + TP_CONN_MGR_PARAM_FLAG_REQUIRED | TP_CONN_MGR_PARAM_FLAG_REGISTER, + NULL, PARAM_SET_SEPARATELY, tp_cm_param_filter_string_nonempty, NULL }, + + /* Username to register with, if different than in the account URI */ + { "auth-user", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, + 0, NULL, PARAM_EASY }, + + /* Password */ + { "password", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, + TP_CONN_MGR_PARAM_FLAG_SECRET, NULL, PARAM_EASY }, + + /* Display name for self */ + { "alias", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, 0, NULL, PARAM_EASY, + /* setting a 0-length alias makes no sense */ + tp_cm_param_filter_string_nonempty, NULL }, + + /* Registrar */ + { "registrar", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, 0, NULL, + PARAM_EASY }, + + /* Used to compose proxy URI */ + { "proxy-host", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, 0, NULL, + PARAM_SET_SEPARATELY }, + { "port", DBUS_TYPE_UINT16_AS_STRING, G_TYPE_UINT, + TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER(SIP_DEFAULT_PORT), + PARAM_SET_SEPARATELY, tp_cm_param_filter_uint_nonzero }, + { "transport", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, + TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, "auto", PARAM_SET_SEPARATELY }, + + /* Enables loose routing as per RFC 3261 */ + { "loose-routing", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, + TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER(FALSE), + PARAM_EASY }, + + /* Used to enable proactive NAT traversal techniques */ + { "discover-binding", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, + TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER(TRUE), + PARAM_EASY }, + + /* Mechanism used for connection keepalive maintenance */ + { "keepalive-mechanism", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, + TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, "auto", PARAM_SET_SEPARATELY }, + + /* Keep-alive interval */ + { "keepalive-interval", DBUS_TYPE_UINT32_AS_STRING, G_TYPE_UINT, + TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER(0), PARAM_EASY }, + + /* Use SRV DNS lookup to discover STUN server */ + { "discover-stun", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, + TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER(TRUE), PARAM_EASY }, + + /* STUN server */ + { "stun-server", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, 0, NULL, + PARAM_EASY }, + + /* STUN port */ + { "stun-port", DBUS_TYPE_UINT16_AS_STRING, G_TYPE_UINT, + TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, + GUINT_TO_POINTER(TPSIP_DEFAULT_STUN_PORT), PARAM_EASY, + tp_cm_param_filter_uint_nonzero }, + + /* If the session content cannot be modified once initially set up */ + { "immutable-streams", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, + TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER(FALSE), + PARAM_EASY }, + + /* Local IP address to use, workaround purposes only */ + { "local-ip-address", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, + 0, NULL, PARAM_EASY }, + + /* Local port for SIP, workaround purposes only */ + { "local-port", DBUS_TYPE_UINT16_AS_STRING, G_TYPE_UINT, 0, NULL, + PARAM_EASY }, + + /* Extra-realm authentication */ + { "extra-auth-user", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, + 0, NULL, PARAM_EASY }, + { "extra-auth-password", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, + TP_CONN_MGR_PARAM_FLAG_SECRET, NULL, PARAM_EASY }, + + { NULL } +}; + +static void +tpsip_protocol_init (TpsipProtocol *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TPSIP_TYPE_PROTOCOL, + TpsipProtocolPrivate); +} + +static const TpCMParamSpec * +get_parameters (TpBaseProtocol *self G_GNUC_UNUSED) +{ + return tpsip_params; +} + +static TpsipConnectionKeepaliveMechanism +priv_parse_keepalive (const gchar *str) +{ + if (str == NULL || strcmp (str, "auto") == 0) + return TPSIP_CONNECTION_KEEPALIVE_AUTO; + if (strcmp (str, "register") == 0) + return TPSIP_CONNECTION_KEEPALIVE_REGISTER; + if (strcmp (str, "options") == 0) + return TPSIP_CONNECTION_KEEPALIVE_OPTIONS; + if (strcmp (str, "stun") == 0) + return TPSIP_CONNECTION_KEEPALIVE_STUN; + if (strcmp (str, "off") == 0) + return TPSIP_CONNECTION_KEEPALIVE_NONE; + + WARNING ("unsupported keepalive-method value \"%s\", falling back to auto", str); + return TPSIP_CONNECTION_KEEPALIVE_AUTO; +} + +static gchar * +priv_compose_proxy_uri (const gchar *host, + const gchar *transport, + guint port) +{ + const gchar *scheme = "sip"; + + if (host == NULL) + return NULL; + + /* Set scheme to SIPS if transport is TLS */ + + if (transport != NULL && !g_ascii_strcasecmp (transport, "tls")) + scheme = "sips"; + + /* Format the resulting URI */ + + if (port) + return g_strdup_printf ("%s:%s:%u", scheme, host, port); + else + return g_strdup_printf ("%s:%s", scheme, host); +} + +/** + * Returns a default SIP proxy address based on the public + * SIP address 'sip_address' and . For instance + * "sip:first.surname@company.com" would result in "sip:company.com". + * The SIP stack will further utilize DNS lookups to find the IP address + * for the SIP server responsible for the domain "company.com". + */ +static gchar * +priv_compose_default_proxy_uri (const gchar *sip_address, + const gchar *transport) +{ + char *result = NULL; + char *host; + char *found; + + g_return_val_if_fail (sip_address != NULL, NULL); + + /* skip sip and sips prefixes, updating transport if necessary */ + found = strchr (sip_address, ':'); + if (found != NULL) { + if (g_ascii_strncasecmp ("sip:", sip_address, 4) == 0) + ; + else if (g_ascii_strncasecmp ("sips:", sip_address, 5) == 0) + { + if (transport == NULL || + strcmp (transport, "auto") == 0) + transport = "tls"; + } + else + { + /* error, unknown URI prefix */ + return NULL; + } + + sip_address = found + 1; + } + + /* skip userinfo */ + found = strchr (sip_address, '@'); + if (found != NULL) + sip_address = found + 1; + + /* copy rest of the string */ + host = g_strdup (sip_address); + + /* mark end (before uri-parameters defs or headers) */ + found = strchr (host, ';'); + if (found == NULL) + found = strchr (host, '?'); + if (found != NULL) + *found = '\0'; + + result = priv_compose_proxy_uri (host, transport, 0); + + g_free (host); + + return result; +} + +static TpBaseConnection * +new_connection (TpBaseProtocol *protocol, + GHashTable *params, + GError **error) +{ + TpsipProtocol *self = TPSIP_PROTOCOL (protocol); + TpsipConnection *conn; + guint i; + const gchar *account; + const gchar *transport; + const gchar *proxy_host; + guint16 port; + gchar *proxy; + TpsipConnectionKeepaliveMechanism keepalive_mechanism; + + account = tp_asv_get_string (params, "account"); + transport = tp_asv_get_string (params, "transport"); + port = tp_asv_get_uint32 (params, "port", NULL); + + conn = g_object_new (TPSIP_TYPE_CONNECTION, + "protocol", PROTOCOL_NAME, + "sofia-root", self->priv->sofia_root, + "address", account, + NULL); + + proxy_host = tp_asv_get_string (params, "proxy-host"); + + if (tp_str_empty (proxy_host)) + { + proxy = priv_compose_default_proxy_uri (account, transport); + DEBUG("set outbound proxy address to <%s>, based on <%s>", proxy, + account); + } + else + { + proxy = priv_compose_proxy_uri (proxy_host, transport, port); + } + + g_object_set (conn, + "proxy", proxy, + NULL); + g_free (proxy); + + if (!tp_str_empty (transport) && tp_strdiff (transport, "auto")) + g_object_set (conn, + "transport", transport, + NULL); + + for (i = 0; tpsip_params[i].name != NULL; i++) + { + if (tpsip_params[i].offset == PARAM_SET_SEPARATELY) + { + DEBUG ("Parameter %s is handled specially", tpsip_params[i].name); + break; + } + + g_assert (tpsip_params[i].offset == PARAM_EASY); + + switch (tpsip_params[i].gtype) + { + case G_TYPE_STRING: + { + const gchar *s = tp_asv_get_string (params, + tpsip_params[i].name); + + if (!tp_str_empty (s)) + g_object_set (conn, + tpsip_params[i].name, s, + NULL); + } + break; + + case G_TYPE_UINT: + { + gboolean valid = FALSE; + guint u = tp_asv_get_uint32 (params, + tpsip_params[i].name, &valid); + + if (valid) + g_object_set (conn, + tpsip_params[i].name, u, + NULL); + } + break; + + case G_TYPE_BOOLEAN: + { + gboolean valid = FALSE; + gboolean b = tp_asv_get_boolean (params, tpsip_params[i].name, + &valid); + + if (valid) + g_object_set (conn, + tpsip_params[i].name, b, + NULL); + } + break; + + default: + /* no other parameters have been written yet */ + g_assert_not_reached (); + } + } + + keepalive_mechanism = priv_parse_keepalive (tp_asv_get_string (params, + "keepalive-mechanism")); + g_object_set (conn, + "keepalive-mechanism", keepalive_mechanism, + NULL); + + return TP_BASE_CONNECTION (conn); +} + +static gchar * +normalize_contact (TpBaseProtocol *self G_GNUC_UNUSED, + const gchar *contact, + GError **error) +{ + return tpsip_normalize_contact (contact, NULL, NULL, 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 GStrv +get_interfaces (TpBaseProtocol *self) +{ + return g_new0 (gchar *, 1); +} + +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) tpsip_connection_get_implemented_interfaces ()); + } + + if (channel_managers != NULL) + { + GType types[] = { + TPSIP_TYPE_TEXT_FACTORY, + TPSIP_TYPE_MEDIA_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 void +tpsip_protocol_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + TpsipProtocol *self = TPSIP_PROTOCOL (object); + + switch (property_id) + { + case PROP_SOFIA_ROOT: + g_value_set_pointer (value, self->priv->sofia_root); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +tpsip_protocol_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + TpsipProtocol *self = TPSIP_PROTOCOL (object); + + switch (property_id) + { + case PROP_SOFIA_ROOT: + self->priv->sofia_root = g_value_get_pointer (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +tpsip_protocol_class_init (TpsipProtocolClass *klass) +{ + TpBaseProtocolClass *base_class = (TpBaseProtocolClass *) klass; + GObjectClass *object_class = (GObjectClass *) klass; + GParamSpec *param_spec; + + g_type_class_add_private (klass, sizeof (TpsipProtocolPrivate)); + + 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 = get_interfaces; + base_class->get_connection_details = get_connection_details; + + object_class->get_property = tpsip_protocol_get_property; + object_class->set_property = tpsip_protocol_set_property; + + param_spec = g_param_spec_pointer ("sofia-root", "Sofia-SIP root", + "the root object for Sofia-SIP", + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_SOFIA_ROOT, + param_spec); +} + +TpBaseProtocol * +tpsip_protocol_new (su_root_t *sofia_root) +{ + return g_object_new (TPSIP_TYPE_PROTOCOL, + "name", PROTOCOL_NAME, + "sofia-root", sofia_root, + NULL); +} diff --git a/src/protocol.h b/src/protocol.h new file mode 100644 index 0000000..7470af1 --- /dev/null +++ b/src/protocol.h @@ -0,0 +1,75 @@ +/* + * protocol.h - header for TpsipProtocol + * 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 TPSIP_PROTOCOL_H +#define TPSIP_PROTOCOL_H + +#include +#include + +#include +#include + +G_BEGIN_DECLS + +typedef struct _TpsipProtocol TpsipProtocol; +typedef struct _TpsipProtocolPrivate TpsipProtocolPrivate; +typedef struct _TpsipProtocolClass TpsipProtocolClass; +typedef struct _TpsipProtocolClassPrivate TpsipProtocolClassPrivate; + +struct _TpsipProtocolClass { + TpBaseProtocolClass parent_class; + + TpsipProtocolClassPrivate *priv; +}; + +struct _TpsipProtocol { + TpBaseProtocol parent; + + TpsipProtocolPrivate *priv; +}; + +GType tpsip_protocol_get_type (void); + +#define TPSIP_TYPE_PROTOCOL \ + (tpsip_protocol_get_type ()) +#define TPSIP_PROTOCOL(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + TPSIP_TYPE_PROTOCOL, \ + TpsipProtocol)) +#define TPSIP_PROTOCOL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + TPSIP_TYPE_PROTOCOL, \ + TpsipProtocolClass)) +#define TPSIP_IS_PROTOCOL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + TPSIP_TYPE_PROTOCOL)) +#define TPSIP_PROTOCOL_GET_CLASS(klass) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + TPSIP_TYPE_PROTOCOL, \ + TpsipProtocolClass)) + +gchar *tpsip_protocol_normalize_contact (const gchar *id, + GError **error); + +TpBaseProtocol *tpsip_protocol_new (su_root_t *sofia_root); + +G_END_DECLS + +#endif diff --git a/src/sip-connection-manager.c b/src/sip-connection-manager.c index 7a33481..ecb882e 100644 --- a/src/sip-connection-manager.c +++ b/src/sip-connection-manager.c @@ -38,6 +38,7 @@ #include #include +#include "protocol.h" #include "sip-connection-manager.h" #include "sip-connection.h" @@ -48,165 +49,6 @@ G_DEFINE_TYPE(TpsipConnectionManager, tpsip_connection_manager, TP_TYPE_BASE_CONNECTION_MANAGER) -/* private structure *//* typedef struct _TpsipConnectionManagerPrivate TpsipConnectionManagerPrivate; */ - -typedef struct { - gchar *account; - gchar *auth_user; - gchar *password; - gchar *alias; - gchar *registrar; - gchar *proxy_host; - guint port; - gchar *transport; - gboolean loose_routing; - gboolean discover_binding; - gchar *keepalive_mechanism; - guint keepalive_interval; - gboolean discover_stun; - gchar *stun_server; - guint stun_port; - gboolean immutable_streams; - gchar *local_ip_address; - guint local_port; - gchar *extra_auth_user; - gchar *extra_auth_password; -} TpsipConnParams; - -static void * -alloc_params (void) -{ - return g_slice_new0 (TpsipConnParams); -} - -static void -free_params (void *p) -{ - TpsipConnParams *params = (TpsipConnParams *)p; - - g_free (params->account); - g_free (params->auth_user); - g_free (params->password); - g_free (params->alias); - g_free (params->registrar); - g_free (params->proxy_host); - g_free (params->transport); - g_free (params->keepalive_mechanism); - g_free (params->stun_server); - g_free (params->local_ip_address); - g_free (params->extra_auth_user); - g_free (params->extra_auth_password); - - g_slice_free (TpsipConnParams, params); -} - -enum { - TPSIP_CONN_PARAM_ACCOUNT = 0, - TPSIP_CONN_PARAM_AUTH_USER, - TPSIP_CONN_PARAM_PASSWORD, - TPSIP_CONN_PARAM_ALIAS, - TPSIP_CONN_PARAM_REGISTRAR, - TPSIP_CONN_PARAM_PROXY_HOST, - TPSIP_CONN_PARAM_PORT, - TPSIP_CONN_PARAM_TRANSPORT, - TPSIP_CONN_PARAM_LOOSE_ROUTING, - TPSIP_CONN_PARAM_DISCOVER_BINDING, - TPSIP_CONN_PARAM_KEEPALIVE_MECHANISM, - TPSIP_CONN_PARAM_KEEPALIVE_INTERVAL, - TPSIP_CONN_PARAM_DISCOVER_STUN, - TPSIP_CONN_PARAM_STUN_SERVER, - TPSIP_CONN_PARAM_STUN_PORT, - TPSIP_CONN_PARAM_IMMUTABLE_STREAMS, - TPSIP_CONN_PARAM_LOCAL_IP_ADDRESS, - TPSIP_CONN_PARAM_LOCAL_PORT, - TPSIP_CONN_PARAM_EXTRA_AUTH_USER, - TPSIP_CONN_PARAM_EXTRA_AUTH_PASSWORD, - N_TPSIP_CONN_PARAMS -}; - -static const TpCMParamSpec tpsip_params[] = { - /* Account (a sip: URI) */ - { "account", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, - TP_CONN_MGR_PARAM_FLAG_REQUIRED | TP_CONN_MGR_PARAM_FLAG_REGISTER, - NULL, G_STRUCT_OFFSET (TpsipConnParams, account) }, - /* Username to register with, if different than in the account URI */ - { "auth-user", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, - 0, NULL, G_STRUCT_OFFSET (TpsipConnParams, auth_user) }, - /* Password */ - { "password", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, - TP_CONN_MGR_PARAM_FLAG_SECRET, - NULL, G_STRUCT_OFFSET (TpsipConnParams, password) }, - /* Display name for self */ - { "alias", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, 0, NULL, - G_STRUCT_OFFSET(TpsipConnParams, alias), - /* setting a 0-length alias makes no sense */ - tp_cm_param_filter_string_nonempty, NULL }, - /* Registrar */ - { "registrar", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, - 0, NULL, G_STRUCT_OFFSET (TpsipConnParams, registrar) }, - /* Used to compose proxy URI */ - { "proxy-host", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, - 0, NULL, G_STRUCT_OFFSET (TpsipConnParams, proxy_host) }, - /* Used to compose proxy URI */ - { "port", DBUS_TYPE_UINT16_AS_STRING, G_TYPE_UINT, - TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER(SIP_DEFAULT_PORT), - G_STRUCT_OFFSET (TpsipConnParams, port) }, - /* Used to compose proxy URI */ - { "transport", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, - TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, "auto", - G_STRUCT_OFFSET (TpsipConnParams, transport) }, - /* Enables loose routing as per RFC 3261 */ - { "loose-routing", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, - TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER(FALSE), - G_STRUCT_OFFSET (TpsipConnParams, loose_routing) }, - /* Used to enable proactive NAT traversal techniques */ - { "discover-binding", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, - TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER(TRUE), - G_STRUCT_OFFSET (TpsipConnParams, discover_binding) }, - /* Mechanism used for connection keepalive maintenance */ - { "keepalive-mechanism", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, - TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, "auto", - G_STRUCT_OFFSET (TpsipConnParams, keepalive_mechanism) }, - /* Keep-alive interval */ - { "keepalive-interval", DBUS_TYPE_UINT32_AS_STRING, G_TYPE_UINT, - TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER(0), - G_STRUCT_OFFSET (TpsipConnParams, keepalive_interval) }, - /* Use SRV DNS lookup to discover STUN server */ - { "discover-stun", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, - TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER(TRUE), - G_STRUCT_OFFSET (TpsipConnParams, discover_stun) }, - /* STUN server */ - { "stun-server", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, - 0, NULL, G_STRUCT_OFFSET (TpsipConnParams, stun_server) }, - /* STUN port */ - { "stun-port", DBUS_TYPE_UINT16_AS_STRING, G_TYPE_UINT, - TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, - GUINT_TO_POINTER(TPSIP_DEFAULT_STUN_PORT), - G_STRUCT_OFFSET (TpsipConnParams, stun_port) }, - /* If the session content cannot be modified once initially set up */ - { "immutable-streams", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, - TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER(FALSE), - G_STRUCT_OFFSET (TpsipConnParams, immutable_streams) }, - /* Local IP address to use, workaround purposes only */ - { "local-ip-address", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, - 0, NULL, G_STRUCT_OFFSET (TpsipConnParams, local_ip_address) }, - /* Local port for SIP, workaround purposes only */ - { "local-port", DBUS_TYPE_UINT16_AS_STRING, G_TYPE_UINT, - 0, NULL, G_STRUCT_OFFSET (TpsipConnParams, local_port) }, - /* Extra-realm authentication */ - { "extra-auth-user", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, - 0, NULL, G_STRUCT_OFFSET (TpsipConnParams, extra_auth_user) }, - { "extra-auth-password", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, - TP_CONN_MGR_PARAM_FLAG_SECRET, - NULL, G_STRUCT_OFFSET (TpsipConnParams, extra_auth_password) }, - { NULL, NULL, 0, 0, NULL, 0 } -}; - -const TpCMProtocolSpec tpsip_protocols[] = { - { "sip", tpsip_params, alloc_params, free_params }, - { NULL, NULL } -}; - struct _TpsipConnectionManagerPrivate { su_root_t *sofia_root; @@ -237,10 +79,24 @@ tpsip_connection_manager_init (TpsipConnectionManager *obj) #endif } +static void +tpsip_connection_manager_constructed (GObject *object) +{ + TpsipConnectionManager *self = TPSIP_CONNECTION_MANAGER (object); + TpBaseConnectionManager *base = (TpBaseConnectionManager *) self; + void (*constructed) (GObject *) = + ((GObjectClass *) tpsip_connection_manager_parent_class)->constructed; + TpBaseProtocol *protocol; + + if (constructed != NULL) + constructed (object); + + protocol = tpsip_protocol_new (self->priv->sofia_root); + tp_base_connection_manager_add_protocol (base, protocol); + g_object_unref (protocol); +} + static void tpsip_connection_manager_finalize (GObject *object); -static TpBaseConnection *tpsip_connection_manager_new_connection ( - TpBaseConnectionManager *base, const gchar *proto, - TpIntSet *params_present, void *parsed_params, GError **error); static void tpsip_connection_manager_class_init (TpsipConnectionManagerClass *klass) @@ -251,11 +107,10 @@ tpsip_connection_manager_class_init (TpsipConnectionManagerClass *klass) g_type_class_add_private (klass, sizeof (TpsipConnectionManagerPrivate)); + object_class->constructed = tpsip_connection_manager_constructed; object_class->finalize = tpsip_connection_manager_finalize; - base_class->new_connection = tpsip_connection_manager_new_connection; base_class->cm_dbus_name = "sofiasip"; - base_class->protocol_params = tpsip_protocols; } void @@ -279,248 +134,3 @@ tpsip_connection_manager_finalize (GObject *object) G_OBJECT_CLASS (tpsip_connection_manager_parent_class)->finalize (object); } - -static gchar * -priv_compose_proxy_uri (const gchar *host, - const gchar *transport, - guint port) -{ - const gchar *scheme = "sip"; - - if (host == NULL) - return NULL; - - /* Set scheme to SIPS if transport is TLS */ - - if (transport != NULL && !g_ascii_strcasecmp (transport, "tls")) - scheme = "sips"; - - /* Format the resulting URI */ - - if (port) - return g_strdup_printf ("%s:%s:%u", scheme, host, port); - else - return g_strdup_printf ("%s:%s", scheme, host); -} - -/** - * Returns a default SIP proxy address based on the public - * SIP address 'sip_address' and . For instance - * "sip:first.surname@company.com" would result in "sip:company.com". - * The SIP stack will further utilize DNS lookups to find the IP address - * for the SIP server responsible for the domain "company.com". - */ -static gchar * -priv_compose_default_proxy_uri (const gchar *sip_address, - const gchar *transport) -{ - char *result = NULL; - char *host; - char *found; - - g_return_val_if_fail (sip_address != NULL, NULL); - - /* skip sip and sips prefixes, updating transport if necessary */ - found = strchr (sip_address, ':'); - if (found != NULL) { - if (g_ascii_strncasecmp ("sip:", sip_address, 4) == 0) - ; - else if (g_ascii_strncasecmp ("sips:", sip_address, 5) == 0) - { - if (transport == NULL || - strcmp (transport, "auto") == 0) - transport = "tls"; - } - else - { - /* error, unknown URI prefix */ - return NULL; - } - - sip_address = found + 1; - } - - /* skip userinfo */ - found = strchr (sip_address, '@'); - if (found != NULL) - sip_address = found + 1; - - /* copy rest of the string */ - host = g_strdup (sip_address); - - /* mark end (before uri-parameters defs or headers) */ - found = strchr (host, ';'); - if (found == NULL) - found = strchr (host, '?'); - if (found != NULL) - *found = '\0'; - - result = priv_compose_proxy_uri (host, transport, 0); - - g_free (host); - - return result; -} - -static TpsipConnectionKeepaliveMechanism -priv_parse_keepalive (const gchar *str) -{ - if (str == NULL || strcmp (str, "auto") == 0) - return TPSIP_CONNECTION_KEEPALIVE_AUTO; - if (strcmp (str, "register") == 0) - return TPSIP_CONNECTION_KEEPALIVE_REGISTER; - if (strcmp (str, "options") == 0) - return TPSIP_CONNECTION_KEEPALIVE_OPTIONS; - if (strcmp (str, "stun") == 0) - return TPSIP_CONNECTION_KEEPALIVE_STUN; - if (strcmp (str, "off") == 0) - return TPSIP_CONNECTION_KEEPALIVE_NONE; - - WARNING ("unsupported keepalive-method value \"%s\", falling back to auto", str); - return TPSIP_CONNECTION_KEEPALIVE_AUTO; -} - -#define SET_PROPERTY_IF_PARAM_SET(prop, param, member) \ - if (tp_intset_is_member (params_present, param)) \ - { \ - g_object_set (connection, prop, member, NULL); \ - } - -#define NULLIFY_IF_EMPTY(param) \ - if ((param) != NULL && (param)[0] == '\0') \ - param = NULL; - -static gboolean -check_not_empty_if_present (const gchar *name, - const gchar *value, - GError **error) -{ - if (value != NULL && value[0] == '\0') - { - g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "If supplied, '%s' account parameter may not be empty", name); - return FALSE; - } - return TRUE; -} - -static TpBaseConnection * -tpsip_connection_manager_new_connection (TpBaseConnectionManager *base, - const gchar *proto, - TpIntSet *params_present, - void *parsed_params, - GError **error) -{ - TpsipConnectionManager *self = TPSIP_CONNECTION_MANAGER (base); - TpsipConnectionManagerPrivate *priv = TPSIP_CONNECTION_MANAGER_GET_PRIVATE (self); - TpBaseConnection *connection = NULL; - TpsipConnParams *params = (TpsipConnParams *)parsed_params; - gchar *proxy = NULL; - TpsipConnectionKeepaliveMechanism keepalive_mechanism; - - if (strcmp (proto, "sip")) { - g_set_error (error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED, - "This connection manager only implements protocol 'sip', not '%s'", - proto); - return NULL; - } - - /* TpBaseConnectionManager code has already checked that required params - * are present (but not that they are non-empty, if we're using >= 0.5.8) - */ - g_assert (params->account); - - /* FIXME: validate account SIP URI properly, using appropriate RFCs */ - if (!check_not_empty_if_present ("account", params->account, error)) - return FALSE; - NULLIFY_IF_EMPTY (params->auth_user); - NULLIFY_IF_EMPTY (params->password); - /* FIXME: validate registrar SIP URI properly, using appropriate RFCs */ - NULLIFY_IF_EMPTY (params->registrar); - /* FIXME: validate proxy host properly */ - NULLIFY_IF_EMPTY (params->proxy_host); - /* FIXME: check against the list (which presumably exists) of valid - * transports */ - NULLIFY_IF_EMPTY (params->transport); - /* FIXME: check against the list (which presumably exists) of valid - * KA mechanisms */ - NULLIFY_IF_EMPTY (params->keepalive_mechanism); - /* FIXME: validate STUN server properly */ - NULLIFY_IF_EMPTY (params->stun_server); - /* FIXME: validate local IP address properly */ - NULLIFY_IF_EMPTY (params->local_ip_address); - NULLIFY_IF_EMPTY (params->extra_auth_user); - NULLIFY_IF_EMPTY (params->extra_auth_password); - - DEBUG("New SIP connection to %s", params->account); - - connection = (TpBaseConnection *)g_object_new(TPSIP_TYPE_CONNECTION, - "protocol", "sip", - "sofia-root", priv->sofia_root, - "address", params->account, - NULL); - - if (params->proxy_host == NULL) { - proxy = priv_compose_default_proxy_uri (params->account, - params->transport); - DEBUG("set outbound proxy address to <%s>, based on <%s>", proxy, params->account); - } else - proxy = priv_compose_proxy_uri (params->proxy_host, - params->transport, - params->port); - - g_object_set (connection, "proxy", proxy, NULL); - g_free (proxy); - - if (params->transport != NULL && strcmp (params->transport, "auto") != 0) - g_object_set (connection, "transport", params->transport, NULL); - - SET_PROPERTY_IF_PARAM_SET ("auth-user", TPSIP_CONN_PARAM_AUTH_USER, - params->auth_user); - - SET_PROPERTY_IF_PARAM_SET ("password", TPSIP_CONN_PARAM_PASSWORD, - params->password); - - SET_PROPERTY_IF_PARAM_SET ("alias", TPSIP_CONN_PARAM_ALIAS, - params->alias); - - SET_PROPERTY_IF_PARAM_SET ("registrar", TPSIP_CONN_PARAM_REGISTRAR, - params->registrar); - - SET_PROPERTY_IF_PARAM_SET ("loose-routing", TPSIP_CONN_PARAM_LOOSE_ROUTING, - params->loose_routing); - - SET_PROPERTY_IF_PARAM_SET ("discover-binding", TPSIP_CONN_PARAM_DISCOVER_BINDING, - params->discover_binding); - - SET_PROPERTY_IF_PARAM_SET ("discover-stun", TPSIP_CONN_PARAM_DISCOVER_STUN, - params->discover_stun); - - SET_PROPERTY_IF_PARAM_SET ("stun-server", TPSIP_CONN_PARAM_STUN_SERVER, - params->stun_server); - - SET_PROPERTY_IF_PARAM_SET ("stun-port", TPSIP_CONN_PARAM_STUN_PORT, - params->stun_port); - - SET_PROPERTY_IF_PARAM_SET ("immutable-streams", TPSIP_CONN_PARAM_IMMUTABLE_STREAMS, - params->immutable_streams); - - SET_PROPERTY_IF_PARAM_SET ("keepalive-interval", - TPSIP_CONN_PARAM_KEEPALIVE_INTERVAL, params->keepalive_interval); - - keepalive_mechanism = priv_parse_keepalive (params->keepalive_mechanism); - g_object_set (connection, "keepalive-mechanism", keepalive_mechanism, NULL); - - SET_PROPERTY_IF_PARAM_SET ("local-ip-address", TPSIP_CONN_PARAM_LOCAL_IP_ADDRESS, - params->local_ip_address); - - SET_PROPERTY_IF_PARAM_SET ("local-port", TPSIP_CONN_PARAM_LOCAL_PORT, - params->local_port); - - SET_PROPERTY_IF_PARAM_SET ("extra-auth-user", TPSIP_CONN_PARAM_EXTRA_AUTH_USER, - params->extra_auth_user); - SET_PROPERTY_IF_PARAM_SET ("extra-auth-password", TPSIP_CONN_PARAM_EXTRA_AUTH_PASSWORD, - params->extra_auth_password); - - return connection; -} diff --git a/src/write-mgr-file.c b/src/write-mgr-file.c index e718d91..bf51a19 100644 --- a/src/write-mgr-file.c +++ b/src/write-mgr-file.c @@ -1,113 +1,357 @@ /* - * write_mgr_file.c - utility to produce .manager files. - * Copyright (C) 2006-2007 Collabora Ltd. - * Copyright (C) 2006-2007 Nokia Corporation + * 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 work is free software; you can redistribute it and/or + * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * This work is distributed in the hope that it will be useful, + * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this work; if not, write to the Free Software + * 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 "sip-connection-manager.h" +#include "protocol.h" -/* Cloned straight from Gabble - this might eventually go in a library */ -static gchar * -mgr_file_contents (const char *busname, - const char *objpath, - const TpCMProtocolSpec protocols[], - GError **error) + +#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) { - GKeyFile *f = g_key_file_new(); - const TpCMProtocolSpec *protocol; + const TpCMParamSpec *parameters = + tp_base_protocol_get_parameters (protocol); const TpCMParamSpec *row; - g_key_file_set_string(f, "ConnectionManager", "BusName", busname); - g_key_file_set_string(f, "ConnectionManager", "ObjectPath", objpath); + 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 (protocol = protocols; protocol->name; protocol++) + for (row = parameters; row->name; row++) { - gchar *section_name = g_strdup_printf("Protocol %s", protocol->name); + if (row->flags & TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT) + { + gchar *default_name = g_strdup_printf ("default-%s", row->name); - for (row = protocol->parameters; row->name; row++) + 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 *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); + 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; } - for (row = protocol->parameters; row->name; row++) + case G_TYPE_STRING: { - if (row->flags & TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT) - { - gchar *default_name = g_strdup_printf("default-%s", row->name); + 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; + } - 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); - } - g_free(default_name); + 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 (); } - g_free(section_name); } - return g_key_file_to_data(f, NULL, error); } -/* defined by telepathy-glib if your version is new enough - included here - * so telepathy-glib 0.5.4 and 0.5.5 will still work - */ -#ifndef TP_CM_BUS_NAME_BASE -# define TP_CM_BUS_NAME_BASE "org.freedesktop.Telepathy.ConnectionManager." -# define TP_CM_OBJECT_PATH_BASE "/org/freedesktop/Telepathy/ConnectionManager/" -#endif +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) + { + TpBaseProtocol *protocol = protocols->data; + GHashTable *props = + tp_base_protocol_get_immutable_properties (protocol); + gchar *section_name = g_strdup_printf ("Protocol %s", + tp_base_protocol_get_name (protocol)); + const gchar * const *ifaces = tp_asv_get_strv (props, + TP_PROP_PROTOCOL_INTERFACES); + const gchar * const *c_ifaces = tp_asv_get_strv (props, + TP_PROP_PROTOCOL_CONNECTION_INTERFACES); + + write_parameters (f, section_name, 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)); + + 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_destroy (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, + tpsip_protocol_new (NULL)); + + s = mgr_file_contents (TP_CM_BUS_NAME_BASE "sofiasip", + TP_CM_OBJECT_PATH_BASE "sofiasip", + protocols, &error); + + g_object_unref (protocols->data); + g_slist_free (protocols); - gchar *s = mgr_file_contents(TP_CM_BUS_NAME_BASE "sofiasip", - TP_CM_OBJECT_PATH_BASE "sofiasip", - tpsip_protocols, &error); if (!s) { - fprintf(stderr, "%s\n", error->message); - g_error_free(error); + fprintf (stderr, "%s", error->message); + g_error_free (error); return 1; } - printf("%s", s); - g_free(s); + printf ("%s", s); + g_free (s); return 0; } -- cgit v1.2.3 From 167d76996438e4e8e3eacf36577ec196354d592e Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 18 Nov 2010 18:16:37 +0000 Subject: Add a simple test for Protocol objects --- tests/twisted/Makefile.am | 1 + tests/twisted/cm/protocol.py | 58 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 tests/twisted/cm/protocol.py diff --git a/tests/twisted/Makefile.am b/tests/twisted/Makefile.am index 3b5e483..bc49772 100644 --- a/tests/twisted/Makefile.am +++ b/tests/twisted/Makefile.am @@ -1,4 +1,5 @@ TWISTED_TESTS = \ + cm/protocol.py \ test-register.py \ test-register-fail.py \ test-handle-normalisation.py \ diff --git a/tests/twisted/cm/protocol.py b/tests/twisted/cm/protocol.py new file mode 100644 index 0000000..f0b0c40 --- /dev/null +++ b/tests/twisted/cm/protocol.py @@ -0,0 +1,58 @@ +""" +Test tpsip's o.fd.T.Protocol implementation +""" + +import dbus +from servicetest import (unwrap, tp_path_prefix, assertEquals, assertContains, + call_async) +from sofiatest import exec_test +import constants as cs + +def test(q, bus, conn, sip): + cm = bus.get_object(cs.CM + '.sofiasip', + tp_path_prefix + '/ConnectionManager/sofiasip') + 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(['sip']), set(protocols.keys())) + + protocol_names = unwrap(cm_iface.ListProtocols()) + assertEquals(set(['sip']), set(protocol_names)) + + cm_params = cm_iface.GetParameters('sip') + local_props = protocols['sip'] + local_params = local_props[cs.PROTOCOL + '.Parameters'] + assertEquals(cm_params, local_params) + + proto = bus.get_object(cm.bus_name, cm.object_path + '/sip') + 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 = local_props[cs.PROTOCOL + '.' + key] + b = proto_props[key] + assertEquals(a, b) + + assertEquals('x-sip', proto_props['VCardField']) + assertEquals('SIP', proto_props['EnglishName']) + assertEquals('im-sip', proto_props['Icon']) + + assertContains(cs.CONN_IFACE_ALIASING, proto_props['ConnectionInterfaces']) + assertContains(cs.CONN_IFACE_CONTACTS, proto_props['ConnectionInterfaces']) + assertContains(cs.CONN_IFACE_REQUESTS, proto_props['ConnectionInterfaces']) + + assertEquals('sip:example@mit.edu', + unwrap(proto_iface.NormalizeContact('example@MIT.Edu'))) + + # Only account is mandatory + call_async(q, proto_iface, 'IdentifyAccount', {}) + q.expect('dbus-error', method='IdentifyAccount', name=cs.INVALID_ARGUMENT) + test_params = {'account': 'smcv@example.com'} + acc_name = unwrap(proto_iface.IdentifyAccount(test_params)) + assertEquals('smcv@example.com', acc_name) + +if __name__ == '__main__': + exec_test(test) -- cgit v1.2.3 From 747c688cdd03a69f7d712e1eeeaaf756a0fb5c43 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 18 Nov 2010 18:19:02 +0000 Subject: Implement type_foreach_channel_class in both channel managers This lets our Protocol objects advertise their requestable channel classes. --- src/media-factory.c | 11 ++++++----- src/text-factory.c | 9 +++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/media-factory.c b/src/media-factory.c index 689d773..f4573c5 100644 --- a/src/media-factory.c +++ b/src/media-factory.c @@ -451,15 +451,15 @@ static const gchar * const named_channel_allowed_properties[] = { NULL }; -/* not advertised in foreach_channel_class - can only be requested with +/* 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 }; static void -tpsip_media_factory_foreach_channel_class (TpChannelManager *manager, - TpChannelManagerChannelClassFunc func, +tpsip_media_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, @@ -476,7 +476,7 @@ tpsip_media_factory_foreach_channel_class (TpChannelManager *manager, handle_type_value); g_value_set_uint (handle_type_value, TP_HANDLE_TYPE_CONTACT); - func (manager, table, named_channel_allowed_properties, user_data); + func (type, table, named_channel_allowed_properties, user_data); g_hash_table_destroy (table); } @@ -688,7 +688,8 @@ channel_manager_iface_init (gpointer g_iface, TpChannelManagerIface *iface = g_iface; iface->foreach_channel = tpsip_media_factory_foreach_channel; - iface->foreach_channel_class = tpsip_media_factory_foreach_channel_class; + iface->type_foreach_channel_class = + tpsip_media_factory_type_foreach_channel_class; iface->request_channel = tpsip_media_factory_request_channel; iface->create_channel = tpsip_media_factory_create_channel; iface->ensure_channel = tpsip_media_factory_ensure_channel; diff --git a/src/text-factory.c b/src/text-factory.c index 881891e..a9cb38c 100644 --- a/src/text-factory.c +++ b/src/text-factory.c @@ -328,8 +328,8 @@ static const gchar * const text_channel_allowed_properties[] = { }; static void -tpsip_text_factory_foreach_channel_class (TpChannelManager *manager, - TpChannelManagerChannelClassFunc func, +tpsip_text_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, @@ -346,7 +346,7 @@ tpsip_text_factory_foreach_channel_class (TpChannelManager *manager, g_hash_table_insert (table, (gchar *) text_channel_fixed_properties[1], value); - func (manager, table, text_channel_allowed_properties, user_data); + func (type, table, text_channel_allowed_properties, user_data); g_hash_table_destroy (table); } @@ -635,7 +635,8 @@ channel_manager_iface_init (gpointer g_iface, gpointer iface_data) TpChannelManagerIface *iface = g_iface; iface->foreach_channel = tpsip_text_factory_foreach_channel; - iface->foreach_channel_class = tpsip_text_factory_foreach_channel_class; + iface->type_foreach_channel_class = + tpsip_text_factory_type_foreach_channel_class; iface->create_channel = tpsip_text_factory_create_channel; iface->request_channel = tpsip_text_factory_request_channel; iface->ensure_channel = tpsip_text_factory_ensure_channel; -- cgit v1.2.3 From 1c58961211f73bf3dd39f3bac4127a0cd7a11bb4 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Tue, 23 Nov 2010 16:12:42 +0000 Subject: Re-word write-mgr-file's header - this isn't Gabble --- src/write-mgr-file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/write-mgr-file.c b/src/write-mgr-file.c index bf51a19..402e89e 100644 --- a/src/write-mgr-file.c +++ b/src/write-mgr-file.c @@ -1,5 +1,5 @@ /* - * write_mgr_file.c - utility to produce gabble.manager. Part of Gabble. + * write_mgr_file.c - utility to produce manager files. Derived from Gabble. * Copyright (C) 2006-2010 Collabora Ltd. * Copyright (C) 2006-2010 Nokia Corporation * -- cgit v1.2.3 From 8221afb2c98fd3ceadf2605b99e2f371ca4829c4 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Tue, 23 Nov 2010 18:53:19 +0200 Subject: Added the Telepathy maintenance scripts and makefile rules Copied from telepathy-glib. --- Makefile.am | 2 + tools/Makefile.am | 3 +- tools/make-release-mail.py | 76 ++++++++++++++++ tools/make-version-script.py | 208 +++++++++++++++++++++++++++++++++++++++++++ tools/telepathy.am | 65 ++++++++++++++ 5 files changed, 353 insertions(+), 1 deletion(-) create mode 100644 tools/make-release-mail.py create mode 100644 tools/make-version-script.py create mode 100644 tools/telepathy.am diff --git a/Makefile.am b/Makefile.am index 242d97d..63c75d0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -35,3 +35,5 @@ include tools/lcov.am LCOV_CHECK_ARGS = CHECK_TWISTED_SLEEP=6 clean-local: lcov-clean + +include tools/telepathy.am diff --git a/tools/Makefile.am b/tools/Makefile.am index b5b2331..f9d9e29 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -17,7 +17,8 @@ EXTRA_DIST = \ glib-signals-marshal-gen.py \ identity.xsl \ lcov.am \ - libglibcodegen.py + libglibcodegen.py \ + telepathy.am CLEANFILES = libglibcodegen.pyc libglibcodegen.pyo $(noinst_SCRIPTS) diff --git a/tools/make-release-mail.py b/tools/make-release-mail.py new file mode 100644 index 0000000..2bd7c2b --- /dev/null +++ b/tools/make-release-mail.py @@ -0,0 +1,76 @@ +#!/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 + +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 line.startswith("%s %s" % (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 line.startswith(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' + +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 + +%(details)s""".strip().rstrip() % { + 'base_url': BASE_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) diff --git a/tools/make-version-script.py b/tools/make-version-script.py new file mode 100644 index 0000000..0d30aa3 --- /dev/null +++ b/tools/make-version-script.py @@ -0,0 +1,208 @@ +#!/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) diff --git a/tools/telepathy.am b/tools/telepathy.am new file mode 100644 index 0000000..45baa77 --- /dev/null +++ b/tools/telepathy.am @@ -0,0 +1,65 @@ +## Useful top-level Makefile.am snippets for Telepathy projects. + +dist-hook: + chmod u+w ${distdir}/ChangeLog + if test -d ${top_srcdir}/.git; then \ + 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 + @if ! git diff --no-ext-diff --quiet --exit-code; then \ + echo "Hey! Your tree is dirty! No release for you." >&2; \ + exit 2; \ + fi + +%.tar.gz.asc: %.tar.gz + $(AM_V_GEN)gpg --detach-sign --armor $@ + +@PACKAGE@-@VERSION@.tar.gz: _is-release-check check distcheck + +maintainer-prepare-release: _is-release-check check distcheck 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: maintainer-prepare-release 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: -- cgit v1.2.3 From 99a098f0c5fe88117df394710b704bb111c73a79 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Tue, 23 Nov 2010 19:05:28 +0200 Subject: Removed the dist-hook for changelog generation Now handled by 'tools/telepathy.am'. --- Makefile.am | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Makefile.am b/Makefile.am index 63c75d0..aab3064 100644 --- a/Makefile.am +++ b/Makefile.am @@ -24,13 +24,6 @@ SUBDIRS = \ src \ tests -dist-hook: - chmod u+w ${distdir}/ChangeLog - if test -d ${top_srcdir}/.git; then \ - git --work-tree=${top_srcdir} log --stat > ${distdir}/ChangeLog || \ - git --work-tree=${top_srcdir} log > ${distdir}/ChangeLog; \ - fi - include tools/lcov.am LCOV_CHECK_ARGS = CHECK_TWISTED_SLEEP=6 -- cgit v1.2.3 From 3908c858e2b19e122cb85202ede3edb39bc75a5e Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Tue, 23 Nov 2010 19:06:23 +0200 Subject: Added constants.py from the test support files to the distribution --- tests/twisted/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/twisted/Makefile.am b/tests/twisted/Makefile.am index bc49772..5988592 100644 --- a/tests/twisted/Makefile.am +++ b/tests/twisted/Makefile.am @@ -28,6 +28,7 @@ check-twisted: EXTRA_DIST = \ $(TWISTED_TESTS) \ + constants.py \ sofiatest.py \ servicetest.py -- cgit v1.2.3 From 15349832aea5b9c4a673e65f4099c116b24522e6 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Tue, 23 Nov 2010 18:54:20 +0200 Subject: Release 0.7.0 NOTE: changed the release header format in NEWS to fit expectation of tools/make-release-mail.py --- NEWS | 12 ++++++++++++ configure.ac | 6 +++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index cb24089..8f9f75a 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,18 @@ recent at the top). See also ChangeLog. +telepathy-sofiasip 0.7.0 (2010-11-23) +------------------------------------- + +The "Fermentation Vat No. 78" release. + +- Opening the new development series. +- Implemented o.fd.Tp.Ch.I.Messages (fd.o #29377). +- Implemented o.fd.Tp.Protocol (fd.o #30538). +- Uses TpDBusDaemon to export media streams (fd.o #31720). +- Depends on telepathy-glib 0.12.0 now. +- Improvements in session state and signalling. + Changes in release 0.6.3 (2010-06-21) ------------------------------------- diff --git a/configure.ac b/configure.ac index a08d34b..93eb480 100644 --- a/configure.ac +++ b/configure.ac @@ -2,9 +2,9 @@ AC_PREREQ([2.59]) m4_define([THIS_PACKAGE],[telepathy-sofiasip]) m4_define([VERSION_MAJOR],[0]) -m4_define([VERSION_MINOR],[6]) -m4_define([VERSION_MICRO],[99]) -m4_define([VERSION_NANO],[1]) +m4_define([VERSION_MINOR],[7]) +m4_define([VERSION_MICRO],[0]) +m4_define([VERSION_NANO],[0]) m4_define([BASE_VERSION],[VERSION_MAJOR.VERSION_MINOR.VERSION_MICRO]) m4_define([THIS_VERSION], -- cgit v1.2.3 From 825053fad9ac2b08ef47bb21a1dc29b0ca7be77f Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Tue, 23 Nov 2010 19:31:28 +0200 Subject: Version 0.7.0.1 for development --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 93eb480..f67e7ac 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,7 @@ m4_define([THIS_PACKAGE],[telepathy-sofiasip]) m4_define([VERSION_MAJOR],[0]) m4_define([VERSION_MINOR],[7]) m4_define([VERSION_MICRO],[0]) -m4_define([VERSION_NANO],[0]) +m4_define([VERSION_NANO],[1]) m4_define([BASE_VERSION],[VERSION_MAJOR.VERSION_MINOR.VERSION_MICRO]) m4_define([THIS_VERSION], -- cgit v1.2.3 From d8e180b6f802cccea36fee81714d82203cba1f5b Mon Sep 17 00:00:00 2001 From: Jonny Lamb Date: Wed, 24 Nov 2010 16:06:04 +0000 Subject: protocol: when hitting a special parameter, just skip it, not all the others too Signed-off-by: Jonny Lamb Reviewed-by: Simon McVittie --- src/protocol.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protocol.c b/src/protocol.c index 1c8bb57..45d960b 100644 --- a/src/protocol.c +++ b/src/protocol.c @@ -314,7 +314,7 @@ new_connection (TpBaseProtocol *protocol, if (tpsip_params[i].offset == PARAM_SET_SEPARATELY) { DEBUG ("Parameter %s is handled specially", tpsip_params[i].name); - break; + continue; } g_assert (tpsip_params[i].offset == PARAM_EASY); -- cgit v1.2.3 From 7fd45d95dc7909585976670a77243a175d977734 Mon Sep 17 00:00:00 2001 From: Jonny Lamb Date: Wed, 24 Nov 2010 16:26:08 +0000 Subject: connection: use TpSimplePasswordManager to get a password if one was not given Signed-off-by: Jonny Lamb --- src/sip-connection-private.h | 3 +++ src/sip-connection.c | 46 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/sip-connection-private.h b/src/sip-connection-private.h index 6a7efcf..2250694 100644 --- a/src/sip-connection-private.h +++ b/src/sip-connection-private.h @@ -27,6 +27,8 @@ #include #include +#include + #ifdef HAVE_LIBIPHB #include #endif @@ -51,6 +53,7 @@ struct _TpsipConnectionPrivate gchar *registrar_realm; TpsipMediaFactory *media_factory; + TpSimplePasswordManager *password_manager; gchar *address; gchar *auth_user; diff --git a/src/sip-connection.c b/src/sip-connection.c index d383dfb..da73e63 100644 --- a/src/sip-connection.c +++ b/src/sip-connection.c @@ -160,6 +160,10 @@ tpsip_connection_create_channel_managers (TpBaseConnection *conn) "connection", self, NULL); g_ptr_array_add (channel_managers, priv->media_factory); + priv->password_manager = tp_simple_password_manager_new ( + conn); + g_ptr_array_add (channel_managers, priv->password_manager); + return channel_managers; } @@ -881,6 +885,38 @@ tpsip_connection_finalize (GObject *obj) G_OBJECT_CLASS (tpsip_connection_parent_class)->finalize (obj); } +static void +_password_manager_prompt_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + TpsipConnection *self = user_data; + TpsipConnectionPrivate *priv = TPSIP_CONNECTION_GET_PRIVATE (self); + TpBaseConnection *base_conn = (TpBaseConnection *) self; + GError *error = NULL; + const GString *password; + + password = tp_simple_password_manager_prompt_finish ( + TP_SIMPLE_PASSWORD_MANAGER (source_object), result, &error); + + if (error != NULL) + { + DEBUG ("Auth channel failed: %s", error->message); + + tp_base_connection_change_status (base_conn, + TP_CONNECTION_STATUS_DISCONNECTED, + TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED); + + g_error_free (error); + return; + } + + g_free (priv->password); + priv->password = g_strdup (password->str); + + nua_register (priv->register_op, TAG_NULL()); +} + static gboolean tpsip_connection_start_connecting (TpBaseConnection *base, GError **error) @@ -977,7 +1013,15 @@ tpsip_connection_start_connecting (TpBaseConnection *base, tpsip_event_target_attach (priv->register_op, (GObject *) self); - nua_register (priv->register_op, TAG_NULL()); + if (priv->password != NULL) + { + nua_register (priv->register_op, TAG_NULL()); + } + else + { + tp_simple_password_manager_prompt_async (priv->password_manager, + _password_manager_prompt_cb, self); + } return TRUE; } -- cgit v1.2.3 From 9b24bd79a3f9437ea406ef4c412ff5aae84da2ce Mon Sep 17 00:00:00 2001 From: Jonny Lamb Date: Thu, 25 Nov 2010 11:52:31 +0000 Subject: connection: use disconnect_with_dbus_error Signed-off-by: Jonny Lamb --- src/sip-connection.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sip-connection.c b/src/sip-connection.c index da73e63..133b453 100644 --- a/src/sip-connection.c +++ b/src/sip-connection.c @@ -903,8 +903,8 @@ _password_manager_prompt_cb (GObject *source_object, { DEBUG ("Auth channel failed: %s", error->message); - tp_base_connection_change_status (base_conn, - TP_CONNECTION_STATUS_DISCONNECTED, + tp_base_connection_disconnect_with_dbus_error (base_conn, + tp_error_get_dbus_name (error->code), NULL, TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED); g_error_free (error); -- cgit v1.2.3 From 04540e3ae94b618270bd3bb43b9ca6d75bf06ab8 Mon Sep 17 00:00:00 2001 From: Jonny Lamb Date: Tue, 7 Dec 2010 16:43:34 +0000 Subject: connection: only disconnect if we're not already disconnected If we don't check this, tp-glib will give us a nice big fat warning. Signed-off-by: Jonny Lamb --- src/sip-connection.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/sip-connection.c b/src/sip-connection.c index 133b453..51247fa 100644 --- a/src/sip-connection.c +++ b/src/sip-connection.c @@ -903,9 +903,12 @@ _password_manager_prompt_cb (GObject *source_object, { DEBUG ("Auth channel failed: %s", error->message); - tp_base_connection_disconnect_with_dbus_error (base_conn, - tp_error_get_dbus_name (error->code), NULL, - TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED); + if (base_conn->status != TP_CONNECTION_STATUS_DISCONNECTED) + { + tp_base_connection_disconnect_with_dbus_error (base_conn, + tp_error_get_dbus_name (error->code), NULL, + TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED); + } g_error_free (error); return; -- cgit v1.2.3 From ef71b9dd869a46476fff2a49b942eb04d9928832 Mon Sep 17 00:00:00 2001 From: Jonny Lamb Date: Tue, 7 Dec 2010 16:52:04 +0000 Subject: configure: depend on new enough tp-glib for TpSimplePasswordManager Signed-off-by: Jonny Lamb --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index f67e7ac..5d2539d 100644 --- a/configure.ac +++ b/configure.ac @@ -85,7 +85,7 @@ AC_SUBST(SOFIA_SIP_UA_CFLAGS) AC_SUBST(SOFIA_SIP_UA_VERSION) dnl Check for telepathy-glib -PKG_CHECK_MODULES(TELEPATHY_GLIB, [telepathy-glib >= 0.12]) +PKG_CHECK_MODULES(TELEPATHY_GLIB, [telepathy-glib >= 0.13.8]) AC_SUBST(TELEPATHY_GLIB_CFLAGS) AC_SUBST(TELEPATHY_GLIB_LIBS) -- cgit v1.2.3 From 75a2d3a792b54c328b809e1f260ad1e49632fb6e Mon Sep 17 00:00:00 2001 From: David Laban Date: Mon, 13 Dec 2010 17:13:01 +0000 Subject: Ask for a password in priv_handle_auth() if not given. This requires a call to tp_simple_password_manager_prompt_async(), so we create a data struct to help us split the function in half. Also, we move all checks from the second half of the function into the first, so they can be handled synchronously. The diff is clearer if you use git diff --patience. --- src/sip-connection.c | 158 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 139 insertions(+), 19 deletions(-) diff --git a/src/sip-connection.c b/src/sip-connection.c index 51247fa..ad5caef 100644 --- a/src/sip-connection.c +++ b/src/sip-connection.c @@ -604,6 +604,54 @@ tpsip_connection_class_init (TpsipConnectionClass *klass) G_STRUCT_OFFSET (TpsipConnectionClass, contacts_class)); } +typedef struct { + TpsipConnection* self; + nua_handle_t *nh; + gchar *method; + gchar *realm; + gchar *user; +} PrivHandleAuthData; + +static PrivHandleAuthData * +priv_handle_auth_data_new (TpsipConnection* self, + nua_handle_t *nh, + const gchar *method, + const gchar *realm, + const gchar *user) +{ + PrivHandleAuthData *data = g_slice_new (PrivHandleAuthData); + + data->self = g_object_ref (self); + data->nh = nua_handle_ref (nh); + data->method = g_strdup (method); + data->realm = g_strdup (realm); + data->user = g_strdup (user); + + return data; +} + +static void +priv_handle_auth_data_free (PrivHandleAuthData *data) +{ + g_object_unref (data->self); + nua_handle_unref (data->nh); + g_free (data->method); + g_free (data->realm); + g_free (data->user); + + g_slice_free (PrivHandleAuthData, data); +} + +static void priv_password_manager_prompt_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data); +static void priv_handle_auth_continue (TpsipConnection* self, + nua_handle_t *nh, + const gchar *method, + const gchar *realm, + const gchar *user, + const gchar *password); + static gboolean priv_handle_auth (TpsipConnection* self, int status, @@ -618,7 +666,6 @@ priv_handle_auth (TpsipConnection* self, const char *realm = NULL; const char *user = NULL; const char *password = NULL; - gchar *auth = NULL; if (status != 401 && status != 407) return FALSE; @@ -646,6 +693,12 @@ priv_handle_auth (TpsipConnection* self, return FALSE; } + if (method == NULL) + { + WARNING ("no method presented for authentication"); + return FALSE; + } + /* step: determine which set of credentials to use */ if (home_realm) { @@ -679,6 +732,9 @@ priv_handle_auth (TpsipConnection* self, /* fall back to the main username */ user = priv->auth_user; password = priv->extra_auth_password; + if (password == NULL) + /* note that this prevents asking the user for a password */ + password = ""; DEBUG("using the extra auth credentials"); } @@ -689,37 +745,101 @@ priv_handle_auth (TpsipConnection* self, if (sipfrom && sipfrom->a_url[0].url_user) /* or use the userpart in "From" header */ user = sipfrom->a_url[0].url_user; + else + return FALSE; } if (password == NULL) - password = ""; + { + PrivHandleAuthData *data = NULL; - /* step: if all info is available, create an authorization response */ - g_assert (realm != NULL); - if (user && method) { - if (realm[0] == '"') - auth = g_strdup_printf ("%s:%s:%s:%s", - method, realm, user, password); - else - auth = g_strdup_printf ("%s:\"%s\":%s:%s", - method, realm, user, password); + DEBUG("asking the user for a password."); + data = priv_handle_auth_data_new (self, nh, method, realm, + user); + tp_simple_password_manager_prompt_async (priv->password_manager, + priv_password_manager_prompt_cb, data); + /* Promise that we'll handle it eventually, even if we end up just + * handling it with a blank password. */ + return TRUE; + } - DEBUG("%s authenticating user='%s' realm=%s", - wa ? "server" : "proxy", user, realm); - } + priv_handle_auth_continue (self, nh, method, realm, + user, password); + return TRUE; +} + +static void +priv_password_manager_prompt_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + PrivHandleAuthData *data = user_data; + GError *error = NULL; + const GString *password_string; + const gchar *password; + + password_string = tp_simple_password_manager_prompt_finish ( + TP_SIMPLE_PASSWORD_MANAGER (source_object), result, &error); - if (auth == NULL) + if (error != NULL) { - WARNING ("authentication data are incomplete"); - return FALSE; + /* we promised to handle the auth challenge in priv_handle_auth() by + * returning TRUE, so we need to handle it anyway, even if it means + * doing it with a blank password. + */ + DEBUG ("Auth channel failed: %s. Using blank password.", error->message); + + password = ""; + + g_error_free (error); } + else + { + TpsipConnectionPrivate *priv = + TPSIP_CONNECTION_GET_PRIVATE (data->self); + + password = password_string->str; + /* also save it for later. */ + g_free (priv->password); + priv->password = g_strdup (password); + } + + priv_handle_auth_continue (data->self, data->nh, data->method, data->realm, + data->user, password); + + priv_handle_auth_data_free (data); +} + +static void +priv_handle_auth_continue (TpsipConnection* self, + nua_handle_t *nh, + const gchar *method, + const gchar *realm, + const gchar *user, + const gchar *password) +{ + gchar *auth = NULL; + + /* step: if all info is available, create an authorization response */ + g_assert (realm != NULL); + g_assert (method != NULL); + g_assert (user != NULL); + g_assert (password != NULL); + + if (realm[0] == '"') + auth = g_strdup_printf ("%s:%s:%s:%s", + method, realm, user, password); + else + auth = g_strdup_printf ("%s:\"%s\":%s:%s", + method, realm, user, password); + + DEBUG ("%s-authenticating user='%s' realm=%s", + method, user, realm); /* step: authenticate */ nua_authenticate(nh, NUTAG_AUTH(auth), TAG_END()); g_free (auth); - - return TRUE; } static gboolean -- cgit v1.2.3 From d2a65899f22ea3d91d7c47793298c55082668be1 Mon Sep 17 00:00:00 2001 From: David Laban Date: Mon, 13 Dec 2010 17:33:56 +0000 Subject: Don't ask the user for a password on connect We don't need to do this until the server asks us to authenticate. --- src/sip-connection.c | 45 +-------------------------------------------- 1 file changed, 1 insertion(+), 44 deletions(-) diff --git a/src/sip-connection.c b/src/sip-connection.c index ad5caef..b515699 100644 --- a/src/sip-connection.c +++ b/src/sip-connection.c @@ -1005,41 +1005,6 @@ tpsip_connection_finalize (GObject *obj) G_OBJECT_CLASS (tpsip_connection_parent_class)->finalize (obj); } -static void -_password_manager_prompt_cb (GObject *source_object, - GAsyncResult *result, - gpointer user_data) -{ - TpsipConnection *self = user_data; - TpsipConnectionPrivate *priv = TPSIP_CONNECTION_GET_PRIVATE (self); - TpBaseConnection *base_conn = (TpBaseConnection *) self; - GError *error = NULL; - const GString *password; - - password = tp_simple_password_manager_prompt_finish ( - TP_SIMPLE_PASSWORD_MANAGER (source_object), result, &error); - - if (error != NULL) - { - DEBUG ("Auth channel failed: %s", error->message); - - if (base_conn->status != TP_CONNECTION_STATUS_DISCONNECTED) - { - tp_base_connection_disconnect_with_dbus_error (base_conn, - tp_error_get_dbus_name (error->code), NULL, - TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED); - } - - g_error_free (error); - return; - } - - g_free (priv->password); - priv->password = g_strdup (password->str); - - nua_register (priv->register_op, TAG_NULL()); -} - static gboolean tpsip_connection_start_connecting (TpBaseConnection *base, GError **error) @@ -1136,15 +1101,7 @@ tpsip_connection_start_connecting (TpBaseConnection *base, tpsip_event_target_attach (priv->register_op, (GObject *) self); - if (priv->password != NULL) - { - nua_register (priv->register_op, TAG_NULL()); - } - else - { - tp_simple_password_manager_prompt_async (priv->password_manager, - _password_manager_prompt_cb, self); - } + nua_register (priv->register_op, TAG_NULL()); return TRUE; } -- cgit v1.2.3 From fd3166fc69faa317c2d7d9f9770c6471d57e5a8d Mon Sep 17 00:00:00 2001 From: David Laban Date: Mon, 13 Dec 2010 20:34:52 +0000 Subject: test registering without a password and using a sasl channel to request the password if the server challenges, or not asking for a password if the server doesn't challenge. --- tests/twisted/Makefile.am | 1 + tests/twisted/sofiatest.py | 9 ++++++++- tests/twisted/test-register-sasl.py | 36 ++++++++++++++++++++++++++++++++++++ tests/twisted/test-register.py | 2 +- 4 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 tests/twisted/test-register-sasl.py diff --git a/tests/twisted/Makefile.am b/tests/twisted/Makefile.am index 5988592..086e437 100644 --- a/tests/twisted/Makefile.am +++ b/tests/twisted/Makefile.am @@ -2,6 +2,7 @@ TWISTED_TESTS = \ cm/protocol.py \ test-register.py \ test-register-fail.py \ + test-register-sasl.py \ test-handle-normalisation.py \ test-message.py \ test-self-alias.py \ diff --git a/tests/twisted/sofiatest.py b/tests/twisted/sofiatest.py index 2ec5b75..94263b9 100644 --- a/tests/twisted/sofiatest.py +++ b/tests/twisted/sofiatest.py @@ -20,6 +20,9 @@ class SipProxy(sip.RegisterProxy): def register(self, message, host, port): if hasattr(self, 'registrar_handler'): + self.event_func(servicetest.Event('sip-register', + uri=str(message.uri), headers=message.headers, body=message.body, + sip_message=message, host=host, port=port)) if self.registrar_handler(message, host, port): sip.RegisterProxy.register(self, message, host, port) else: @@ -48,7 +51,11 @@ def prepare_test(event_func, register_cb, params=None): } if params is not None: - actual_params.update(params) + for k, v in params.items(): + if v is None: + actual_params.pop(k, None) + else: + actual_params[k] = v bus = dbus.SessionBus() conn = servicetest.make_connection(bus, event_func, diff --git a/tests/twisted/test-register-sasl.py b/tests/twisted/test-register-sasl.py new file mode 100644 index 0000000..f4ef924 --- /dev/null +++ b/tests/twisted/test-register-sasl.py @@ -0,0 +1,36 @@ +""" +Test SIP registration failure. +""" + +import dbus + +from sofiatest import exec_test + +def test(q, bus, conn, sip): + conn.Connect() + q.expect('dbus-signal', signal='StatusChanged', args=[1, 1]) + + q.expect('sip-register') + + nc = q.expect('dbus-signal', signal='NewChannels') + (((path, props),),) = nc.args + assert props['org.freedesktop.Telepathy.Channel.ChannelType'] == \ + 'org.freedesktop.Telepathy.Channel.Type.ServerAuthentication' + assert props['org.freedesktop.Telepathy.Channel.Interface.SASLAuthentication.AvailableMechanisms'] == \ + ['X-TELEPATHY-PASSWORD'] + + chan = dbus.Interface(bus.get_object(conn._named_service, path), + "org.freedesktop.Telepathy.Channel.Interface.SASLAuthentication") + + chan.StartMechanismWithData('X-TELEPATHY-PASSWORD', 'wrong password') + chan.AcceptSASL() + + q.expect('sip-register') + + q.expect('dbus-signal', signal='StatusChanged', args=[2, 3]) + return True + +if __name__ == '__main__': + exec_test(test, register_cb=lambda *args: False, + params={"password": None}) + diff --git a/tests/twisted/test-register.py b/tests/twisted/test-register.py index b44b93d..9a292e7 100644 --- a/tests/twisted/test-register.py +++ b/tests/twisted/test-register.py @@ -13,5 +13,5 @@ def test(q, bus, conn, sip): return True if __name__ == '__main__': - exec_test(test) + exec_test(test, params={"password": None}) -- cgit v1.2.3 From 96c98cfc3a7f59d0c04573098e498503b4ae4a47 Mon Sep 17 00:00:00 2001 From: Jonny Lamb Date: Wed, 15 Dec 2010 14:38:27 +0000 Subject: protocol: implement TpBaseProtocolClass->dup_authentication_types Signed-off-by: Jonny Lamb --- src/protocol.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/protocol.c b/src/protocol.c index 45d960b..d9c2383 100644 --- a/src/protocol.c +++ b/src/protocol.c @@ -439,6 +439,17 @@ get_connection_details (TpBaseProtocol *self, } } +static GStrv +dup_authentication_types (TpBaseProtocol *base) +{ + const gchar * const types[] = { + TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION, + NULL + }; + + return g_strdupv ((GStrv) types); +} + static void tpsip_protocol_get_property (GObject *object, guint property_id, @@ -494,6 +505,7 @@ tpsip_protocol_class_init (TpsipProtocolClass *klass) base_class->identify_account = identify_account; base_class->get_interfaces = get_interfaces; base_class->get_connection_details = get_connection_details; + base_class->dup_authentication_types = dup_authentication_types; object_class->get_property = tpsip_protocol_get_property; object_class->set_property = tpsip_protocol_set_property; -- cgit v1.2.3 From 6ef183a45800b000fe1c402255e822963f90dc0c Mon Sep 17 00:00:00 2001 From: Jonny Lamb Date: Wed, 15 Dec 2010 14:42:50 +0000 Subject: configure: increase version of tp-glib required This is required for TpBaseProtocolClass->dup_authentication_types. Signed-off-by: Jonny Lamb --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 5d2539d..a3a5fb8 100644 --- a/configure.ac +++ b/configure.ac @@ -85,7 +85,7 @@ AC_SUBST(SOFIA_SIP_UA_CFLAGS) AC_SUBST(SOFIA_SIP_UA_VERSION) dnl Check for telepathy-glib -PKG_CHECK_MODULES(TELEPATHY_GLIB, [telepathy-glib >= 0.13.8]) +PKG_CHECK_MODULES(TELEPATHY_GLIB, [telepathy-glib >= 0.13.9]) AC_SUBST(TELEPATHY_GLIB_CFLAGS) AC_SUBST(TELEPATHY_GLIB_LIBS) -- cgit v1.2.3