summaryrefslogtreecommitdiff
path: root/TelepathyQt
diff options
context:
space:
mode:
authorJeremy Whiting <jeremy.whiting@collabora.com>2011-11-10 15:21:06 -0700
committerAndre Moreira Magalhaes (andrunko) <andre.magalhaes@collabora.co.uk>2011-11-24 14:45:44 -0200
commitbe40b6f313c8d7b1f0fb59fd06ea87b0934e4bad (patch)
treeed66d713ce0d85acf0bcb65d3145693e4d150dae /TelepathyQt
parentaafde57c570a56bb98df4103a4ee38ed25b91897 (diff)
Renamed TelepathyQt4 directory to TelepathyQt.
Diffstat (limited to 'TelepathyQt')
-rw-r--r--TelepathyQt/AbstractClient13
-rw-r--r--TelepathyQt/AbstractClientApprover13
-rw-r--r--TelepathyQt/AbstractClientHandler13
-rw-r--r--TelepathyQt/AbstractClientObserver13
-rw-r--r--TelepathyQt/AbstractInterface13
-rw-r--r--TelepathyQt/Account13
-rw-r--r--TelepathyQt/AccountCapabilityFilter13
-rw-r--r--TelepathyQt/AccountFactory13
-rw-r--r--TelepathyQt/AccountFilter13
-rw-r--r--TelepathyQt/AccountInterface13
-rw-r--r--TelepathyQt/AccountInterfaceAddressingInterface13
-rw-r--r--TelepathyQt/AccountInterfaceAvatarInterface13
-rw-r--r--TelepathyQt/AccountManager13
-rw-r--r--TelepathyQt/AccountManagerInterface13
-rw-r--r--TelepathyQt/AccountPropertyFilter13
-rw-r--r--TelepathyQt/AccountSet13
-rw-r--r--TelepathyQt/AndFilter13
-rw-r--r--TelepathyQt/AuthenticationTLSCertificateInterface13
-rw-r--r--TelepathyQt/AvatarData13
-rw-r--r--TelepathyQt/AvatarSpec13
-rw-r--r--TelepathyQt/CMakeLists.txt694
-rw-r--r--TelepathyQt/CapabilitiesBase13
-rw-r--r--TelepathyQt/Channel13
-rw-r--r--TelepathyQt/ChannelClassFeatures13
-rw-r--r--TelepathyQt/ChannelClassSpec13
-rw-r--r--TelepathyQt/ChannelClassSpecList13
-rw-r--r--TelepathyQt/ChannelDispatchOperation13
-rw-r--r--TelepathyQt/ChannelDispatchOperationInterface13
-rw-r--r--TelepathyQt/ChannelDispatcher13
-rw-r--r--TelepathyQt/ChannelDispatcherInterface13
-rw-r--r--TelepathyQt/ChannelFactory13
-rw-r--r--TelepathyQt/ChannelInterface13
-rw-r--r--TelepathyQt/ChannelInterfaceAnonymityInterface13
-rw-r--r--TelepathyQt/ChannelInterfaceCallStateInterface13
-rw-r--r--TelepathyQt/ChannelInterfaceChatStateInterface13
-rw-r--r--TelepathyQt/ChannelInterfaceConferenceInterface13
-rw-r--r--TelepathyQt/ChannelInterfaceDTMFInterface13
-rw-r--r--TelepathyQt/ChannelInterfaceGroupInterface13
-rw-r--r--TelepathyQt/ChannelInterfaceHoldInterface13
-rw-r--r--TelepathyQt/ChannelInterfaceMediaSignallingInterface13
-rw-r--r--TelepathyQt/ChannelInterfaceMessagesInterface13
-rw-r--r--TelepathyQt/ChannelInterfacePasswordInterface13
-rw-r--r--TelepathyQt/ChannelInterfaceSASLAuthenticationInterface13
-rw-r--r--TelepathyQt/ChannelInterfaceSecurableInterface13
-rw-r--r--TelepathyQt/ChannelInterfaceServicePointInterface13
-rw-r--r--TelepathyQt/ChannelInterfaceTubeInterface13
-rw-r--r--TelepathyQt/ChannelRequest13
-rw-r--r--TelepathyQt/ChannelRequestHints13
-rw-r--r--TelepathyQt/ChannelRequestInterface13
-rw-r--r--TelepathyQt/ChannelTypeContactListInterface13
-rw-r--r--TelepathyQt/ChannelTypeContactSearchInterface13
-rw-r--r--TelepathyQt/ChannelTypeFileTransferInterface13
-rw-r--r--TelepathyQt/ChannelTypeRoomListInterface13
-rw-r--r--TelepathyQt/ChannelTypeServerAuthenticationInterface13
-rw-r--r--TelepathyQt/ChannelTypeServerTLSConnectionInterface13
-rw-r--r--TelepathyQt/ChannelTypeStreamTubeInterface13
-rw-r--r--TelepathyQt/ChannelTypeStreamedMediaInterface13
-rw-r--r--TelepathyQt/ChannelTypeTextInterface13
-rw-r--r--TelepathyQt/ChannelTypeTubeInterface13
-rw-r--r--TelepathyQt/ChannelTypeTubesInterface13
-rw-r--r--TelepathyQt/Client13
-rw-r--r--TelepathyQt/ClientApproverInterface13
-rw-r--r--TelepathyQt/ClientHandlerInterface13
-rw-r--r--TelepathyQt/ClientInterface13
-rw-r--r--TelepathyQt/ClientInterfaceRequestsInterface13
-rw-r--r--TelepathyQt/ClientObserverInterface13
-rw-r--r--TelepathyQt/ClientRegistrar13
-rw-r--r--TelepathyQt/Connection13
-rw-r--r--TelepathyQt/ConnectionCapabilities13
-rw-r--r--TelepathyQt/ConnectionFactory13
-rw-r--r--TelepathyQt/ConnectionInterface13
-rw-r--r--TelepathyQt/ConnectionInterfaceAliasingInterface13
-rw-r--r--TelepathyQt/ConnectionInterfaceAnonymityInterface13
-rw-r--r--TelepathyQt/ConnectionInterfaceAvatarsInterface13
-rw-r--r--TelepathyQt/ConnectionInterfaceBalanceInterface13
-rw-r--r--TelepathyQt/ConnectionInterfaceCapabilitiesInterface13
-rw-r--r--TelepathyQt/ConnectionInterfaceCellularInterface13
-rw-r--r--TelepathyQt/ConnectionInterfaceClientTypes17
-rw-r--r--TelepathyQt/ConnectionInterfaceClientTypesInterface13
-rw-r--r--TelepathyQt/ConnectionInterfaceContactBlockingInterface13
-rw-r--r--TelepathyQt/ConnectionInterfaceContactCapabilitiesInterface13
-rw-r--r--TelepathyQt/ConnectionInterfaceContactGroups17
-rw-r--r--TelepathyQt/ConnectionInterfaceContactGroupsInterface13
-rw-r--r--TelepathyQt/ConnectionInterfaceContactInfoInterface13
-rw-r--r--TelepathyQt/ConnectionInterfaceContactList17
-rw-r--r--TelepathyQt/ConnectionInterfaceContactListInterface13
-rw-r--r--TelepathyQt/ConnectionInterfaceContactsInterface13
-rw-r--r--TelepathyQt/ConnectionInterfaceLocationInterface13
-rw-r--r--TelepathyQt/ConnectionInterfaceMailNotificationInterface13
-rw-r--r--TelepathyQt/ConnectionInterfacePowerSaving17
-rw-r--r--TelepathyQt/ConnectionInterfacePowerSavingInterface13
-rw-r--r--TelepathyQt/ConnectionInterfacePresenceInterface13
-rw-r--r--TelepathyQt/ConnectionInterfaceRequestsInterface13
-rw-r--r--TelepathyQt/ConnectionInterfaceServicePointInterface13
-rw-r--r--TelepathyQt/ConnectionInterfaceSimplePresenceInterface13
-rw-r--r--TelepathyQt/ConnectionLowlevel13
-rw-r--r--TelepathyQt/ConnectionManager13
-rw-r--r--TelepathyQt/ConnectionManagerInterface13
-rw-r--r--TelepathyQt/ConnectionManagerLowlevel13
-rw-r--r--TelepathyQt/Constants13
-rw-r--r--TelepathyQt/Contact13
-rw-r--r--TelepathyQt/ContactCapabilities13
-rw-r--r--TelepathyQt/ContactFactory13
-rw-r--r--TelepathyQt/ContactManager13
-rw-r--r--TelepathyQt/ContactMessenger13
-rw-r--r--TelepathyQt/ContactSearchChannel13
-rw-r--r--TelepathyQt/DBus13
-rw-r--r--TelepathyQt/DBusDaemonInterface13
-rw-r--r--TelepathyQt/DBusProxy13
-rw-r--r--TelepathyQt/DBusProxyFactory13
-rw-r--r--TelepathyQt/Debug13
-rw-r--r--TelepathyQt/Farsight/CMakeLists.txt52
-rw-r--r--TelepathyQt/Farsight/Channel13
-rw-r--r--TelepathyQt/Farsight/channel.cpp87
-rw-r--r--TelepathyQt/Farsight/channel.h43
-rw-r--r--TelepathyQt/Farsight/global.h46
-rw-r--r--TelepathyQt/Feature13
-rw-r--r--TelepathyQt/Features13
-rw-r--r--TelepathyQt/FileTransferChannel13
-rw-r--r--TelepathyQt/FileTransferChannelCreationProperties13
-rw-r--r--TelepathyQt/Filter13
-rw-r--r--TelepathyQt/FixedFeatureFactory13
-rw-r--r--TelepathyQt/GenericCapabilityFilter13
-rw-r--r--TelepathyQt/GenericPropertyFilter13
-rw-r--r--TelepathyQt/Global13
-rw-r--r--TelepathyQt/HandledChannelNotifier13
-rw-r--r--TelepathyQt/IncomingFileTransferChannel13
-rw-r--r--TelepathyQt/IncomingStreamTubeChannel13
-rw-r--r--TelepathyQt/IntrospectableInterface13
-rw-r--r--TelepathyQt/KeyFile13
-rw-r--r--TelepathyQt/LocationInfo13
-rw-r--r--TelepathyQt/ManagerFile13
-rw-r--r--TelepathyQt/MediaSessionHandler13
-rw-r--r--TelepathyQt/MediaSessionHandlerInterface13
-rw-r--r--TelepathyQt/MediaStreamHandler13
-rw-r--r--TelepathyQt/MediaStreamHandlerInterface13
-rw-r--r--TelepathyQt/Message13
-rw-r--r--TelepathyQt/MessageContentPart13
-rw-r--r--TelepathyQt/MessageContentPartList13
-rw-r--r--TelepathyQt/MethodInvocationContext13
-rw-r--r--TelepathyQt/NotFilter13
-rw-r--r--TelepathyQt/Object13
-rw-r--r--TelepathyQt/OptionalInterfaceFactory13
-rw-r--r--TelepathyQt/OrFilter13
-rw-r--r--TelepathyQt/OutgoingFileTransferChannel13
-rw-r--r--TelepathyQt/OutgoingStreamTubeChannel13
-rw-r--r--TelepathyQt/PeerInterface13
-rw-r--r--TelepathyQt/PendingAccount13
-rw-r--r--TelepathyQt/PendingChannel13
-rw-r--r--TelepathyQt/PendingChannelRequest13
-rw-r--r--TelepathyQt/PendingComposite13
-rw-r--r--TelepathyQt/PendingConnection13
-rw-r--r--TelepathyQt/PendingContactAttributes13
-rw-r--r--TelepathyQt/PendingContactInfo13
-rw-r--r--TelepathyQt/PendingContacts13
-rw-r--r--TelepathyQt/PendingFailure13
-rw-r--r--TelepathyQt/PendingHandles13
-rw-r--r--TelepathyQt/PendingOperation13
-rw-r--r--TelepathyQt/PendingReady13
-rw-r--r--TelepathyQt/PendingSendMessage13
-rw-r--r--TelepathyQt/PendingStreamTubeConnection13
-rw-r--r--TelepathyQt/PendingStreamedMediaStreams13
-rw-r--r--TelepathyQt/PendingStringList13
-rw-r--r--TelepathyQt/PendingSuccess13
-rw-r--r--TelepathyQt/PendingVariant13
-rw-r--r--TelepathyQt/PendingVariantMap13
-rw-r--r--TelepathyQt/PendingVoid13
-rw-r--r--TelepathyQt/Presence13
-rw-r--r--TelepathyQt/PresenceSpec13
-rw-r--r--TelepathyQt/PresenceSpecList13
-rw-r--r--TelepathyQt/Profile13
-rw-r--r--TelepathyQt/ProfileManager13
-rw-r--r--TelepathyQt/Properties13
-rw-r--r--TelepathyQt/PropertiesInterface13
-rw-r--r--TelepathyQt/PropertiesInterfaceInterface13
-rw-r--r--TelepathyQt/ProtocolInfo13
-rw-r--r--TelepathyQt/ProtocolParameter13
-rw-r--r--TelepathyQt/ReadinessHelper13
-rw-r--r--TelepathyQt/ReadyObject13
-rw-r--r--TelepathyQt/ReceivedMessage13
-rw-r--r--TelepathyQt/RefCounted13
-rw-r--r--TelepathyQt/ReferencedHandles12
-rw-r--r--TelepathyQt/ReferencedHandlesIterator6
-rw-r--r--TelepathyQt/RequestableChannelClassSpec13
-rw-r--r--TelepathyQt/RequestableChannelClassSpecList13
-rw-r--r--TelepathyQt/RoomListChannel13
-rw-r--r--TelepathyQt/SharedPtr13
-rw-r--r--TelepathyQt/SimpleCallObserver13
-rw-r--r--TelepathyQt/SimpleObserver13
-rw-r--r--TelepathyQt/SimpleTextObserver13
-rw-r--r--TelepathyQt/StatefulDBusProxy13
-rw-r--r--TelepathyQt/StatelessDBusProxy13
-rw-r--r--TelepathyQt/StreamTubeChannel13
-rw-r--r--TelepathyQt/StreamTubeClient13
-rw-r--r--TelepathyQt/StreamTubeServer13
-rw-r--r--TelepathyQt/StreamedMediaChannel13
-rw-r--r--TelepathyQt/StreamedMediaStream13
-rw-r--r--TelepathyQt/TelepathyQt-uninstalled.pc.in11
-rw-r--r--TelepathyQt/TelepathyQt.pc.in11
-rw-r--r--TelepathyQt/TelepathyQtFarsight-uninstalled.pc.in11
-rw-r--r--TelepathyQt/TelepathyQtFarsight.pc.in11
-rw-r--r--TelepathyQt/TextChannel13
-rw-r--r--TelepathyQt/TubeChannel13
-rw-r--r--TelepathyQt/Types13
-rw-r--r--TelepathyQt/Utils13
-rw-r--r--TelepathyQt/abstract-client.cpp988
-rw-r--r--TelepathyQt/abstract-client.h323
-rw-r--r--TelepathyQt/abstract-interface.cpp136
-rw-r--r--TelepathyQt/abstract-interface.h76
-rw-r--r--TelepathyQt/account-capability-filter.dox30
-rw-r--r--TelepathyQt/account-capability-filter.h39
-rw-r--r--TelepathyQt/account-factory.cpp157
-rw-r--r--TelepathyQt/account-factory.h79
-rw-r--r--TelepathyQt/account-filter.h39
-rw-r--r--TelepathyQt/account-manager.cpp1115
-rw-r--r--TelepathyQt/account-manager.h152
-rw-r--r--TelepathyQt/account-manager.xml9
-rw-r--r--TelepathyQt/account-property-filter.cpp94
-rw-r--r--TelepathyQt/account-property-filter.h59
-rw-r--r--TelepathyQt/account-set-internal.h82
-rw-r--r--TelepathyQt/account-set.cpp418
-rw-r--r--TelepathyQt/account-set.h79
-rw-r--r--TelepathyQt/account.cpp4472
-rw-r--r--TelepathyQt/account.h598
-rw-r--r--TelepathyQt/account.xml13
-rw-r--r--TelepathyQt/and-filter.dox33
-rw-r--r--TelepathyQt/and-filter.h83
-rw-r--r--TelepathyQt/async-model.dox56
-rw-r--r--TelepathyQt/avatar.cpp172
-rw-r--r--TelepathyQt/avatar.h86
-rw-r--r--TelepathyQt/capabilities-base.cpp348
-rw-r--r--TelepathyQt/capabilities-base.h85
-rw-r--r--TelepathyQt/channel-class-features.h45
-rw-r--r--TelepathyQt/channel-class-spec.cpp555
-rw-r--r--TelepathyQt/channel-class-spec.h277
-rw-r--r--TelepathyQt/channel-dispatch-operation-internal.h51
-rw-r--r--TelepathyQt/channel-dispatch-operation.cpp623
-rw-r--r--TelepathyQt/channel-dispatch-operation.h114
-rw-r--r--TelepathyQt/channel-dispatch-operation.xml9
-rw-r--r--TelepathyQt/channel-dispatcher.cpp26
-rw-r--r--TelepathyQt/channel-dispatcher.h32
-rw-r--r--TelepathyQt/channel-dispatcher.xml9
-rw-r--r--TelepathyQt/channel-factory.cpp529
-rw-r--r--TelepathyQt/channel-factory.h305
-rw-r--r--TelepathyQt/channel-internal.h50
-rw-r--r--TelepathyQt/channel-request.cpp803
-rw-r--r--TelepathyQt/channel-request.h154
-rw-r--r--TelepathyQt/channel-request.xml9
-rw-r--r--TelepathyQt/channel.cpp3596
-rw-r--r--TelepathyQt/channel.h256
-rw-r--r--TelepathyQt/channel.xml37
-rw-r--r--TelepathyQt/client-registrar-internal.h365
-rw-r--r--TelepathyQt/client-registrar.cpp1038
-rw-r--r--TelepathyQt/client-registrar.h97
-rw-r--r--TelepathyQt/client.cpp26
-rw-r--r--TelepathyQt/client.h32
-rw-r--r--TelepathyQt/client.xml14
-rw-r--r--TelepathyQt/connection-capabilities.cpp303
-rw-r--r--TelepathyQt/connection-capabilities.h77
-rw-r--r--TelepathyQt/connection-factory.cpp150
-rw-r--r--TelepathyQt/connection-factory.h78
-rw-r--r--TelepathyQt/connection-internal.h61
-rw-r--r--TelepathyQt/connection-lowlevel.h96
-rw-r--r--TelepathyQt/connection-manager-internal.h159
-rw-r--r--TelepathyQt/connection-manager-lowlevel.h64
-rw-r--r--TelepathyQt/connection-manager.cpp1076
-rw-r--r--TelepathyQt/connection-manager.h125
-rw-r--r--TelepathyQt/connection-manager.xml12
-rw-r--r--TelepathyQt/connection.cpp2578
-rw-r--r--TelepathyQt/connection.h249
-rw-r--r--TelepathyQt/connection.xml30
-rw-r--r--TelepathyQt/constants.h178
-rw-r--r--TelepathyQt/contact-capabilities.cpp127
-rw-r--r--TelepathyQt/contact-capabilities.h66
-rw-r--r--TelepathyQt/contact-factory.cpp146
-rw-r--r--TelepathyQt/contact-factory.h76
-rw-r--r--TelepathyQt/contact-manager-internal.h389
-rw-r--r--TelepathyQt/contact-manager-roster.cpp2217
-rw-r--r--TelepathyQt/contact-manager.cpp1592
-rw-r--r--TelepathyQt/contact-manager.h201
-rw-r--r--TelepathyQt/contact-messenger.cpp257
-rw-r--r--TelepathyQt/contact-messenger.h78
-rw-r--r--TelepathyQt/contact-search-channel-internal.h52
-rw-r--r--TelepathyQt/contact-search-channel.cpp676
-rw-r--r--TelepathyQt/contact-search-channel.h114
-rw-r--r--TelepathyQt/contact.cpp1352
-rw-r--r--TelepathyQt/contact.h246
-rw-r--r--TelepathyQt/dbus-daemon.xml80
-rw-r--r--TelepathyQt/dbus-introspectable.xml16
-rw-r--r--TelepathyQt/dbus-peer.xml19
-rw-r--r--TelepathyQt/dbus-properties.xml29
-rw-r--r--TelepathyQt/dbus-proxy-factory-internal.h58
-rw-r--r--TelepathyQt/dbus-proxy-factory.cpp295
-rw-r--r--TelepathyQt/dbus-proxy-factory.h85
-rw-r--r--TelepathyQt/dbus-proxy.cpp393
-rw-r--r--TelepathyQt/dbus-proxy.h122
-rw-r--r--TelepathyQt/dbus.cpp26
-rw-r--r--TelepathyQt/dbus.h54
-rw-r--r--TelepathyQt/dbus.xml12
-rw-r--r--TelepathyQt/debug-internal.h170
-rw-r--r--TelepathyQt/debug.cpp187
-rw-r--r--TelepathyQt/debug.h46
-rw-r--r--TelepathyQt/examples.dox154
-rw-r--r--TelepathyQt/fake-handler-manager-internal.cpp165
-rw-r--r--TelepathyQt/fake-handler-manager-internal.h90
-rw-r--r--TelepathyQt/feature.cpp89
-rw-r--r--TelepathyQt/feature.h94
-rw-r--r--TelepathyQt/file-transfer-channel-creation-properties.cpp433
-rw-r--r--TelepathyQt/file-transfer-channel-creation-properties.h96
-rw-r--r--TelepathyQt/file-transfer-channel.cpp703
-rw-r--r--TelepathyQt/file-transfer-channel.h108
-rw-r--r--TelepathyQt/filter.dox30
-rw-r--r--TelepathyQt/filter.h63
-rw-r--r--TelepathyQt/fixed-feature-factory.cpp116
-rw-r--r--TelepathyQt/fixed-feature-factory.h69
-rw-r--r--TelepathyQt/future-channel-dispatcher.xml20
-rw-r--r--TelepathyQt/future-channel.xml13
-rw-r--r--TelepathyQt/future-interfaces.xml14
-rw-r--r--TelepathyQt/future-internal.h36
-rw-r--r--TelepathyQt/future-misc.xml7
-rw-r--r--TelepathyQt/future.cpp32
-rw-r--r--TelepathyQt/generic-capability-filter.dox36
-rw-r--r--TelepathyQt/generic-capability-filter.h114
-rw-r--r--TelepathyQt/generic-property-filter.dox33
-rw-r--r--TelepathyQt/generic-property-filter.h77
-rw-r--r--TelepathyQt/global.h103
-rw-r--r--TelepathyQt/groups.dox115
-rw-r--r--TelepathyQt/handled-channel-notifier.cpp103
-rw-r--r--TelepathyQt/handled-channel-notifier.h75
-rw-r--r--TelepathyQt/incoming-file-transfer-channel.cpp390
-rw-r--r--TelepathyQt/incoming-file-transfer-channel.h81
-rw-r--r--TelepathyQt/incoming-stream-tube-channel.cpp435
-rw-r--r--TelepathyQt/incoming-stream-tube-channel.h80
-rw-r--r--TelepathyQt/key-file.cpp582
-rw-r--r--TelepathyQt/key-file.h91
-rw-r--r--TelepathyQt/location-info.cpp221
-rw-r--r--TelepathyQt/location-info.h95
-rw-r--r--TelepathyQt/main.dox133
-rw-r--r--TelepathyQt/manager-file.cpp618
-rw-r--r--TelepathyQt/manager-file.h78
-rw-r--r--TelepathyQt/media-session-handler.cpp26
-rw-r--r--TelepathyQt/media-session-handler.h50
-rw-r--r--TelepathyQt/media-session-handler.xml9
-rw-r--r--TelepathyQt/media-stream-handler.cpp26
-rw-r--r--TelepathyQt/media-stream-handler.h50
-rw-r--r--TelepathyQt/media-stream-handler.xml9
-rw-r--r--TelepathyQt/message-content-part.cpp96
-rw-r--r--TelepathyQt/message-content-part.h96
-rw-r--r--TelepathyQt/message.cpp911
-rw-r--r--TelepathyQt/message.h173
-rw-r--r--TelepathyQt/method-invocation-context.dox41
-rw-r--r--TelepathyQt/method-invocation-context.h192
-rw-r--r--TelepathyQt/not-filter.dox32
-rw-r--r--TelepathyQt/not-filter.h73
-rw-r--r--TelepathyQt/object.cpp65
-rw-r--r--TelepathyQt/object.h63
-rw-r--r--TelepathyQt/optional-interface-factory.cpp178
-rw-r--r--TelepathyQt/optional-interface-factory.h140
-rw-r--r--TelepathyQt/or-filter.dox33
-rw-r--r--TelepathyQt/or-filter.h83
-rw-r--r--TelepathyQt/outgoing-file-transfer-channel.cpp371
-rw-r--r--TelepathyQt/outgoing-file-transfer-channel.h78
-rw-r--r--TelepathyQt/outgoing-stream-tube-channel-internal.h122
-rw-r--r--TelepathyQt/outgoing-stream-tube-channel.cpp821
-rw-r--r--TelepathyQt/outgoing-stream-tube-channel.h89
-rw-r--r--TelepathyQt/pending-account.cpp184
-rw-r--r--TelepathyQt/pending-account.h75
-rw-r--r--TelepathyQt/pending-channel-request-internal.h73
-rw-r--r--TelepathyQt/pending-channel-request.cpp276
-rw-r--r--TelepathyQt/pending-channel-request.h84
-rw-r--r--TelepathyQt/pending-channel.cpp555
-rw-r--r--TelepathyQt/pending-channel.h103
-rw-r--r--TelepathyQt/pending-connection.cpp171
-rw-r--r--TelepathyQt/pending-connection.h73
-rw-r--r--TelepathyQt/pending-contact-attributes.cpp219
-rw-r--r--TelepathyQt/pending-contact-attributes.h77
-rw-r--r--TelepathyQt/pending-contact-info.cpp128
-rw-r--r--TelepathyQt/pending-contact-info.h66
-rw-r--r--TelepathyQt/pending-contacts.cpp468
-rw-r--r--TelepathyQt/pending-contacts.h108
-rw-r--r--TelepathyQt/pending-handles.cpp490
-rw-r--r--TelepathyQt/pending-handles.h96
-rw-r--r--TelepathyQt/pending-operation.cpp424
-rw-r--r--TelepathyQt/pending-operation.h89
-rw-r--r--TelepathyQt/pending-ready.cpp148
-rw-r--r--TelepathyQt/pending-ready.h71
-rw-r--r--TelepathyQt/pending-send-message.cpp153
-rw-r--r--TelepathyQt/pending-send-message.h77
-rw-r--r--TelepathyQt/pending-stream-tube-connection.cpp272
-rw-r--r--TelepathyQt/pending-stream-tube-connection.h81
-rw-r--r--TelepathyQt/pending-string-list.cpp100
-rw-r--r--TelepathyQt/pending-string-list.h63
-rw-r--r--TelepathyQt/pending-variant-map.cpp91
-rw-r--r--TelepathyQt/pending-variant-map.h60
-rw-r--r--TelepathyQt/pending-variant.cpp91
-rw-r--r--TelepathyQt/pending-variant.h60
-rw-r--r--TelepathyQt/presence.cpp335
-rw-r--r--TelepathyQt/presence.h135
-rw-r--r--TelepathyQt/profile-manager.cpp332
-rw-r--r--TelepathyQt/profile-manager.h75
-rw-r--r--TelepathyQt/profile.cpp1216
-rw-r--r--TelepathyQt/profile.h176
-rw-r--r--TelepathyQt/properties.cpp26
-rw-r--r--TelepathyQt/properties.h51
-rw-r--r--TelepathyQt/properties.xml9
-rw-r--r--TelepathyQt/protocol-info.cpp374
-rw-r--r--TelepathyQt/protocol-info.h102
-rw-r--r--TelepathyQt/protocol-parameter.cpp176
-rw-r--r--TelepathyQt/protocol-parameter.h87
-rw-r--r--TelepathyQt/readiness-helper.cpp684
-rw-r--r--TelepathyQt/readiness-helper.h130
-rw-r--r--TelepathyQt/ready-object.cpp161
-rw-r--r--TelepathyQt/ready-object.h69
-rw-r--r--TelepathyQt/referenced-handles.cpp333
-rw-r--r--TelepathyQt/referenced-handles.h264
-rw-r--r--TelepathyQt/request-temporary-handler-internal.cpp135
-rw-r--r--TelepathyQt/request-temporary-handler-internal.h91
-rw-r--r--TelepathyQt/requestable-channel-class-spec.cpp494
-rw-r--r--TelepathyQt/requestable-channel-class-spec.h134
-rw-r--r--TelepathyQt/room-list-channel.cpp106
-rw-r--r--TelepathyQt/room-list-channel.h59
-rw-r--r--TelepathyQt/shared-ptr.dox111
-rw-r--r--TelepathyQt/shared-ptr.h152
-rw-r--r--TelepathyQt/simple-call-observer.cpp298
-rw-r--r--TelepathyQt/simple-call-observer.h95
-rw-r--r--TelepathyQt/simple-observer-internal.h261
-rw-r--r--TelepathyQt/simple-observer.cpp643
-rw-r--r--TelepathyQt/simple-observer.h105
-rw-r--r--TelepathyQt/simple-pending-operations.h110
-rw-r--r--TelepathyQt/simple-stream-tube-handler.cpp254
-rw-r--r--TelepathyQt/simple-stream-tube-handler.h120
-rw-r--r--TelepathyQt/simple-text-observer-internal.h70
-rw-r--r--TelepathyQt/simple-text-observer.cpp298
-rw-r--r--TelepathyQt/simple-text-observer.h80
-rw-r--r--TelepathyQt/stable-interfaces.xml26
-rw-r--r--TelepathyQt/stream-tube-channel.cpp740
-rw-r--r--TelepathyQt/stream-tube-channel.h108
-rw-r--r--TelepathyQt/stream-tube-client-internal.h61
-rw-r--r--TelepathyQt/stream-tube-client.cpp1048
-rw-r--r--TelepathyQt/stream-tube-client.h217
-rw-r--r--TelepathyQt/stream-tube-server-internal.h56
-rw-r--r--TelepathyQt/stream-tube-server.cpp1134
-rw-r--r--TelepathyQt/stream-tube-server.h253
-rw-r--r--TelepathyQt/streamed-media-channel.cpp1539
-rw-r--r--TelepathyQt/streamed-media-channel.h228
-rw-r--r--TelepathyQt/test-backdoors.cpp50
-rw-r--r--TelepathyQt/test-backdoors.h59
-rw-r--r--TelepathyQt/text-channel.cpp1277
-rw-r--r--TelepathyQt/text-channel.h139
-rw-r--r--TelepathyQt/tls-certificate.cpp26
-rw-r--r--TelepathyQt/tls-certificate.h31
-rw-r--r--TelepathyQt/tls-certificate.xml9
-rw-r--r--TelepathyQt/tube-channel.cpp281
-rw-r--r--TelepathyQt/tube-channel.h80
-rw-r--r--TelepathyQt/types-internal.h156
-rw-r--r--TelepathyQt/types.cpp73
-rw-r--r--TelepathyQt/types.h171
-rw-r--r--TelepathyQt/utils.cpp120
-rw-r--r--TelepathyQt/utils.h41
459 files changed, 67731 insertions, 0 deletions
diff --git a/TelepathyQt/AbstractClient b/TelepathyQt/AbstractClient
new file mode 100644
index 00000000..7f0c94af
--- /dev/null
+++ b/TelepathyQt/AbstractClient
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_AbstractClient_HEADER_GUARD_
+#define _TelepathyQt_AbstractClient_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/abstract-client.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/AbstractClientApprover b/TelepathyQt/AbstractClientApprover
new file mode 100644
index 00000000..d264aae9
--- /dev/null
+++ b/TelepathyQt/AbstractClientApprover
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_AbstractClientApprover_HEADER_GUARD_
+#define _TelepathyQt_AbstractClientApprover_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/abstract-client.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/AbstractClientHandler b/TelepathyQt/AbstractClientHandler
new file mode 100644
index 00000000..0bec382e
--- /dev/null
+++ b/TelepathyQt/AbstractClientHandler
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_AbstractClientHandler_HEADER_GUARD_
+#define _TelepathyQt_AbstractClientHandler_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/abstract-client.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/AbstractClientObserver b/TelepathyQt/AbstractClientObserver
new file mode 100644
index 00000000..ae779798
--- /dev/null
+++ b/TelepathyQt/AbstractClientObserver
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_AbstractClientObserver_HEADER_GUARD_
+#define _TelepathyQt_AbstractClientObserver_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/abstract-client.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/AbstractInterface b/TelepathyQt/AbstractInterface
new file mode 100644
index 00000000..fd780b61
--- /dev/null
+++ b/TelepathyQt/AbstractInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_AbstractInterface_HEADER_GUARD_
+#define _TelepathyQt_AbstractInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/abstract-interface.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/Account b/TelepathyQt/Account
new file mode 100644
index 00000000..9c2387e9
--- /dev/null
+++ b/TelepathyQt/Account
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_Account_HEADER_GUARD_
+#define _TelepathyQt_Account_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/account.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/AccountCapabilityFilter b/TelepathyQt/AccountCapabilityFilter
new file mode 100644
index 00000000..29a7253f
--- /dev/null
+++ b/TelepathyQt/AccountCapabilityFilter
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_AccountCapabilityFilter_HEADER_GUARD_
+#define _TelepathyQt_AccountCapabilityFilter_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/account-capability-filter.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/AccountFactory b/TelepathyQt/AccountFactory
new file mode 100644
index 00000000..e8a23acf
--- /dev/null
+++ b/TelepathyQt/AccountFactory
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_AccountFactory_HEADER_GUARD_
+#define _TelepathyQt_AccountFactory_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/account-factory.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/AccountFilter b/TelepathyQt/AccountFilter
new file mode 100644
index 00000000..643d92e4
--- /dev/null
+++ b/TelepathyQt/AccountFilter
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_AccountFilter_HEADER_GUARD_
+#define _TelepathyQt_AccountFilter_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/account-filter.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/AccountInterface b/TelepathyQt/AccountInterface
new file mode 100644
index 00000000..f9f45617
--- /dev/null
+++ b/TelepathyQt/AccountInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_AccountInterface_HEADER_GUARD_
+#define _TelepathyQt_AccountInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/account.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/AccountInterfaceAddressingInterface b/TelepathyQt/AccountInterfaceAddressingInterface
new file mode 100644
index 00000000..c5fc153a
--- /dev/null
+++ b/TelepathyQt/AccountInterfaceAddressingInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_AccountInterfaceAddressingInterface_HEADER_GUARD_
+#define _TelepathyQt_AccountInterfaceAddressingInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/account.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/AccountInterfaceAvatarInterface b/TelepathyQt/AccountInterfaceAvatarInterface
new file mode 100644
index 00000000..b1d9e2e7
--- /dev/null
+++ b/TelepathyQt/AccountInterfaceAvatarInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_AccountInterfaceAvatarInterface_HEADER_GUARD_
+#define _TelepathyQt_AccountInterfaceAvatarInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/account.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/AccountManager b/TelepathyQt/AccountManager
new file mode 100644
index 00000000..2ca7a6e5
--- /dev/null
+++ b/TelepathyQt/AccountManager
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_AccountManager_HEADER_GUARD_
+#define _TelepathyQt_AccountManager_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/account-manager.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/AccountManagerInterface b/TelepathyQt/AccountManagerInterface
new file mode 100644
index 00000000..0266ec43
--- /dev/null
+++ b/TelepathyQt/AccountManagerInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_AccountManagerInterface_HEADER_GUARD_
+#define _TelepathyQt_AccountManagerInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/account-manager.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/AccountPropertyFilter b/TelepathyQt/AccountPropertyFilter
new file mode 100644
index 00000000..7f493242
--- /dev/null
+++ b/TelepathyQt/AccountPropertyFilter
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_AccountPropertyFilter_HEADER_GUARD_
+#define _TelepathyQt_AccountPropertyFilter_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/account-property-filter.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/AccountSet b/TelepathyQt/AccountSet
new file mode 100644
index 00000000..a60e4a40
--- /dev/null
+++ b/TelepathyQt/AccountSet
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_AccountSet_HEADER_GUARD_
+#define _TelepathyQt_AccountSet_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/account-set.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/AndFilter b/TelepathyQt/AndFilter
new file mode 100644
index 00000000..f9f8eef8
--- /dev/null
+++ b/TelepathyQt/AndFilter
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_AndFilter_HEADER_GUARD_
+#define _TelepathyQt_AndFilter_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/and-filter.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/AuthenticationTLSCertificateInterface b/TelepathyQt/AuthenticationTLSCertificateInterface
new file mode 100644
index 00000000..613f93cb
--- /dev/null
+++ b/TelepathyQt/AuthenticationTLSCertificateInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_AuthenticationTLSCertificateInterface_HEADER_GUARD_
+#define _TelepathyQt_AuthenticationTLSCertificateInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/tls-certificate.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/AvatarData b/TelepathyQt/AvatarData
new file mode 100644
index 00000000..1615d6f2
--- /dev/null
+++ b/TelepathyQt/AvatarData
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_AvatarData_HEADER_GUARD_
+#define _TelepathyQt_AvatarData_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/avatar.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/AvatarSpec b/TelepathyQt/AvatarSpec
new file mode 100644
index 00000000..838f9192
--- /dev/null
+++ b/TelepathyQt/AvatarSpec
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_AvatarSpec_HEADER_GUARD_
+#define _TelepathyQt_AvatarSpec_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/avatar.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/CMakeLists.txt b/TelepathyQt/CMakeLists.txt
new file mode 100644
index 00000000..69f127ca
--- /dev/null
+++ b/TelepathyQt/CMakeLists.txt
@@ -0,0 +1,694 @@
+file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/_gen")
+
+# Set the required flags found in TelepathyDefaults
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${VISIBILITY_HIDDEN_FLAGS} ${COMPILER_COVERAGE_FLAGS} ${DEPRECATED_DECLARATIONS_FLAGS}")
+set(LD_FLAGS "${LD_FLAGS} ${VISIBILITY_HIDDEN_FLAGS} ${COMPILER_COVERAGE_FLAGS} ${DEPRECATED_DECLARATIONS_FLAGS}")
+
+# We are building Telepathy-Qt4
+add_definitions(-DBUILDING_TP_QT)
+
+# Sources for Tp-Qt4
+set(telepathy_qt4_SRCS
+ abstract-client.cpp
+ abstract-interface.cpp
+ account.cpp
+ account-factory.cpp
+ account-manager.cpp
+ account-property-filter.cpp
+ account-set.cpp
+ account-set-internal.h
+ avatar.cpp
+ capabilities-base.cpp
+ channel.cpp
+ channel-class-spec.cpp
+ channel-dispatcher.cpp
+ channel-dispatch-operation.cpp
+ channel-factory.cpp
+ channel-internal.h
+ channel-request.cpp
+ client.cpp
+ client-registrar.cpp
+ client-registrar-internal.h
+ connection.cpp
+ connection-capabilities.cpp
+ connection-factory.cpp
+ connection-internal.h
+ connection-manager.cpp
+ connection-manager-internal.h
+ contact.cpp
+ contact-capabilities.cpp
+ contact-factory.cpp
+ contact-manager.cpp
+ contact-manager-roster.cpp
+ contact-messenger.cpp
+ contact-search-channel.cpp
+ dbus.cpp
+ dbus-proxy.cpp
+ dbus-proxy-factory.cpp
+ dbus-proxy-factory-internal.h
+ debug.cpp
+ debug-internal.h
+ fake-handler-manager-internal.cpp
+ fake-handler-manager-internal.h
+ feature.cpp
+ file-transfer-channel.cpp
+ file-transfer-channel-creation-properties.cpp
+ fixed-feature-factory.cpp
+ future.cpp
+ future-internal.h
+ handled-channel-notifier.cpp
+ incoming-file-transfer-channel.cpp
+ incoming-stream-tube-channel.cpp
+ key-file.cpp
+ location-info.cpp
+ manager-file.cpp
+ media-session-handler.cpp
+ media-stream-handler.cpp
+ message.cpp
+ message-content-part.cpp
+ object.cpp
+ optional-interface-factory.cpp
+ outgoing-file-transfer-channel.cpp
+ outgoing-stream-tube-channel.cpp
+ pending-account.cpp
+ pending-channel.cpp
+ pending-channel-request.cpp
+ pending-channel-request-internal.h
+ pending-connection.cpp
+ pending-contact-attributes.cpp
+ pending-contact-info.cpp
+ pending-contacts.cpp
+ pending-handles.cpp
+ pending-operation.cpp
+ pending-ready.cpp
+ pending-send-message.cpp
+ pending-string-list.cpp
+ pending-stream-tube-connection.cpp
+ pending-variant.cpp
+ presence.cpp
+ pending-variant-map.cpp
+ profile.cpp
+ profile-manager.cpp
+ properties.cpp
+ protocol-info.cpp
+ protocol-parameter.cpp
+ readiness-helper.cpp
+ requestable-channel-class-spec.cpp
+ ready-object.cpp
+ referenced-handles.cpp
+ request-temporary-handler-internal.cpp
+ request-temporary-handler-internal.h
+ room-list-channel.cpp
+ simple-call-observer.cpp
+ simple-observer.cpp
+ simple-observer-internal.h
+ simple-stream-tube-handler.cpp
+ simple-text-observer.cpp
+ simple-text-observer-internal.h
+ stream-tube-channel.cpp
+ stream-tube-client.cpp
+ stream-tube-client-internal.h
+ stream-tube-server.cpp
+ stream-tube-server-internal.h
+ streamed-media-channel.cpp
+ test-backdoors.cpp
+ test-backdoors.h
+ text-channel.cpp
+ tls-certificate.cpp
+ tube-channel.cpp
+ types.cpp
+ types-internal.h
+ utils.cpp)
+
+# Exported headers for Tp-Qt4
+set(telepathy_qt4_HEADERS
+ AbstractClient
+ AbstractClientApprover
+ abstract-client.h
+ AbstractClientHandler
+ AbstractClientObserver
+ AbstractInterface
+ abstract-interface.h
+ Account
+ account.h
+ AccountCapabilityFilter
+ account-capability-filter.h
+ AccountFactory
+ account-factory.h
+ AccountFilter
+ account-filter.h
+ AccountInterface
+ AccountInterfaceAddressingInterface
+ AccountInterfaceAvatarInterface
+ AccountManager
+ account-manager.h
+ AccountManagerInterface
+ account-property-filter.h
+ AccountPropertyFilter
+ AccountSet
+ account-set.h
+ AndFilter
+ and-filter.h
+ AuthenticationTLSCertificateInterface
+ AvatarData
+ AvatarSpec
+ avatar.h
+ CapabilitiesBase
+ capabilities-base.h
+ Channel
+ channel.h
+ ChannelClassFeatures
+ channel-class-features.h
+ ChannelClassSpec
+ ChannelClassSpecList
+ channel-class-spec.h
+ ChannelDispatcher
+ ChannelDispatcherInterface
+ channel-dispatcher.h
+ ChannelDispatchOperation
+ channel-dispatch-operation.h
+ ChannelDispatchOperationInterface
+ ChannelFactory
+ channel-factory.h
+ ChannelInterface
+ ChannelInterfaceAnonymityInterface
+ ChannelInterfaceCallStateInterface
+ ChannelInterfaceChatStateInterface
+ ChannelInterfaceConferenceInterface
+ ChannelInterfaceDTMFInterface
+ ChannelInterfaceGroupInterface
+ ChannelInterfaceHoldInterface
+ ChannelInterfaceMediaSignallingInterface
+ ChannelInterfaceMessagesInterface
+ ChannelInterfacePasswordInterface
+ ChannelInterfaceSASLAuthenticationInterface
+ ChannelInterfaceSecurableInterface
+ ChannelInterfaceServicePointInterface
+ ChannelInterfaceTubeInterface
+ ChannelRequest
+ ChannelRequestHints
+ channel-request.h
+ ChannelRequestInterface
+ ChannelTypeContactListInterface
+ ChannelTypeContactSearchInterface
+ ChannelTypeFileTransferInterface
+ ChannelTypeRoomListInterface
+ ChannelTypeServerAuthenticationInterface
+ ChannelTypeServerTLSConnectionInterface
+ ChannelTypeStreamedMediaInterface
+ ChannelTypeStreamTubeInterface
+ ChannelTypeTextInterface
+ ChannelTypeTubeInterface
+ ChannelTypeTubesInterface
+ Client
+ ClientApproverInterface
+ client.h
+ ClientHandlerInterface
+ ClientInterface
+ ClientInterfaceRequestsInterface
+ ClientObserverInterface
+ ClientRegistrar
+ client-registrar.h
+ Connection
+ ConnectionCapabilities
+ connection-capabilities.h
+ connection.h
+ ConnectionFactory
+ connection-factory.h
+ connection-lowlevel.h
+ ConnectionInterface
+ ConnectionInterfaceAliasingInterface
+ ConnectionInterfaceAnonymityInterface
+ ConnectionInterfaceAvatarsInterface
+ ConnectionInterfaceBalanceInterface
+ ConnectionInterfaceCapabilitiesInterface
+ ConnectionInterfaceCellularInterface
+ ConnectionInterfaceClientTypes
+ ConnectionInterfaceContactBlockingInterface
+ ConnectionInterfaceClientTypesInterface
+ ConnectionInterfaceContactCapabilitiesInterface
+ ConnectionInterfaceContactGroups
+ ConnectionInterfaceContactGroupsInterface
+ ConnectionInterfaceContactInfoInterface
+ ConnectionInterfaceContactList
+ ConnectionInterfaceContactListInterface
+ ConnectionInterfaceContactsInterface
+ ConnectionInterfaceLocationInterface
+ ConnectionInterfaceMailNotificationInterface
+ ConnectionInterfacePowerSaving
+ ConnectionInterfacePowerSavingInterface
+ ConnectionInterfacePresenceInterface
+ ConnectionInterfaceRequestsInterface
+ ConnectionInterfaceServicePointInterface
+ ConnectionInterfaceSimplePresenceInterface
+ ConnectionLowlevel
+ ConnectionManager
+ connection-manager.h
+ connection-manager-lowlevel.h
+ ConnectionManagerInterface
+ ConnectionManagerLowlevel
+ Constants
+ constants.h
+ Contact
+ contact.h
+ ContactCapabilities
+ contact-capabilities.h
+ ContactFactory
+ contact-factory.h
+ ContactManager
+ contact-manager.h
+ ContactMessenger
+ contact-messenger.h
+ ContactSearchChannel
+ contact-search-channel.h
+ DBus
+ DBusDaemonInterface
+ dbus.h
+ DBusProxy
+ dbus-proxy.h
+ DBusProxyFactory
+ dbus-proxy-factory.h
+ Debug
+ debug.h
+ Feature
+ Features
+ feature.h
+ FileTransferChannel
+ FileTransferChannelCreationProperties
+ file-transfer-channel-creation-properties.h
+ file-transfer-channel.h
+ Filter
+ filter.h
+ FixedFeatureFactory
+ fixed-feature-factory.h
+ GenericCapabilityFilter
+ generic-capability-filter.h
+ GenericPropertyFilter
+ generic-property-filter.h
+ Global
+ global.h
+ HandledChannelNotifier
+ handled-channel-notifier.h
+ IncomingFileTransferChannel
+ incoming-file-transfer-channel.h
+ IncomingStreamTubeChannel
+ incoming-stream-tube-channel.h
+ IntrospectableInterface
+ KeyFile
+ key-file.h
+ LocationInfo
+ location-info.h
+ ManagerFile
+ manager-file.h
+ MediaSessionHandler
+ media-session-handler.h
+ MediaSessionHandlerInterface
+ MediaStreamHandler
+ media-stream-handler.h
+ MediaStreamHandlerInterface
+ Message
+ message.h
+ MessageContentPart
+ MessageContentPartList
+ message-content-part.h
+ MethodInvocationContext
+ method-invocation-context.h
+ NotFilter
+ not-filter.h
+ Object
+ object.h
+ OptionalInterfaceFactory
+ optional-interface-factory.h
+ OrFilter
+ or-filter.h
+ OutgoingFileTransferChannel
+ outgoing-file-transfer-channel.h
+ OutgoingStreamTubeChannel
+ outgoing-stream-tube-channel.h
+ PeerInterface
+ PendingAccount
+ pending-account.h
+ PendingChannel
+ pending-channel.h
+ PendingChannelRequest
+ pending-channel-request.h
+ PendingComposite
+ PendingConnection
+ pending-connection.h
+ PendingContactAttributes
+ pending-contact-attributes.h
+ PendingContactInfo
+ pending-contact-info.h
+ PendingContacts
+ pending-contacts.h
+ PendingFailure
+ PendingHandles
+ pending-handles.h
+ PendingOperation
+ pending-operation.h
+ PendingReady
+ pending-ready.h
+ PendingSendMessage
+ pending-send-message.h
+ PendingStreamedMediaStreams
+ PendingStreamTubeConnection
+ pending-stream-tube-connection.h
+ PendingStringList
+ pending-string-list.h
+ PendingSuccess
+ PendingVariant
+ pending-variant.h
+ PendingVariantMap
+ pending-variant-map.h
+ PendingVoid
+ Presence
+ presence.h
+ PresenceSpec
+ PresenceSpecList
+ Profile
+ profile.h
+ ProfileManager
+ profile-manager.h
+ Properties
+ properties.h
+ PropertiesInterface
+ PropertiesInterfaceInterface
+ ProtocolInfo
+ protocol-info.h
+ ProtocolParameter
+ protocol-parameter.h
+ ReadinessHelper
+ readiness-helper.h
+ ReadyObject
+ ready-object.h
+ ReceivedMessage
+ RefCounted
+ ReferencedHandles
+ referenced-handles.h
+ ReferencedHandlesIterator
+ requestable-channel-class-spec.h
+ RequestableChannelClassSpec
+ RequestableChannelClassSpecList
+ RoomListChannel
+ room-list-channel.h
+ SharedPtr
+ shared-ptr.h
+ SimpleCallObserver
+ simple-call-observer.h
+ SimpleObserver
+ simple-observer.h
+ simple-pending-operations.h
+ SimpleTextObserver
+ simple-text-observer.h
+ StatefulDBusProxy
+ StatelessDBusProxy
+ StreamTubeChannel
+ StreamTubeClient
+ StreamTubeServer
+ stream-tube-channel.h
+ stream-tube-client.h
+ stream-tube-server.h
+ StreamedMediaChannel
+ streamed-media-channel.h
+ StreamedMediaStream
+ TextChannel
+ text-channel.h
+ tls-certificate.h
+ TubeChannel
+ tube-channel.h
+ Types
+ types.h
+ Utils
+ utils.h)
+
+# Generated headers which will be installed and exported
+set(telepathy_qt4_gen_HEADERS
+ ${CMAKE_CURRENT_BINARY_DIR}/_gen/cli-account.h
+ ${CMAKE_CURRENT_BINARY_DIR}/_gen/cli-account-manager.h
+ ${CMAKE_CURRENT_BINARY_DIR}/_gen/cli-channel.h
+ ${CMAKE_CURRENT_BINARY_DIR}/_gen/cli-channel-dispatcher.h
+ ${CMAKE_CURRENT_BINARY_DIR}/_gen/cli-channel-dispatch-operation.h
+ ${CMAKE_CURRENT_BINARY_DIR}/_gen/cli-channel-request.h
+ ${CMAKE_CURRENT_BINARY_DIR}/_gen/cli-client.h
+ ${CMAKE_CURRENT_BINARY_DIR}/_gen/cli-connection.h
+ ${CMAKE_CURRENT_BINARY_DIR}/_gen/cli-connection-manager.h
+ ${CMAKE_CURRENT_BINARY_DIR}/_gen/cli-dbus.h
+ ${CMAKE_CURRENT_BINARY_DIR}/_gen/cli-media-session-handler.h
+ ${CMAKE_CURRENT_BINARY_DIR}/_gen/cli-media-stream-handler.h
+ ${CMAKE_CURRENT_BINARY_DIR}/_gen/cli-properties.h
+ ${CMAKE_CURRENT_BINARY_DIR}/_gen/cli-tls-certificate.h
+ ${CMAKE_CURRENT_BINARY_DIR}/_gen/constants.h
+ ${CMAKE_CURRENT_BINARY_DIR}/_gen/types.h)
+
+# Headers file moc will be run on
+set(telepathy_qt4_MOC_SRCS
+ abstract-interface.h
+ account.h
+ account-factory.h
+ account-manager.h
+ account-set.h
+ account-set-internal.h
+ channel.h
+ channel-dispatch-operation.h
+ channel-dispatch-operation-internal.h
+ channel-factory.h
+ channel-internal.h
+ channel-request.h
+ client-registrar.h
+ client-registrar-internal.h
+ connection.h
+ connection-internal.h
+ connection-lowlevel.h
+ connection-manager.h
+ connection-manager-internal.h
+ connection-manager-lowlevel.h
+ contact.h
+ contact-manager.h
+ contact-manager-internal.h
+ contact-messenger.h
+ contact-search-channel.h
+ contact-search-channel-internal.h
+ dbus-proxy.h
+ dbus-proxy-factory.h
+ dbus-proxy-factory-internal.h
+ fake-handler-manager-internal.h
+ file-transfer-channel.h
+ fixed-feature-factory.h
+ handled-channel-notifier.h
+ incoming-file-transfer-channel.h
+ incoming-stream-tube-channel.h
+ object.h
+ outgoing-file-transfer-channel.h
+ outgoing-stream-tube-channel.h
+ outgoing-stream-tube-channel-internal.h
+ pending-account.h
+ pending-channel.h
+ pending-channel-request.h
+ pending-channel-request-internal.h
+ pending-connection.h
+ pending-contact-attributes.h
+ pending-contact-info.h
+ pending-contacts.h
+ pending-handles.h
+ pending-operation.h
+ pending-ready.h
+ pending-send-message.h
+ pending-stream-tube-connection.h
+ pending-string-list.h
+ pending-variant.h
+ pending-variant-map.h
+ profile-manager.h
+ readiness-helper.h
+ request-temporary-handler-internal.h
+ room-list-channel.h
+ simple-call-observer.h
+ simple-pending-operations.h
+ simple-observer.h
+ simple-observer-internal.h
+ simple-stream-tube-handler.h
+ simple-text-observer.h
+ simple-text-observer-internal.h
+ stream-tube-channel.h
+ stream-tube-client.h
+ stream-tube-client-internal.h
+ stream-tube-server.h
+ stream-tube-server-internal.h
+ streamed-media-channel.h
+ text-channel.h
+ tube-channel.h)
+
+# Generate the spec files for both stable and future spec
+set(gen_stable_spec_xml ${CMAKE_CURRENT_BINARY_DIR}/_gen/stable-spec.xml)
+set(gen_future_spec_xml ${CMAKE_CURRENT_BINARY_DIR}/_gen/future-spec.xml)
+
+tpqt_xincludator(stable-ifaces-includator ${CMAKE_CURRENT_SOURCE_DIR}/stable-interfaces.xml ${gen_stable_spec_xml})
+tpqt_xincludator(future-ifaces-includator ${CMAKE_CURRENT_SOURCE_DIR}/future-interfaces.xml ${gen_future_spec_xml})
+
+add_custom_target(all-generated-sources)
+
+tpqt_constants_gen(stable-constants ${gen_stable_spec_xml} ${CMAKE_CURRENT_BINARY_DIR}/_gen/constants.h
+ --namespace=Tp
+ --str-constant-prefix=TELEPATHY_
+ --define-prefix=TP_QT_
+ --must-define=IN_TP_QT_HEADER
+ DEPENDS stable-ifaces-includator)
+tpqt_constants_gen(future-constants ${gen_future_spec_xml} ${CMAKE_CURRENT_BINARY_DIR}/_gen/future-constants.h
+ --namespace=TpFuture
+ --str-constant-prefix=TP_FUTURE_
+ --define-prefix=TP_QT_FUTURE_
+ DEPENDS future-ifaces-includator)
+
+tpqt_types_gen(stable-typesgen ${gen_stable_spec_xml}
+ ${CMAKE_CURRENT_BINARY_DIR}/_gen/types.h ${CMAKE_CURRENT_BINARY_DIR}/_gen/types-body.hpp
+ Tp TelepathyQt/types.h TelepathyQt/Types
+ --must-define=IN_TP_QT_HEADER
+ --visibility=TP_QT_EXPORT
+ DEPENDS stable-constants)
+tpqt_types_gen(future-typesgen ${gen_future_spec_xml}
+ ${CMAKE_CURRENT_BINARY_DIR}/_gen/future-types.h ${CMAKE_CURRENT_BINARY_DIR}/_gen/future-types-body.hpp
+ TpFuture TelepathyQt/future-internal.h TelepathyQt/future-internal.h
+ DEPENDS future-constants)
+
+# Add the generated types to the library's sources
+list(APPEND telepathy_qt4_SRCS ${CMAKE_CURRENT_BINARY_DIR}/_gen/types.h)
+list(APPEND telepathy_qt4_SRCS ${CMAKE_CURRENT_BINARY_DIR}/_gen/types-body.hpp)
+list(APPEND telepathy_qt4_SRCS ${CMAKE_CURRENT_BINARY_DIR}/_gen/future-constants.h)
+list(APPEND telepathy_qt4_SRCS ${CMAKE_CURRENT_BINARY_DIR}/_gen/future-types.h)
+list(APPEND telepathy_qt4_SRCS ${CMAKE_CURRENT_BINARY_DIR}/_gen/future-types-body.hpp)
+
+# For each spec, both stable and future, generate a cli file and add it to the sources (including mocs).
+set(SPECS
+ account
+ account-manager
+ channel
+ channel-dispatcher
+ channel-dispatch-operation
+ channel-request
+ client
+ connection
+ connection-manager
+ dbus
+ media-session-handler
+ media-stream-handler
+ properties
+ tls-certificate)
+foreach(spec ${SPECS})
+ tpqt_xincludator(${spec}-spec-xincludator ${CMAKE_CURRENT_SOURCE_DIR}/${spec}.xml ${CMAKE_CURRENT_BINARY_DIR}/_gen/spec-${spec}.xml
+ DEPENDS stable-typesgen)
+ set(NEW_FILES
+ ${CMAKE_CURRENT_BINARY_DIR}/_gen/cli-${spec}-body.hpp
+ ${CMAKE_CURRENT_BINARY_DIR}/_gen/cli-${spec}.moc.hpp)
+ list(APPEND telepathy_qt4_SRCS ${NEW_FILES})
+ list(APPEND telepathy_qt4_generated_specs_mocs "moc-cli-${spec}.moc.hpp")
+ set_source_files_properties(${NEW_FILES} PROPERTIES GENERATED true)
+endforeach(spec ${SPECS})
+
+set(FUTURE_SPECS
+ channel
+ channel-dispatcher
+ misc)
+foreach(spec ${FUTURE_SPECS})
+ tpqt_xincludator(${spec}-future-xincludator ${CMAKE_CURRENT_SOURCE_DIR}/future-${spec}.xml ${CMAKE_CURRENT_BINARY_DIR}/_gen/future-${spec}.xml
+ DEPENDS stable-typesgen future-typesgen)
+ set(NEW_FILES
+ ${CMAKE_CURRENT_BINARY_DIR}/_gen/future-${spec}.h
+ ${CMAKE_CURRENT_BINARY_DIR}/_gen/future-${spec}-body.hpp
+ ${CMAKE_CURRENT_BINARY_DIR}/_gen/future-${spec}.moc.hpp)
+ list(APPEND telepathy_qt4_SRCS ${NEW_FILES})
+ list(APPEND telepathy_qt4_generated_specs_mocs "moc-future-${spec}.moc.hpp")
+ set_source_files_properties(${NEW_FILES} PROPERTIES GENERATED true)
+endforeach(spec ${FUTURE_SPECS})
+
+# The escape character in MSVC is ^
+if(MSVC)
+ set(TYPES_INCLUDE ^<TelepathyQt/Types^> )
+else(MSVC)
+ set(TYPES_INCLUDE '<TelepathyQt/Types>' )
+endif(MSVC)
+
+# Use the client generator for generating headers out of specs
+tpqt_client_generator(account clientaccount AccountManager Tp::Client --mainiface=Tp::Client::AccountInterface DEPENDS account-spec-xincludator)
+tpqt_client_generator(account-manager clientam AccountManager Tp::Client --mainiface=Tp::Client::AccountManagerInterface DEPENDS account-manager-spec-xincludator)
+tpqt_client_generator(channel clientchannel Channel Tp::Client --mainiface=Tp::Client::ChannelInterface DEPENDS channel-spec-xincludator)
+tpqt_client_generator(channel-dispatcher clientchanneldispatcher ChannelDispatcher Tp::Client --mainiface=Tp::Client::ChannelDispatcherInterface DEPENDS channel-dispatcher-spec-xincludator)
+tpqt_client_generator(channel-dispatch-operation clientchanneldispatchoperation ChannelDispatchOperation Tp::Client --mainiface=Tp::Client::ChannelDispatchOperationInterface DEPENDS channel-dispatch-operation-spec-xincludator)
+tpqt_client_generator(channel-request clientchannelrequest ChannelRequest Tp::Client --mainiface=Tp::Client::ChannelRequestInterface DEPENDS channel-request-spec-xincludator)
+tpqt_client_generator(client clientclient Client Tp::Client --mainiface=Tp::Client::ClientInterface DEPENDS client-spec-xincludator)
+tpqt_client_generator(connection clientconn Connection Tp::Client --mainiface=Tp::Client::ConnectionInterface DEPENDS connection-spec-xincludator)
+tpqt_client_generator(connection-manager clientcm ConnectionManager Tp::Client --mainiface=Tp::Client::ConnectionManagerInterface DEPENDS connection-manager-spec-xincludator)
+tpqt_client_generator(dbus clientdbus DBus Tp::Client::DBus DEPENDS dbus-spec-xincludator)
+tpqt_client_generator(media-session-handler clientmsesh MediaSessionHandler Tp::Client --mainiface=Tp::Client::MediaSessionHandlerInterface DEPENDS media-session-handler-spec-xincludator)
+tpqt_client_generator(media-stream-handler clientmstrh MediaStreamHandler Tp::Client --mainiface=Tp::Client::MediaStreamHandlerInterface DEPENDS media-stream-handler-spec-xincludator)
+tpqt_client_generator(properties clientprops Properties Tp::Client DEPENDS properties-spec-xincludator)
+tpqt_client_generator(tls-certificate clienttls TLSCertificate Tp::Client DEPENDS tls-certificate-spec-xincludator)
+
+tpqt_future_client_generator(channel TpFuture::Client --mainiface=Tp::Client::ChannelInterface DEPENDS channel-future-xincludator)
+tpqt_future_client_generator(channel-dispatcher TpFuture::Client --mainiface=Tp::Client::ChannelDispatcherInterface DEPENDS channel-dispatcher-future-xincludator)
+tpqt_future_client_generator(misc TpFuture::Client DEPENDS misc-future-xincludator)
+
+if (TARGET doxygen-doc)
+ add_dependencies(doxygen-doc all-generated-sources)
+endif (TARGET doxygen-doc)
+
+# Create the library
+if (ENABLE_COMPILER_COVERAGE)
+ add_library(telepathy-qt4 STATIC ${telepathy_qt4_SRCS})
+else (ENABLE_COMPILER_COVERAGE)
+ add_library(telepathy-qt4 SHARED ${telepathy_qt4_SRCS})
+endif (ENABLE_COMPILER_COVERAGE)
+
+# generate client moc files
+foreach(moc_src ${telepathy_qt4_MOC_SRCS})
+ set(generated_file _gen/${moc_src})
+ string(REPLACE ".h" ".moc.hpp" generated_file ${generated_file})
+ tpqt_generate_moc_i_target_deps(${CMAKE_CURRENT_SOURCE_DIR}/${moc_src} ${CMAKE_CURRENT_BINARY_DIR}/${generated_file}
+ ${telepathy_qt4_generated_specs_mocs})
+ list(APPEND telepathy_qt4_SRCS ${CMAKE_CURRENT_BINARY_DIR}/${generated_file})
+ string(REPLACE ".h" ".moc.hpp" moc_src ${moc_src})
+ add_dependencies(telepathy-qt4 "moc-${moc_src}")
+endforeach(moc_src ${telepathy_qt4_MOC_SRCS})
+
+# Link
+target_link_libraries(telepathy-qt4
+ ${QT_QTCORE_LIBRARY}
+ ${QT_QTDBUS_LIBRARY}
+ ${QT_QTNETWORK_LIBRARIES}
+ ${QT_QTXML_LIBRARIES})
+
+if (ENABLE_COMPILER_COVERAGE)
+ target_link_libraries(telepathy-qt4 gcov)
+endif (ENABLE_COMPILER_COVERAGE)
+
+# Set the correct version number
+set_target_properties(telepathy-qt4 PROPERTIES
+ SOVERSION ${TP_QT_ABI_VERSION}
+ VERSION ${TP_QT_LIBRARY_VERSION})
+
+
+# Install header files
+install(FILES ${telepathy_qt4_HEADERS} DESTINATION ${INCLUDE_INSTALL_DIR}/telepathy-1.0/TelepathyQt COMPONENT headers)
+install(FILES ${telepathy_qt4_gen_HEADERS} DESTINATION ${INCLUDE_INSTALL_DIR}/telepathy-1.0/TelepathyQt/_gen COMPONENT headers)
+
+# Install the library - watch out for the correct components
+if (WIN32)
+ install(TARGETS telepathy-qt4 RUNTIME DESTINATION ${LIB_INSTALL_DIR} COMPONENT mainlibrary
+ ARCHIVE DESTINATION ${LIB_INSTALL_DIR} COMPONENT headers)
+else (WIN32)
+ install(TARGETS telepathy-qt4 LIBRARY DESTINATION ${LIB_INSTALL_DIR} COMPONENT mainlibrary
+ ARCHIVE DESTINATION ${LIB_INSTALL_DIR} COMPONENT headers)
+endif (WIN32)
+
+# pkg-config file
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/TelepathyQt.pc.in ${CMAKE_CURRENT_BINARY_DIR}/TelepathyQt.pc)
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/TelepathyQt-uninstalled.pc.in ${CMAKE_CURRENT_BINARY_DIR}/TelepathyQt-uninstalled.pc)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/TelepathyQt.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig COMPONENT headers)
+
+# If Farsight was found, install its pkg-config files as well, and go into the subdirectory
+if(FARSIGHT_COMPONENTS_FOUND)
+ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/TelepathyQtFarsight.pc.in ${CMAKE_CURRENT_BINARY_DIR}/TelepathyQtFarsight.pc)
+ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/TelepathyQtFarsight-uninstalled.pc.in ${CMAKE_CURRENT_BINARY_DIR}/TelepathyQtFarsight-uninstalled.pc)
+ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/TelepathyQtFarsight.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig COMPONENT farsight_headers)
+endif(FARSIGHT_COMPONENTS_FOUND)
+
+add_subdirectory(Farsight)
+
diff --git a/TelepathyQt/CapabilitiesBase b/TelepathyQt/CapabilitiesBase
new file mode 100644
index 00000000..c31c2e06
--- /dev/null
+++ b/TelepathyQt/CapabilitiesBase
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_CapabilitiesBase_HEADER_GUARD_
+#define _TelepathyQt_CapabilitiesBase_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/capabilities-base.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/Channel b/TelepathyQt/Channel
new file mode 100644
index 00000000..d928dc41
--- /dev/null
+++ b/TelepathyQt/Channel
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_Channel_HEADER_GUARD_
+#define _TelepathyQt_Channel_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelClassFeatures b/TelepathyQt/ChannelClassFeatures
new file mode 100644
index 00000000..76d561ee
--- /dev/null
+++ b/TelepathyQt/ChannelClassFeatures
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelClassFeatures_HEADER_GUARD_
+#define _TelepathyQt_ChannelClassFeatures_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel-class-features.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelClassSpec b/TelepathyQt/ChannelClassSpec
new file mode 100644
index 00000000..7ea7c105
--- /dev/null
+++ b/TelepathyQt/ChannelClassSpec
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelClassSpec_HEADER_GUARD_
+#define _TelepathyQt_ChannelClassSpec_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel-class-spec.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelClassSpecList b/TelepathyQt/ChannelClassSpecList
new file mode 100644
index 00000000..7e9bf67a
--- /dev/null
+++ b/TelepathyQt/ChannelClassSpecList
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelClassSpecList_HEADER_GUARD_
+#define _TelepathyQt_ChannelClassSpecList_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel-class-spec.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelDispatchOperation b/TelepathyQt/ChannelDispatchOperation
new file mode 100644
index 00000000..2753db02
--- /dev/null
+++ b/TelepathyQt/ChannelDispatchOperation
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelDispatchOperation_HEADER_GUARD_
+#define _TelepathyQt_ChannelDispatchOperation_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel-dispatch-operation.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelDispatchOperationInterface b/TelepathyQt/ChannelDispatchOperationInterface
new file mode 100644
index 00000000..8d056b9e
--- /dev/null
+++ b/TelepathyQt/ChannelDispatchOperationInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelDispatchOperationInterface_HEADER_GUARD_
+#define _TelepathyQt_ChannelDispatchOperationInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel-dispatch-operation.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelDispatcher b/TelepathyQt/ChannelDispatcher
new file mode 100644
index 00000000..3d63b2a0
--- /dev/null
+++ b/TelepathyQt/ChannelDispatcher
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelDispatcher_HEADER_GUARD_
+#define _TelepathyQt_ChannelDispatcher_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel-dispatcher.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelDispatcherInterface b/TelepathyQt/ChannelDispatcherInterface
new file mode 100644
index 00000000..6b03037f
--- /dev/null
+++ b/TelepathyQt/ChannelDispatcherInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelDispatcherInterface_HEADER_GUARD_
+#define _TelepathyQt_ChannelDispatcherInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel-dispatcher.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelFactory b/TelepathyQt/ChannelFactory
new file mode 100644
index 00000000..7e56a397
--- /dev/null
+++ b/TelepathyQt/ChannelFactory
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelFactory_HEADER_GUARD_
+#define _TelepathyQt_ChannelFactory_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel-factory.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelInterface b/TelepathyQt/ChannelInterface
new file mode 100644
index 00000000..20db0895
--- /dev/null
+++ b/TelepathyQt/ChannelInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelInterface_HEADER_GUARD_
+#define _TelepathyQt_ChannelInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelInterfaceAnonymityInterface b/TelepathyQt/ChannelInterfaceAnonymityInterface
new file mode 100644
index 00000000..dfafd9e9
--- /dev/null
+++ b/TelepathyQt/ChannelInterfaceAnonymityInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelInterfaceAnonymityInterface_HEADER_GUARD_
+#define _TelepathyQt_ChannelInterfaceAnonymityInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelInterfaceCallStateInterface b/TelepathyQt/ChannelInterfaceCallStateInterface
new file mode 100644
index 00000000..794aa5b1
--- /dev/null
+++ b/TelepathyQt/ChannelInterfaceCallStateInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelInterfaceCallStateInterface_HEADER_GUARD_
+#define _TelepathyQt_ChannelInterfaceCallStateInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelInterfaceChatStateInterface b/TelepathyQt/ChannelInterfaceChatStateInterface
new file mode 100644
index 00000000..84469132
--- /dev/null
+++ b/TelepathyQt/ChannelInterfaceChatStateInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelInterfaceChatStateInterface_HEADER_GUARD_
+#define _TelepathyQt_ChannelInterfaceChatStateInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelInterfaceConferenceInterface b/TelepathyQt/ChannelInterfaceConferenceInterface
new file mode 100644
index 00000000..dcb0eb98
--- /dev/null
+++ b/TelepathyQt/ChannelInterfaceConferenceInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelInterfaceConferenceInterface_HEADER_GUARD_
+#define _TelepathyQt_ChannelInterfaceConferenceInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelInterfaceDTMFInterface b/TelepathyQt/ChannelInterfaceDTMFInterface
new file mode 100644
index 00000000..f72e4908
--- /dev/null
+++ b/TelepathyQt/ChannelInterfaceDTMFInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelInterfaceDTMFInterface_HEADER_GUARD_
+#define _TelepathyQt_ChannelInterfaceDTMFInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelInterfaceGroupInterface b/TelepathyQt/ChannelInterfaceGroupInterface
new file mode 100644
index 00000000..87a26a5a
--- /dev/null
+++ b/TelepathyQt/ChannelInterfaceGroupInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelInterfaceGroupInterface_HEADER_GUARD_
+#define _TelepathyQt_ChannelInterfaceGroupInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelInterfaceHoldInterface b/TelepathyQt/ChannelInterfaceHoldInterface
new file mode 100644
index 00000000..a99ee042
--- /dev/null
+++ b/TelepathyQt/ChannelInterfaceHoldInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelInterfaceHoldInterface_HEADER_GUARD_
+#define _TelepathyQt_ChannelInterfaceHoldInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelInterfaceMediaSignallingInterface b/TelepathyQt/ChannelInterfaceMediaSignallingInterface
new file mode 100644
index 00000000..4747f68e
--- /dev/null
+++ b/TelepathyQt/ChannelInterfaceMediaSignallingInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelInterfaceMediaSignallingInterface_HEADER_GUARD_
+#define _TelepathyQt_ChannelInterfaceMediaSignallingInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelInterfaceMessagesInterface b/TelepathyQt/ChannelInterfaceMessagesInterface
new file mode 100644
index 00000000..342ab11d
--- /dev/null
+++ b/TelepathyQt/ChannelInterfaceMessagesInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelInterfaceMessagesInterface_HEADER_GUARD_
+#define _TelepathyQt_ChannelInterfaceMessagesInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelInterfacePasswordInterface b/TelepathyQt/ChannelInterfacePasswordInterface
new file mode 100644
index 00000000..804d836c
--- /dev/null
+++ b/TelepathyQt/ChannelInterfacePasswordInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelInterfacePasswordInterface_HEADER_GUARD_
+#define _TelepathyQt_ChannelInterfacePasswordInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelInterfaceSASLAuthenticationInterface b/TelepathyQt/ChannelInterfaceSASLAuthenticationInterface
new file mode 100644
index 00000000..67e07552
--- /dev/null
+++ b/TelepathyQt/ChannelInterfaceSASLAuthenticationInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelInterfaceSASLConnectionInterface_HEADER_GUARD_
+#define _TelepathyQt_ChannelInterfaceSASLConnectionInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelInterfaceSecurableInterface b/TelepathyQt/ChannelInterfaceSecurableInterface
new file mode 100644
index 00000000..05a80b19
--- /dev/null
+++ b/TelepathyQt/ChannelInterfaceSecurableInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelInterfaceSecurableInterface_HEADER_GUARD_
+#define _TelepathyQt_ChannelInterfaceSecurableInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelInterfaceServicePointInterface b/TelepathyQt/ChannelInterfaceServicePointInterface
new file mode 100644
index 00000000..6d5ffd36
--- /dev/null
+++ b/TelepathyQt/ChannelInterfaceServicePointInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelInterfaceServicePointInterface_HEADER_GUARD_
+#define _TelepathyQt_ChannelInterfaceServicePointInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelInterfaceTubeInterface b/TelepathyQt/ChannelInterfaceTubeInterface
new file mode 100644
index 00000000..65ae6845
--- /dev/null
+++ b/TelepathyQt/ChannelInterfaceTubeInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelInterfaceTubeInterface_HEADER_GUARD_
+#define _TelepathyQt_ChannelInterfaceTubeInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelRequest b/TelepathyQt/ChannelRequest
new file mode 100644
index 00000000..fec50cbe
--- /dev/null
+++ b/TelepathyQt/ChannelRequest
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelRequest_HEADER_GUARD_
+#define _TelepathyQt_ChannelRequest_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel-request.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelRequestHints b/TelepathyQt/ChannelRequestHints
new file mode 100644
index 00000000..2264d95b
--- /dev/null
+++ b/TelepathyQt/ChannelRequestHints
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelRequestHints_HEADER_GUARD_
+#define _TelepathyQt_ChannelRequestHints_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel-request.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelRequestInterface b/TelepathyQt/ChannelRequestInterface
new file mode 100644
index 00000000..1de5b515
--- /dev/null
+++ b/TelepathyQt/ChannelRequestInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelRequestInterface_HEADER_GUARD_
+#define _TelepathyQt_ChannelRequestInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel-request.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelTypeContactListInterface b/TelepathyQt/ChannelTypeContactListInterface
new file mode 100644
index 00000000..2bedccf9
--- /dev/null
+++ b/TelepathyQt/ChannelTypeContactListInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelTypeContactListInterface_HEADER_GUARD_
+#define _TelepathyQt_ChannelTypeContactListInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelTypeContactSearchInterface b/TelepathyQt/ChannelTypeContactSearchInterface
new file mode 100644
index 00000000..1f7b7afd
--- /dev/null
+++ b/TelepathyQt/ChannelTypeContactSearchInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelTypeContactSearchInterface_HEADER_GUARD_
+#define _TelepathyQt_ChannelTypeContactSearchInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelTypeFileTransferInterface b/TelepathyQt/ChannelTypeFileTransferInterface
new file mode 100644
index 00000000..8d6692c6
--- /dev/null
+++ b/TelepathyQt/ChannelTypeFileTransferInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelTypeFileTransferInterface_HEADER_GUARD_
+#define _TelepathyQt_ChannelTypeFileTransferInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelTypeRoomListInterface b/TelepathyQt/ChannelTypeRoomListInterface
new file mode 100644
index 00000000..ba763a51
--- /dev/null
+++ b/TelepathyQt/ChannelTypeRoomListInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelTypeRoomListInterface_HEADER_GUARD_
+#define _TelepathyQt_ChannelTypeRoomListInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelTypeServerAuthenticationInterface b/TelepathyQt/ChannelTypeServerAuthenticationInterface
new file mode 100644
index 00000000..f03cd149
--- /dev/null
+++ b/TelepathyQt/ChannelTypeServerAuthenticationInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelTypeServerAuthenticationInterface_HEADER_GUARD_
+#define _TelepathyQt_ChannelTypeServerAuthenticationInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelTypeServerTLSConnectionInterface b/TelepathyQt/ChannelTypeServerTLSConnectionInterface
new file mode 100644
index 00000000..e69a6542
--- /dev/null
+++ b/TelepathyQt/ChannelTypeServerTLSConnectionInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelTypeServerTLSConnectionInterface_HEADER_GUARD_
+#define _TelepathyQt_ChannelTypeServerTLSConnectionInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelTypeStreamTubeInterface b/TelepathyQt/ChannelTypeStreamTubeInterface
new file mode 100644
index 00000000..390b9774
--- /dev/null
+++ b/TelepathyQt/ChannelTypeStreamTubeInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_Client_ChannelTypeStreamTubeInterface_HEADER_GUARD_
+#define _TelepathyQt_Client_ChannelTypeStreamTubeInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelTypeStreamedMediaInterface b/TelepathyQt/ChannelTypeStreamedMediaInterface
new file mode 100644
index 00000000..965d664e
--- /dev/null
+++ b/TelepathyQt/ChannelTypeStreamedMediaInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelTypeStreamedMediaInterface_HEADER_GUARD_
+#define _TelepathyQt_ChannelTypeStreamedMediaInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelTypeTextInterface b/TelepathyQt/ChannelTypeTextInterface
new file mode 100644
index 00000000..291690fd
--- /dev/null
+++ b/TelepathyQt/ChannelTypeTextInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelTypeTextInterface_HEADER_GUARD_
+#define _TelepathyQt_ChannelTypeTextInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelTypeTubeInterface b/TelepathyQt/ChannelTypeTubeInterface
new file mode 100644
index 00000000..54b4ddb9
--- /dev/null
+++ b/TelepathyQt/ChannelTypeTubeInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_Client_ChannelTypeTubeInterface_HEADER_GUARD_
+#define _TelepathyQt_Client_ChannelTypeTubeInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ChannelTypeTubesInterface b/TelepathyQt/ChannelTypeTubesInterface
new file mode 100644
index 00000000..2fc367a5
--- /dev/null
+++ b/TelepathyQt/ChannelTypeTubesInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ChannelTypeTubesInterface_HEADER_GUARD_
+#define _TelepathyQt_ChannelTypeTubesInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/Client b/TelepathyQt/Client
new file mode 100644
index 00000000..4df3c82e
--- /dev/null
+++ b/TelepathyQt/Client
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_Client_HEADER_GUARD_
+#define _TelepathyQt_Client_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/client.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ClientApproverInterface b/TelepathyQt/ClientApproverInterface
new file mode 100644
index 00000000..f69f9d82
--- /dev/null
+++ b/TelepathyQt/ClientApproverInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ClientApproverInterface_HEADER_GUARD_
+#define _TelepathyQt_ClientApproverInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/client.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ClientHandlerInterface b/TelepathyQt/ClientHandlerInterface
new file mode 100644
index 00000000..1ddc9062
--- /dev/null
+++ b/TelepathyQt/ClientHandlerInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ClientHandlerInterface_HEADER_GUARD_
+#define _TelepathyQt_ClientHandlerInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/client.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ClientInterface b/TelepathyQt/ClientInterface
new file mode 100644
index 00000000..4a6b2cc5
--- /dev/null
+++ b/TelepathyQt/ClientInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ClientInterface_HEADER_GUARD_
+#define _TelepathyQt_ClientInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/client.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ClientInterfaceRequestsInterface b/TelepathyQt/ClientInterfaceRequestsInterface
new file mode 100644
index 00000000..cef1cabc
--- /dev/null
+++ b/TelepathyQt/ClientInterfaceRequestsInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ClientInterfaceRequestsInterface_HEADER_GUARD_
+#define _TelepathyQt_ClientInterfaceRequestsInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/client.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ClientObserverInterface b/TelepathyQt/ClientObserverInterface
new file mode 100644
index 00000000..ace48ff4
--- /dev/null
+++ b/TelepathyQt/ClientObserverInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ClientObserverInterface_HEADER_GUARD_
+#define _TelepathyQt_ClientObserverInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/client.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ClientRegistrar b/TelepathyQt/ClientRegistrar
new file mode 100644
index 00000000..eac9e94e
--- /dev/null
+++ b/TelepathyQt/ClientRegistrar
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ClientRegistrar_HEADER_GUARD_
+#define _TelepathyQt_ClientRegistrar_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/client-registrar.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/Connection b/TelepathyQt/Connection
new file mode 100644
index 00000000..1f108be6
--- /dev/null
+++ b/TelepathyQt/Connection
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_Connection_HEADER_GUARD_
+#define _TelepathyQt_Connection_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/connection.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionCapabilities b/TelepathyQt/ConnectionCapabilities
new file mode 100644
index 00000000..3d5a9b1d
--- /dev/null
+++ b/TelepathyQt/ConnectionCapabilities
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ConnectionCapabilities_HEADER_GUARD_
+#define _TelepathyQt_ConnectionCapabilities_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/connection-capabilities.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionFactory b/TelepathyQt/ConnectionFactory
new file mode 100644
index 00000000..8af15eed
--- /dev/null
+++ b/TelepathyQt/ConnectionFactory
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ConnectionFactory_HEADER_GUARD_
+#define _TelepathyQt_ConnectionFactory_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/connection-factory.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionInterface b/TelepathyQt/ConnectionInterface
new file mode 100644
index 00000000..4f4fd3f8
--- /dev/null
+++ b/TelepathyQt/ConnectionInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ConnectionInterface_HEADER_GUARD_
+#define _TelepathyQt_ConnectionInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/connection.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionInterfaceAliasingInterface b/TelepathyQt/ConnectionInterfaceAliasingInterface
new file mode 100644
index 00000000..08d44a08
--- /dev/null
+++ b/TelepathyQt/ConnectionInterfaceAliasingInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ConnectionInterfaceAliasingInterface_HEADER_GUARD_
+#define _TelepathyQt_ConnectionInterfaceAliasingInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/connection.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionInterfaceAnonymityInterface b/TelepathyQt/ConnectionInterfaceAnonymityInterface
new file mode 100644
index 00000000..5bd148ed
--- /dev/null
+++ b/TelepathyQt/ConnectionInterfaceAnonymityInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ConnectionInterfaceAnonymityInterface_HEADER_GUARD_
+#define _TelepathyQt_ConnectionInterfaceAnonymityInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/connection.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionInterfaceAvatarsInterface b/TelepathyQt/ConnectionInterfaceAvatarsInterface
new file mode 100644
index 00000000..deebb8cc
--- /dev/null
+++ b/TelepathyQt/ConnectionInterfaceAvatarsInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ConnectionInterfaceAvatarsInterface_HEADER_GUARD_
+#define _TelepathyQt_ConnectionInterfaceAvatarsInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/connection.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionInterfaceBalanceInterface b/TelepathyQt/ConnectionInterfaceBalanceInterface
new file mode 100644
index 00000000..8a6fbfd3
--- /dev/null
+++ b/TelepathyQt/ConnectionInterfaceBalanceInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ConnectionInterfaceBalanceInterface_HEADER_GUARD_
+#define _TelepathyQt_ConnectionInterfaceBalanceInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/connection.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionInterfaceCapabilitiesInterface b/TelepathyQt/ConnectionInterfaceCapabilitiesInterface
new file mode 100644
index 00000000..7129834b
--- /dev/null
+++ b/TelepathyQt/ConnectionInterfaceCapabilitiesInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ConnectionInterfaceCapabilitiesInterface_HEADER_GUARD_
+#define _TelepathyQt_ConnectionInterfaceCapabilitiesInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/connection.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionInterfaceCellularInterface b/TelepathyQt/ConnectionInterfaceCellularInterface
new file mode 100644
index 00000000..8fb1b72a
--- /dev/null
+++ b/TelepathyQt/ConnectionInterfaceCellularInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ConnectionInterfaceCellularInterface_HEADER_GUARD_
+#define _TelepathyQt_ConnectionInterfaceCellularInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/connection.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionInterfaceClientTypes b/TelepathyQt/ConnectionInterfaceClientTypes
new file mode 100644
index 00000000..4d829532
--- /dev/null
+++ b/TelepathyQt/ConnectionInterfaceClientTypes
@@ -0,0 +1,17 @@
+#ifndef _TelepathyQt_ConnectionInterfaceClientTyes_HEADER_GUARD_
+#define _TelepathyQt_ConnectionInterfaceClientTyes_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#ifdef TP_QT_DEPRECATED_WARNINGS
+#warning "This file will be removed in a future tp-qt4 release, use #include <TelepathyQt/ConnectionInterfaceClientTypesInterface> instead"
+#endif
+
+#include <TelepathyQt/connection.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionInterfaceClientTypesInterface b/TelepathyQt/ConnectionInterfaceClientTypesInterface
new file mode 100644
index 00000000..9fba41f3
--- /dev/null
+++ b/TelepathyQt/ConnectionInterfaceClientTypesInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ConnectionInterfaceClientTypesInterface_HEADER_GUARD_
+#define _TelepathyQt_ConnectionInterfaceClientTypesInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/connection.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionInterfaceContactBlockingInterface b/TelepathyQt/ConnectionInterfaceContactBlockingInterface
new file mode 100644
index 00000000..57acbebe
--- /dev/null
+++ b/TelepathyQt/ConnectionInterfaceContactBlockingInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ConnectionInterfaceContactBlockingInterface_HEADER_GUARD_
+#define _TelepathyQt_ConnectionInterfaceContactBlockingInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/connection.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionInterfaceContactCapabilitiesInterface b/TelepathyQt/ConnectionInterfaceContactCapabilitiesInterface
new file mode 100644
index 00000000..a30c4b22
--- /dev/null
+++ b/TelepathyQt/ConnectionInterfaceContactCapabilitiesInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ConnectionInterfaceContactCapabilitiesInterface_HEADER_GUARD_
+#define _TelepathyQt_ConnectionInterfaceContactCapabilitiesInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/connection.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionInterfaceContactGroups b/TelepathyQt/ConnectionInterfaceContactGroups
new file mode 100644
index 00000000..48800391
--- /dev/null
+++ b/TelepathyQt/ConnectionInterfaceContactGroups
@@ -0,0 +1,17 @@
+#ifndef _TelepathyQt_ConnectionInterfaceContactGroups_HEADER_GUARD_
+#define _TelepathyQt_ConnectionInterfaceContactGroups_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#ifdef TP_QT_DEPRECATED_WARNINGS
+#warning "This file will be removed in a future tp-qt4 release, use #include <TelepathyQt/ConnectionInterfaceContactGroupsInterface> instead"
+#endif
+
+#include <TelepathyQt/connection.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionInterfaceContactGroupsInterface b/TelepathyQt/ConnectionInterfaceContactGroupsInterface
new file mode 100644
index 00000000..8891d430
--- /dev/null
+++ b/TelepathyQt/ConnectionInterfaceContactGroupsInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ConnectionInterfaceContactGroupsInterface_HEADER_GUARD_
+#define _TelepathyQt_ConnectionInterfaceContactGroupsInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/connection.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionInterfaceContactInfoInterface b/TelepathyQt/ConnectionInterfaceContactInfoInterface
new file mode 100644
index 00000000..1b465d2b
--- /dev/null
+++ b/TelepathyQt/ConnectionInterfaceContactInfoInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ConnectionInterfaceContactInfoInterface_HEADER_GUARD_
+#define _TelepathyQt_ConnectionInterfaceContactInfoInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/connection.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionInterfaceContactList b/TelepathyQt/ConnectionInterfaceContactList
new file mode 100644
index 00000000..674ab4f1
--- /dev/null
+++ b/TelepathyQt/ConnectionInterfaceContactList
@@ -0,0 +1,17 @@
+#ifndef _TelepathyQt_ConnectionInterfaceContactList_HEADER_GUARD_
+#define _TelepathyQt_ConnectionInterfaceContactList_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#ifdef TP_QT_DEPRECATED_WARNINGS
+#warning "This file will be removed in a future tp-qt4 release, use #include <TelepathyQt/ConnectionInterfaceContactListInterface> instead"
+#endif
+
+#include <TelepathyQt/connection.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionInterfaceContactListInterface b/TelepathyQt/ConnectionInterfaceContactListInterface
new file mode 100644
index 00000000..3f80a62a
--- /dev/null
+++ b/TelepathyQt/ConnectionInterfaceContactListInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ConnectionInterfaceContactListInterface_HEADER_GUARD_
+#define _TelepathyQt_ConnectionInterfaceContactListInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/connection.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionInterfaceContactsInterface b/TelepathyQt/ConnectionInterfaceContactsInterface
new file mode 100644
index 00000000..ed8e3e2e
--- /dev/null
+++ b/TelepathyQt/ConnectionInterfaceContactsInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ConnectionInterfaceContactsInterface_HEADER_GUARD_
+#define _TelepathyQt_ConnectionInterfaceContactsInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/connection.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionInterfaceLocationInterface b/TelepathyQt/ConnectionInterfaceLocationInterface
new file mode 100644
index 00000000..158efea3
--- /dev/null
+++ b/TelepathyQt/ConnectionInterfaceLocationInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ConnectionInterfaceLocationInterface_HEADER_GUARD_
+#define _TelepathyQt_ConnectionInterfaceLocationInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/connection.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionInterfaceMailNotificationInterface b/TelepathyQt/ConnectionInterfaceMailNotificationInterface
new file mode 100644
index 00000000..43cd102a
--- /dev/null
+++ b/TelepathyQt/ConnectionInterfaceMailNotificationInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ConnectionInterfaceMailNotificationInterface_HEADER_GUARD_
+#define _TelepathyQt_ConnectionInterfaceMailNotificationInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/connection.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionInterfacePowerSaving b/TelepathyQt/ConnectionInterfacePowerSaving
new file mode 100644
index 00000000..5a9d6d09
--- /dev/null
+++ b/TelepathyQt/ConnectionInterfacePowerSaving
@@ -0,0 +1,17 @@
+#ifndef _TelepathyQt_ConnectionInterfacePowerSaving_HEADER_GUARD_
+#define _TelepathyQt_ConnectionInterfacePowerSaving_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#ifdef TP_QT_DEPRECATED_WARNINGS
+#warning "This file will be removed in a future tp-qt4 release, use #include <TelepathyQt/ConnectionInterfacePowerSavingInterface> instead"
+#endif
+
+#include <TelepathyQt/connection.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionInterfacePowerSavingInterface b/TelepathyQt/ConnectionInterfacePowerSavingInterface
new file mode 100644
index 00000000..ed0a01c7
--- /dev/null
+++ b/TelepathyQt/ConnectionInterfacePowerSavingInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ConnectionInterfacePowerSavingInterface_HEADER_GUARD_
+#define _TelepathyQt_ConnectionInterfacePowerSavingInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/connection.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionInterfacePresenceInterface b/TelepathyQt/ConnectionInterfacePresenceInterface
new file mode 100644
index 00000000..7626a27c
--- /dev/null
+++ b/TelepathyQt/ConnectionInterfacePresenceInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ConnectionInterfacePresenceInterface_HEADER_GUARD_
+#define _TelepathyQt_ConnectionInterfacePresenceInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/connection.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionInterfaceRequestsInterface b/TelepathyQt/ConnectionInterfaceRequestsInterface
new file mode 100644
index 00000000..ece2c7c2
--- /dev/null
+++ b/TelepathyQt/ConnectionInterfaceRequestsInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ConnectionInterfaceRequestsInterface_HEADER_GUARD_
+#define _TelepathyQt_ConnectionInterfaceRequestsInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/connection.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionInterfaceServicePointInterface b/TelepathyQt/ConnectionInterfaceServicePointInterface
new file mode 100644
index 00000000..19f7bfbd
--- /dev/null
+++ b/TelepathyQt/ConnectionInterfaceServicePointInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ConnectionInterfaceServicePointInterface_HEADER_GUARD_
+#define _TelepathyQt_ConnectionInterfaceServicePointInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/connection.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionInterfaceSimplePresenceInterface b/TelepathyQt/ConnectionInterfaceSimplePresenceInterface
new file mode 100644
index 00000000..47dddb9e
--- /dev/null
+++ b/TelepathyQt/ConnectionInterfaceSimplePresenceInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ConnectionInterfaceSimplePresenceInterface_HEADER_GUARD_
+#define _TelepathyQt_ConnectionInterfaceSimplePresenceInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/connection.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionLowlevel b/TelepathyQt/ConnectionLowlevel
new file mode 100644
index 00000000..25a53129
--- /dev/null
+++ b/TelepathyQt/ConnectionLowlevel
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ConnectionLowlevel_HEADER_GUARD
+#define _TelepathyQt_ConnectionLowlevel_HEADER_GUARD
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/connection-lowlevel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionManager b/TelepathyQt/ConnectionManager
new file mode 100644
index 00000000..3ae63f6f
--- /dev/null
+++ b/TelepathyQt/ConnectionManager
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ConnectionManager_HEADER_GUARD_
+#define _TelepathyQt_ConnectionManager_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/connection-manager.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionManagerInterface b/TelepathyQt/ConnectionManagerInterface
new file mode 100644
index 00000000..2f829012
--- /dev/null
+++ b/TelepathyQt/ConnectionManagerInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ConnectionManagerInterface_HEADER_GUARD_
+#define _TelepathyQt_ConnectionManagerInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/connection-manager.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ConnectionManagerLowlevel b/TelepathyQt/ConnectionManagerLowlevel
new file mode 100644
index 00000000..d75e427f
--- /dev/null
+++ b/TelepathyQt/ConnectionManagerLowlevel
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ConnectionManagerLowlevel_HEADER_GUARD
+#define _TelepathyQt_ConnectionManagerLowlevel_HEADER_GUARD
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/connection-manager-lowlevel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/Constants b/TelepathyQt/Constants
new file mode 100644
index 00000000..c7ddc806
--- /dev/null
+++ b/TelepathyQt/Constants
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_Constants_HEADER_GUARD_
+#define _TelepathyQt_Constants_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/constants.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/Contact b/TelepathyQt/Contact
new file mode 100644
index 00000000..ae1a9c8d
--- /dev/null
+++ b/TelepathyQt/Contact
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_Contact_HEADER_GUARD_
+#define _TelepathyQt_Contact_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/contact.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ContactCapabilities b/TelepathyQt/ContactCapabilities
new file mode 100644
index 00000000..c012e0dd
--- /dev/null
+++ b/TelepathyQt/ContactCapabilities
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ContactCapabilities_HEADER_GUARD_
+#define _TelepathyQt_ContactCapabilities_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/contact-capabilities.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ContactFactory b/TelepathyQt/ContactFactory
new file mode 100644
index 00000000..957f2e41
--- /dev/null
+++ b/TelepathyQt/ContactFactory
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ContactFactory_HEADER_GUARD_
+#define _TelepathyQt_ContactFactory_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/contact-factory.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ContactManager b/TelepathyQt/ContactManager
new file mode 100644
index 00000000..6f5fae54
--- /dev/null
+++ b/TelepathyQt/ContactManager
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ContactManager_HEADER_GUARD_
+#define _TelepathyQt_ContactManager_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/contact-manager.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ContactMessenger b/TelepathyQt/ContactMessenger
new file mode 100644
index 00000000..58cb7e10
--- /dev/null
+++ b/TelepathyQt/ContactMessenger
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ContactMessenger_HEADER_GUARD_
+#define _TelepathyQt_ContactMessenger_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/contact-messenger.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ContactSearchChannel b/TelepathyQt/ContactSearchChannel
new file mode 100644
index 00000000..e4b8bf0d
--- /dev/null
+++ b/TelepathyQt/ContactSearchChannel
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ContactSearchChannel_HEADER_GUARD_
+#define _TelepathyQt_ContactSearchChannel_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/contact-search-channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/DBus b/TelepathyQt/DBus
new file mode 100644
index 00000000..9a72aa46
--- /dev/null
+++ b/TelepathyQt/DBus
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_DBus_HEADER_GUARD_
+#define _TelepathyQt_DBus_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/dbus.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/DBusDaemonInterface b/TelepathyQt/DBusDaemonInterface
new file mode 100644
index 00000000..e003fd6a
--- /dev/null
+++ b/TelepathyQt/DBusDaemonInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_DBusDaemonInterface_HEADER_GUARD_
+#define _TelepathyQt_DBusDaemonInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/dbus.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/DBusProxy b/TelepathyQt/DBusProxy
new file mode 100644
index 00000000..a26c1c2e
--- /dev/null
+++ b/TelepathyQt/DBusProxy
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_DBusProxy_HEADER_GUARD_
+#define _TelepathyQt_DBusProxy_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/dbus-proxy.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/DBusProxyFactory b/TelepathyQt/DBusProxyFactory
new file mode 100644
index 00000000..c40af4ef
--- /dev/null
+++ b/TelepathyQt/DBusProxyFactory
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_DBusProxyFactory_HEADER_GUARD_
+#define _TelepathyQt_DBusProxyFactory_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/dbus-proxy-factory.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/Debug b/TelepathyQt/Debug
new file mode 100644
index 00000000..f67ec73b
--- /dev/null
+++ b/TelepathyQt/Debug
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_Debug_HEADER_GUARD_
+#define _TelepathyQt_Debug_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/debug.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/Farsight/CMakeLists.txt b/TelepathyQt/Farsight/CMakeLists.txt
new file mode 100644
index 00000000..0deba3c2
--- /dev/null
+++ b/TelepathyQt/Farsight/CMakeLists.txt
@@ -0,0 +1,52 @@
+if(FARSIGHT_COMPONENTS_FOUND)
+ include_directories(${TELEPATHY_FARSIGHT_INCLUDE_DIR}
+ ${GSTREAMER_INCLUDE_DIR}
+ ${GLIB2_INCLUDE_DIR}
+ ${LIBXML2_INCLUDE_DIR}
+ ${DBUS_INCLUDE_DIR})
+
+ # It gets inherited from the previous directory, hence it has to be removed explicitely
+ remove_definitions(-DBUILDING_TP_QT)
+ # We are building Telepathy-Qt4-Farsight
+ add_definitions(-DBUILDING_TP_QT_FARSIGHT -DQT_NO_KEYWORDS)
+
+ set(telepathy_qt4_farsight_SRCS
+ channel.cpp)
+
+ set(telepathy_qt4_farsight_HEADERS
+ Channel
+ channel.h
+ global.h)
+
+ # Create the library
+ if (ENABLE_COMPILER_COVERAGE)
+ add_library(telepathy-qt4-farsight STATIC ${telepathy_qt4_farsight_SRCS})
+ else (ENABLE_COMPILER_COVERAGE)
+ add_library(telepathy-qt4-farsight SHARED ${telepathy_qt4_farsight_SRCS})
+ endif (ENABLE_COMPILER_COVERAGE)
+ # Link
+ target_link_libraries(telepathy-qt4-farsight
+ ${QT_QTDBUS_LIBRARY}
+ ${QT_QTCORE_LIBRARY}
+ ${TELEPATHY_FARSIGHT_LIBRARIES}
+ ${GSTREAMER_INTERFACE_LIBRARY}
+ telepathy-qt4)
+
+ # Set the correct version number
+ set_target_properties(telepathy-qt4-farsight PROPERTIES
+ SOVERSION ${TP_QT_ABI_VERSION}
+ VERSION ${TP_QT_LIBRARY_VERSION})
+
+ # Install the library - watch out for the correct components
+ if (WIN32)
+ install(TARGETS telepathy-qt4-farsight RUNTIME DESTINATION ${LIB_INSTALL_DIR} COMPONENT farsight
+ ARCHIVE DESTINATION ${LIB_INSTALL_DIR} COMPONENT farsight_headers)
+ else (WIN32)
+ install(TARGETS telepathy-qt4-farsight LIBRARY DESTINATION ${LIB_INSTALL_DIR} COMPONENT farsight
+ ARCHIVE DESTINATION ${LIB_INSTALL_DIR} COMPONENT farsight_headers)
+ endif (WIN32)
+
+ # Install headers
+ install(FILES ${telepathy_qt4_farsight_HEADERS} DESTINATION ${INCLUDE_INSTALL_DIR}/telepathy-1.0/TelepathyQt/Farsight
+ COMPONENT farsight_headers)
+endif(FARSIGHT_COMPONENTS_FOUND)
diff --git a/TelepathyQt/Farsight/Channel b/TelepathyQt/Farsight/Channel
new file mode 100644
index 00000000..ea05f89d
--- /dev/null
+++ b/TelepathyQt/Farsight/Channel
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_Farsight_Channel_HEADER_GUARD_
+#define _TelepathyQt_Farsight_Channel_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Farsight/channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/Farsight/channel.cpp b/TelepathyQt/Farsight/channel.cpp
new file mode 100644
index 00000000..d920176e
--- /dev/null
+++ b/TelepathyQt/Farsight/channel.cpp
@@ -0,0 +1,87 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/Farsight/Channel>
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/Channel>
+#include <TelepathyQt/Connection>
+#include <TelepathyQt/StreamedMediaChannel>
+
+#include <telepathy-glib/channel.h>
+#include <telepathy-glib/connection.h>
+#include <telepathy-glib/dbus.h>
+
+namespace Tp
+{
+
+TfChannel *createFarsightChannel(const StreamedMediaChannelPtr &channel)
+{
+ if (!channel->handlerStreamingRequired()) {
+ warning() << "Handler streaming not required";
+ return 0;
+ }
+
+ TpDBusDaemon *dbus = tp_dbus_daemon_dup(0);
+
+ if (!dbus) {
+ warning() << "Unable to connect to D-Bus";
+ return 0;
+ }
+
+ ConnectionPtr connection = channel->connection();
+
+ TpConnection *gconnection = tp_connection_new(dbus,
+ connection->busName().toAscii(),
+ connection->objectPath().toAscii(),
+ 0);
+ g_object_unref(dbus);
+ dbus = 0;
+
+ if (!gconnection) {
+ warning() << "Unable to construct TpConnection";
+ return 0;
+ }
+
+ TpChannel *gchannel = tp_channel_new(gconnection,
+ channel->objectPath().toAscii(),
+ TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA,
+ (TpHandleType) channel->targetHandleType(),
+ channel->targetHandle(),
+ 0);
+ g_object_unref(gconnection);
+ gconnection = 0;
+
+ if (!gchannel) {
+ warning() << "Unable to construct TpChannel";
+ return 0;
+ }
+
+ TfChannel *ret = tf_channel_new(gchannel);
+ g_object_unref(gchannel);
+ gchannel = 0;
+
+ return ret;
+}
+
+} // Tp
diff --git a/TelepathyQt/Farsight/channel.h b/TelepathyQt/Farsight/channel.h
new file mode 100644
index 00000000..1dffb940
--- /dev/null
+++ b/TelepathyQt/Farsight/channel.h
@@ -0,0 +1,43 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_Farsight_channel_h_HEADER_GUARD_
+#define _TelepathyQt_Farsight_channel_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Farsight/global.h>
+
+#include <TelepathyQt/Types>
+
+#include <telepathy-farsight/channel.h>
+
+namespace Tp
+{
+
+TP_QT_FS_EXPORT TfChannel *createFarsightChannel(const StreamedMediaChannelPtr &channel);
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/Farsight/global.h b/TelepathyQt/Farsight/global.h
new file mode 100644
index 00000000..2d5c1100
--- /dev/null
+++ b/TelepathyQt/Farsight/global.h
@@ -0,0 +1,46 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_Farsight_global_h_HEADER_GUARD_
+#define _TelepathyQt_Farsight_global_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <QtGlobal>
+
+#ifdef BUILDING_TP_QT_FARSIGHT
+# define TP_QT_FS_EXPORT Q_DECL_EXPORT
+#else
+# define TP_QT_FS_EXPORT Q_DECL_IMPORT
+#endif
+
+#if !defined(Q_OS_WIN) && defined(QT_VISIBILITY_AVAILABLE)
+# define TP_QT_FS_NO_EXPORT __attribute__((visibility("hidden")))
+#endif
+
+#ifndef TP_QT_FS_NO_EXPORT
+# define TP_QT_FS_NO_EXPORT
+#endif
+
+#endif
diff --git a/TelepathyQt/Feature b/TelepathyQt/Feature
new file mode 100644
index 00000000..20d307a4
--- /dev/null
+++ b/TelepathyQt/Feature
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_Feature_HEADER_GUARD_
+#define _TelepathyQt_Feature_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/feature.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/Features b/TelepathyQt/Features
new file mode 100644
index 00000000..d2e6f515
--- /dev/null
+++ b/TelepathyQt/Features
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_Features_HEADER_GUARD_
+#define _TelepathyQt_Features_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/feature.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/FileTransferChannel b/TelepathyQt/FileTransferChannel
new file mode 100644
index 00000000..fc506226
--- /dev/null
+++ b/TelepathyQt/FileTransferChannel
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_FileTransferChannel_HEADER_GUARD_
+#define _TelepathyQt_FileTransferChannel_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/file-transfer-channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/FileTransferChannelCreationProperties b/TelepathyQt/FileTransferChannelCreationProperties
new file mode 100644
index 00000000..5908db7b
--- /dev/null
+++ b/TelepathyQt/FileTransferChannelCreationProperties
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_FileTransferChannelCreationProperties_HEADER_GUARD_
+#define _TelepathyQt_FileTransferChannelCreationProperties_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/file-transfer-channel-creation-properties.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/Filter b/TelepathyQt/Filter
new file mode 100644
index 00000000..c14e4eaf
--- /dev/null
+++ b/TelepathyQt/Filter
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_Filter_HEADER_GUARD_
+#define _TelepathyQt_Filter_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/filter.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/FixedFeatureFactory b/TelepathyQt/FixedFeatureFactory
new file mode 100644
index 00000000..4fc90138
--- /dev/null
+++ b/TelepathyQt/FixedFeatureFactory
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_FixedFeatureFactory_HEADER_GUARD_
+#define _TelepathyQt_FixedFeatureFactory_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/fixed-feature-factory.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/GenericCapabilityFilter b/TelepathyQt/GenericCapabilityFilter
new file mode 100644
index 00000000..f6c41e90
--- /dev/null
+++ b/TelepathyQt/GenericCapabilityFilter
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_GenericCapabilityFilter_HEADER_GUARD_
+#define _TelepathyQt_GenericCapabilityFilter_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/generic-capability-filter.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/GenericPropertyFilter b/TelepathyQt/GenericPropertyFilter
new file mode 100644
index 00000000..1dc535cd
--- /dev/null
+++ b/TelepathyQt/GenericPropertyFilter
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_GenericPropertyFilter_HEADER_GUARD_
+#define _TelepathyQt_GenericPropertyFilter_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/generic-property-filter.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/Global b/TelepathyQt/Global
new file mode 100644
index 00000000..6d227ae5
--- /dev/null
+++ b/TelepathyQt/Global
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_Global_HEADER_GUARD_
+#define _TelepathyQt_Global_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/global.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/HandledChannelNotifier b/TelepathyQt/HandledChannelNotifier
new file mode 100644
index 00000000..987cb8e8
--- /dev/null
+++ b/TelepathyQt/HandledChannelNotifier
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_HandledChannelNotifier_HEADER_GUARD_
+#define _TelepathyQt_HandledChannelNotifier_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/handled-channel-notifier.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/IncomingFileTransferChannel b/TelepathyQt/IncomingFileTransferChannel
new file mode 100644
index 00000000..092bd683
--- /dev/null
+++ b/TelepathyQt/IncomingFileTransferChannel
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_IncomingFileTransferChannel_HEADER_GUARD_
+#define _TelepathyQt_IncomingFileTransferChannel_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/incoming-file-transfer-channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/IncomingStreamTubeChannel b/TelepathyQt/IncomingStreamTubeChannel
new file mode 100644
index 00000000..36e32486
--- /dev/null
+++ b/TelepathyQt/IncomingStreamTubeChannel
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_IncomingStreamTubeChannel_HEADER_GUARD_
+#define _TelepathyQt_IncomingStreamTubeChannel_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/incoming-stream-tube-channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/IntrospectableInterface b/TelepathyQt/IntrospectableInterface
new file mode 100644
index 00000000..1b11a4d4
--- /dev/null
+++ b/TelepathyQt/IntrospectableInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_IntrospectableInterface_HEADER_GUARD_
+#define _TelepathyQt_IntrospectableInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/dbus.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/KeyFile b/TelepathyQt/KeyFile
new file mode 100644
index 00000000..1bbb3cd0
--- /dev/null
+++ b/TelepathyQt/KeyFile
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_KeyFile_HEADER_GUARD_
+#define _TelepathyQt_KeyFile_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/key-file.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/LocationInfo b/TelepathyQt/LocationInfo
new file mode 100644
index 00000000..c924e9aa
--- /dev/null
+++ b/TelepathyQt/LocationInfo
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_LocationInfo_HEADER_GUARD_
+#define _TelepathyQt_LocationInfo_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/location-info.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ManagerFile b/TelepathyQt/ManagerFile
new file mode 100644
index 00000000..0765484b
--- /dev/null
+++ b/TelepathyQt/ManagerFile
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ManagerFile_HEADER_GUARD_
+#define _TelepathyQt_ManagerFile_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/manager-file.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/MediaSessionHandler b/TelepathyQt/MediaSessionHandler
new file mode 100644
index 00000000..bdabd07c
--- /dev/null
+++ b/TelepathyQt/MediaSessionHandler
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_MediaSessionHandler_HEADER_GUARD_
+#define _TelepathyQt_MediaSessionHandler_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/media-session-handler.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/MediaSessionHandlerInterface b/TelepathyQt/MediaSessionHandlerInterface
new file mode 100644
index 00000000..25760fa5
--- /dev/null
+++ b/TelepathyQt/MediaSessionHandlerInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_MediaSessionHandlerInterface_HEADER_GUARD_
+#define _TelepathyQt_MediaSessionHandlerInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/media-session-handler.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/MediaStreamHandler b/TelepathyQt/MediaStreamHandler
new file mode 100644
index 00000000..3ead065c
--- /dev/null
+++ b/TelepathyQt/MediaStreamHandler
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_MediaStreamHandler_HEADER_GUARD_
+#define _TelepathyQt_MediaStreamHandler_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/media-stream-handler.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/MediaStreamHandlerInterface b/TelepathyQt/MediaStreamHandlerInterface
new file mode 100644
index 00000000..760eb087
--- /dev/null
+++ b/TelepathyQt/MediaStreamHandlerInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_MediaStreamHandlerInterface_HEADER_GUARD_
+#define _TelepathyQt_MediaStreamHandlerInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/media-stream-handler.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/Message b/TelepathyQt/Message
new file mode 100644
index 00000000..2ebdbf6a
--- /dev/null
+++ b/TelepathyQt/Message
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_Message_HEADER_GUARD_
+#define _TelepathyQt_Message_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/message.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/MessageContentPart b/TelepathyQt/MessageContentPart
new file mode 100644
index 00000000..94a45e74
--- /dev/null
+++ b/TelepathyQt/MessageContentPart
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_MessageContentPart_HEADER_GUARD_
+#define _TelepathyQt_MessageContentPart_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/message-content-part.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/MessageContentPartList b/TelepathyQt/MessageContentPartList
new file mode 100644
index 00000000..b15e5aca
--- /dev/null
+++ b/TelepathyQt/MessageContentPartList
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_MessageContentPartList_HEADER_GUARD_
+#define _TelepathyQt_MessageContentPartList_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/message-content-part.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/MethodInvocationContext b/TelepathyQt/MethodInvocationContext
new file mode 100644
index 00000000..5c9bfd66
--- /dev/null
+++ b/TelepathyQt/MethodInvocationContext
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_MethodInvocationContext_HEADER_GUARD_
+#define _TelepathyQt_MethodInvocationContext_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/method-invocation-context.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/NotFilter b/TelepathyQt/NotFilter
new file mode 100644
index 00000000..9947d99b
--- /dev/null
+++ b/TelepathyQt/NotFilter
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_NotFilter_HEADER_GUARD_
+#define _TelepathyQt_NotFilter_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/not-filter.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/Object b/TelepathyQt/Object
new file mode 100644
index 00000000..077c9317
--- /dev/null
+++ b/TelepathyQt/Object
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_Object_HEADER_GUARD_
+#define _TelepathyQt_Object_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/object.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/OptionalInterfaceFactory b/TelepathyQt/OptionalInterfaceFactory
new file mode 100644
index 00000000..910a65a8
--- /dev/null
+++ b/TelepathyQt/OptionalInterfaceFactory
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_OptionalInterfaceFactory_HEADER_GUARD_
+#define _TelepathyQt_OptionalInterfaceFactory_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/optional-interface-factory.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/OrFilter b/TelepathyQt/OrFilter
new file mode 100644
index 00000000..a54ceb89
--- /dev/null
+++ b/TelepathyQt/OrFilter
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_OrFilter_HEADER_GUARD_
+#define _TelepathyQt_OrFilter_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/or-filter.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/OutgoingFileTransferChannel b/TelepathyQt/OutgoingFileTransferChannel
new file mode 100644
index 00000000..f04a4336
--- /dev/null
+++ b/TelepathyQt/OutgoingFileTransferChannel
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_OutgoingFileTransferChannel_HEADER_GUARD_
+#define _TelepathyQt_OutgoingFileTransferChannel_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/outgoing-file-transfer-channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/OutgoingStreamTubeChannel b/TelepathyQt/OutgoingStreamTubeChannel
new file mode 100644
index 00000000..18a0b147
--- /dev/null
+++ b/TelepathyQt/OutgoingStreamTubeChannel
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_OutgoingStreamTubeChannel_HEADER_GUARD_
+#define _TelepathyQt_OutgoingStreamTubeChannel_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/outgoing-stream-tube-channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/PeerInterface b/TelepathyQt/PeerInterface
new file mode 100644
index 00000000..3acee4af
--- /dev/null
+++ b/TelepathyQt/PeerInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_PeerInterface_HEADER_GUARD_
+#define _TelepathyQt_PeerInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/dbus.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/PendingAccount b/TelepathyQt/PendingAccount
new file mode 100644
index 00000000..66cba3cd
--- /dev/null
+++ b/TelepathyQt/PendingAccount
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_PendingAccount_HEADER_GUARD_
+#define _TelepathyQt_PendingAccount_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/pending-account.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/PendingChannel b/TelepathyQt/PendingChannel
new file mode 100644
index 00000000..1b023c8d
--- /dev/null
+++ b/TelepathyQt/PendingChannel
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_PendingChannel_HEADER_GUARD_
+#define _TelepathyQt_PendingChannel_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/pending-channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/PendingChannelRequest b/TelepathyQt/PendingChannelRequest
new file mode 100644
index 00000000..22e8bbb3
--- /dev/null
+++ b/TelepathyQt/PendingChannelRequest
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_PendingChannelRequest_HEADER_GUARD_
+#define _TelepathyQt_PendingChannelRequest_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/pending-channel-request.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/PendingComposite b/TelepathyQt/PendingComposite
new file mode 100644
index 00000000..d6c281e1
--- /dev/null
+++ b/TelepathyQt/PendingComposite
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_PendingComposite_HEADER_GUARD_
+#define _TelepathyQt_PendingComposite_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/simple-pending-operations.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/PendingConnection b/TelepathyQt/PendingConnection
new file mode 100644
index 00000000..6f4ab9ac
--- /dev/null
+++ b/TelepathyQt/PendingConnection
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_PendingConnection_HEADER_GUARD_
+#define _TelepathyQt_PendingConnection_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/pending-connection.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/PendingContactAttributes b/TelepathyQt/PendingContactAttributes
new file mode 100644
index 00000000..e67e2055
--- /dev/null
+++ b/TelepathyQt/PendingContactAttributes
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_PendingContactAttributes_HEADER_GUARD_
+#define _TelepathyQt_PendingContactAttributes_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/pending-contact-attributes.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/PendingContactInfo b/TelepathyQt/PendingContactInfo
new file mode 100644
index 00000000..95cc7933
--- /dev/null
+++ b/TelepathyQt/PendingContactInfo
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_PendingContactInfo_HEADER_GUARD_
+#define _TelepathyQt_PendingContactInfo_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/pending-contact-info.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/PendingContacts b/TelepathyQt/PendingContacts
new file mode 100644
index 00000000..d74e6cee
--- /dev/null
+++ b/TelepathyQt/PendingContacts
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_PendingContacts_HEADER_GUARD_
+#define _TelepathyQt_PendingContacts_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/pending-contacts.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/PendingFailure b/TelepathyQt/PendingFailure
new file mode 100644
index 00000000..ccc62b3e
--- /dev/null
+++ b/TelepathyQt/PendingFailure
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_PendingFailure_HEADER_GUARD_
+#define _TelepathyQt_PendingFailure_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/simple-pending-operations.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/PendingHandles b/TelepathyQt/PendingHandles
new file mode 100644
index 00000000..27d0e197
--- /dev/null
+++ b/TelepathyQt/PendingHandles
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_PendingHandles_HEADER_GUARD_
+#define _TelepathyQt_PendingHandles_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/pending-handles.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/PendingOperation b/TelepathyQt/PendingOperation
new file mode 100644
index 00000000..b56e8d42
--- /dev/null
+++ b/TelepathyQt/PendingOperation
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_PendingOperation_HEADER_GUARD_
+#define _TelepathyQt_PendingOperation_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/pending-operation.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/PendingReady b/TelepathyQt/PendingReady
new file mode 100644
index 00000000..f2f67e94
--- /dev/null
+++ b/TelepathyQt/PendingReady
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_PendingReady_HEADER_GUARD_
+#define _TelepathyQt_PendingReady_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/pending-ready.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/PendingSendMessage b/TelepathyQt/PendingSendMessage
new file mode 100644
index 00000000..9b6f3496
--- /dev/null
+++ b/TelepathyQt/PendingSendMessage
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_PendingSendMessage_HEADER_GUARD_
+#define _TelepathyQt_PendingSendMessage_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/pending-send-message.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/PendingStreamTubeConnection b/TelepathyQt/PendingStreamTubeConnection
new file mode 100644
index 00000000..36abeaa1
--- /dev/null
+++ b/TelepathyQt/PendingStreamTubeConnection
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_PendingStreamTubeConnection_HEADER_GUARD_
+#define _TelepathyQt_PendingStreamTubeConnection_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/pending-stream-tube-connection.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/PendingStreamedMediaStreams b/TelepathyQt/PendingStreamedMediaStreams
new file mode 100644
index 00000000..6373fe6c
--- /dev/null
+++ b/TelepathyQt/PendingStreamedMediaStreams
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_PendingStreamedMediaStreams_HEADER_GUARD_
+#define _TelepathyQt_PendingStreamedMediaStreams_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/streamed-media-channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/PendingStringList b/TelepathyQt/PendingStringList
new file mode 100644
index 00000000..cab8b464
--- /dev/null
+++ b/TelepathyQt/PendingStringList
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_PendingStringList_HEADER_GUARD_
+#define _TelepathyQt_PendingStringList_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/pending-string-list.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/PendingSuccess b/TelepathyQt/PendingSuccess
new file mode 100644
index 00000000..80876b72
--- /dev/null
+++ b/TelepathyQt/PendingSuccess
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_PendingSuccess_HEADER_GUARD_
+#define _TelepathyQt_PendingSuccess_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/simple-pending-operations.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/PendingVariant b/TelepathyQt/PendingVariant
new file mode 100644
index 00000000..42228800
--- /dev/null
+++ b/TelepathyQt/PendingVariant
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_PendingVariant_HEADER_GUARD_
+#define _TelepathyQt_PendingVariant_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/pending-variant.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/PendingVariantMap b/TelepathyQt/PendingVariantMap
new file mode 100644
index 00000000..32dae3ce
--- /dev/null
+++ b/TelepathyQt/PendingVariantMap
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_PendingVariantMap_HEADER_GUARD_
+#define _TelepathyQt_PendingVariantMap_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/pending-variant-map.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/PendingVoid b/TelepathyQt/PendingVoid
new file mode 100644
index 00000000..56d82372
--- /dev/null
+++ b/TelepathyQt/PendingVoid
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_PendingVoid_HEADER_GUARD_
+#define _TelepathyQt_PendingVoid_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/simple-pending-operations.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/Presence b/TelepathyQt/Presence
new file mode 100644
index 00000000..74638b30
--- /dev/null
+++ b/TelepathyQt/Presence
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_Presence_HEADER_GUARD_
+#define _TelepathyQt_Presence_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/presence.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/PresenceSpec b/TelepathyQt/PresenceSpec
new file mode 100644
index 00000000..b8ec4448
--- /dev/null
+++ b/TelepathyQt/PresenceSpec
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_PresenceSpec_HEADER_GUARD_
+#define _TelepathyQt_PresenceSpec_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/presence.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/PresenceSpecList b/TelepathyQt/PresenceSpecList
new file mode 100644
index 00000000..71600307
--- /dev/null
+++ b/TelepathyQt/PresenceSpecList
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_PresenceSpecList_HEADER_GUARD_
+#define _TelepathyQt_PresenceSpecList_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/presence.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/Profile b/TelepathyQt/Profile
new file mode 100644
index 00000000..536856bf
--- /dev/null
+++ b/TelepathyQt/Profile
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_Profile_HEADER_GUARD_
+#define _TelepathyQt_Profile_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/profile.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ProfileManager b/TelepathyQt/ProfileManager
new file mode 100644
index 00000000..711d5dc0
--- /dev/null
+++ b/TelepathyQt/ProfileManager
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ProfileManager_HEADER_GUARD_
+#define _TelepathyQt_ProfileManager_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/profile-manager.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/Properties b/TelepathyQt/Properties
new file mode 100644
index 00000000..111f1ad1
--- /dev/null
+++ b/TelepathyQt/Properties
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_Properties_HEADER_GUARD_
+#define _TelepathyQt_Properties_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/properties.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/PropertiesInterface b/TelepathyQt/PropertiesInterface
new file mode 100644
index 00000000..a34d9109
--- /dev/null
+++ b/TelepathyQt/PropertiesInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_PropertiesInterface_HEADER_GUARD_
+#define _TelepathyQt_PropertiesInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/dbus.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/PropertiesInterfaceInterface b/TelepathyQt/PropertiesInterfaceInterface
new file mode 100644
index 00000000..4b11dc0c
--- /dev/null
+++ b/TelepathyQt/PropertiesInterfaceInterface
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_PropertiesInterfaceInterface_HEADER_GUARD_
+#define _TelepathyQt_PropertiesInterfaceInterface_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/properties.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ProtocolInfo b/TelepathyQt/ProtocolInfo
new file mode 100644
index 00000000..02398b47
--- /dev/null
+++ b/TelepathyQt/ProtocolInfo
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ProtocolInfo_HEADER_GUARD_
+#define _TelepathyQt_ProtocolInfo_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/protocol-info.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ProtocolParameter b/TelepathyQt/ProtocolParameter
new file mode 100644
index 00000000..93db0dc4
--- /dev/null
+++ b/TelepathyQt/ProtocolParameter
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ProtocolParameter_HEADER_GUARD_
+#define _TelepathyQt_ProtocolParameter_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/protocol-parameter.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ReadinessHelper b/TelepathyQt/ReadinessHelper
new file mode 100644
index 00000000..7e3339bd
--- /dev/null
+++ b/TelepathyQt/ReadinessHelper
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ReadinessHelper_HEADER_GUARD_
+#define _TelepathyQt_ReadinessHelper_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/readiness-helper.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ReadyObject b/TelepathyQt/ReadyObject
new file mode 100644
index 00000000..699a7c54
--- /dev/null
+++ b/TelepathyQt/ReadyObject
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ReadyObject_HEADER_GUARD_
+#define _TelepathyQt_ReadyObject_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/ready-object.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ReceivedMessage b/TelepathyQt/ReceivedMessage
new file mode 100644
index 00000000..ad43afed
--- /dev/null
+++ b/TelepathyQt/ReceivedMessage
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_ReceivedMessage_HEADER_GUARD_
+#define _TelepathyQt_ReceivedMessage_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/message.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/RefCounted b/TelepathyQt/RefCounted
new file mode 100644
index 00000000..879d287d
--- /dev/null
+++ b/TelepathyQt/RefCounted
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_RefCounted_HEADER_GUARD_
+#define _TelepathyQt_RefCounted_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/shared-ptr.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/ReferencedHandles b/TelepathyQt/ReferencedHandles
new file mode 100644
index 00000000..14c8a125
--- /dev/null
+++ b/TelepathyQt/ReferencedHandles
@@ -0,0 +1,12 @@
+#ifndef _TelepathyQt_ReferencedHandles_HEADER_GUARD_
+#define _TelepathyQt_ReferencedHandles_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/referenced-handles.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
diff --git a/TelepathyQt/ReferencedHandlesIterator b/TelepathyQt/ReferencedHandlesIterator
new file mode 100644
index 00000000..7c4eb994
--- /dev/null
+++ b/TelepathyQt/ReferencedHandlesIterator
@@ -0,0 +1,6 @@
+#ifndef _TelepathyQt_ReferencedHandlesIterator_HEADER_GUARD_
+#define _TelepathyQt_ReferencedHandlesIterator_HEADER_GUARD_
+
+#include <TelepathyQt/referenced-handles.h>
+
+#endif
diff --git a/TelepathyQt/RequestableChannelClassSpec b/TelepathyQt/RequestableChannelClassSpec
new file mode 100644
index 00000000..404372cd
--- /dev/null
+++ b/TelepathyQt/RequestableChannelClassSpec
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_RequestableChannelClassSpec_HEADER_GUARD_
+#define _TelepathyQt_RequestableChannelClassSpec_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/requestable-channel-class-spec.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/RequestableChannelClassSpecList b/TelepathyQt/RequestableChannelClassSpecList
new file mode 100644
index 00000000..1ab34cb8
--- /dev/null
+++ b/TelepathyQt/RequestableChannelClassSpecList
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_RequestableChannelClassSpecList_HEADER_GUARD_
+#define _TelepathyQt_RequestableChannelClassSpecList_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/requestable-channel-class-spec.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/RoomListChannel b/TelepathyQt/RoomListChannel
new file mode 100644
index 00000000..b8a66b97
--- /dev/null
+++ b/TelepathyQt/RoomListChannel
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_RoomListChannel_HEADER_GUARD_
+#define _TelepathyQt_RoomListChannel_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/room-list-channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/SharedPtr b/TelepathyQt/SharedPtr
new file mode 100644
index 00000000..9ecff90c
--- /dev/null
+++ b/TelepathyQt/SharedPtr
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_SharedPtr_HEADER_GUARD_
+#define _TelepathyQt_SharedPtr_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/shared-ptr.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/SimpleCallObserver b/TelepathyQt/SimpleCallObserver
new file mode 100644
index 00000000..e46c423c
--- /dev/null
+++ b/TelepathyQt/SimpleCallObserver
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_SimpleCallObserver_HEADER_GUARD_
+#define _TelepathyQt_SimpleCallObserver_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/simple-call-observer.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/SimpleObserver b/TelepathyQt/SimpleObserver
new file mode 100644
index 00000000..7b984380
--- /dev/null
+++ b/TelepathyQt/SimpleObserver
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_SimpleObserver_HEADER_GUARD_
+#define _TelepathyQt_SimpleObserver_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/simple-observer.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/SimpleTextObserver b/TelepathyQt/SimpleTextObserver
new file mode 100644
index 00000000..85ffc6ce
--- /dev/null
+++ b/TelepathyQt/SimpleTextObserver
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_SimpleTextObserver_HEADER_GUARD_
+#define _TelepathyQt_SimpleTextObserver_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/simple-text-observer.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/StatefulDBusProxy b/TelepathyQt/StatefulDBusProxy
new file mode 100644
index 00000000..beb6dec7
--- /dev/null
+++ b/TelepathyQt/StatefulDBusProxy
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_StatefulDBusProxy_HEADER_GUARD_
+#define _TelepathyQt_StatefulDBusProxy_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/dbus-proxy.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/StatelessDBusProxy b/TelepathyQt/StatelessDBusProxy
new file mode 100644
index 00000000..ca5bd535
--- /dev/null
+++ b/TelepathyQt/StatelessDBusProxy
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_StatelessDBusProxy_HEADER_GUARD_
+#define _TelepathyQt_StatelessDBusProxy_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/dbus-proxy.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/StreamTubeChannel b/TelepathyQt/StreamTubeChannel
new file mode 100644
index 00000000..c541efff
--- /dev/null
+++ b/TelepathyQt/StreamTubeChannel
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_StreamTubeChannel_HEADER_GUARD_
+#define _TelepathyQt_StreamTubeChannel_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/stream-tube-channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/StreamTubeClient b/TelepathyQt/StreamTubeClient
new file mode 100644
index 00000000..8e768358
--- /dev/null
+++ b/TelepathyQt/StreamTubeClient
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_StreamTubeClient_HEADER_GUARD_
+#define _TelepathyQt_StreamTubeClient_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/stream-tube-client.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/StreamTubeServer b/TelepathyQt/StreamTubeServer
new file mode 100644
index 00000000..f83908f8
--- /dev/null
+++ b/TelepathyQt/StreamTubeServer
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_StreamTubeServer_HEADER_GUARD_
+#define _TelepathyQt_StreamTubeServer_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/stream-tube-server.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/StreamedMediaChannel b/TelepathyQt/StreamedMediaChannel
new file mode 100644
index 00000000..ea74bd75
--- /dev/null
+++ b/TelepathyQt/StreamedMediaChannel
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_StreamedMediaChannel_HEADER_GUARD_
+#define _TelepathyQt_StreamedMediaChannel_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/streamed-media-channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/StreamedMediaStream b/TelepathyQt/StreamedMediaStream
new file mode 100644
index 00000000..81f1e6fc
--- /dev/null
+++ b/TelepathyQt/StreamedMediaStream
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_StreamedMediaStream_HEADER_GUARD_
+#define _TelepathyQt_StreamedMediaStream_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/streamed-media-channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/TelepathyQt-uninstalled.pc.in b/TelepathyQt/TelepathyQt-uninstalled.pc.in
new file mode 100644
index 00000000..a1d00985
--- /dev/null
+++ b/TelepathyQt/TelepathyQt-uninstalled.pc.in
@@ -0,0 +1,11 @@
+prefix=/nonexistent
+exec_prefix=/nonexistent
+abs_top_builddir=${CMAKE_BINARY_DIR}
+abs_top_srcdir=${CMAKE_SOURCE_DIR}
+
+Name: Telepathy-Qt (uninstalled copy)
+Description: Qt utility library for the Telepathy framework
+Version: ${PACKAGE_VERSION}
+Requires.private: QtCore >= 4.5, QtDBus >= 4.5, QtNetwork >= 4.5
+Libs: ${CMAKE_BINARY_DIR}/TelepathyQt/libtelepathy-qt.so
+Cflags: -I${CMAKE_SOURCE_DIR} -I${CMAKE_BINARY_DIR}
diff --git a/TelepathyQt/TelepathyQt.pc.in b/TelepathyQt/TelepathyQt.pc.in
new file mode 100644
index 00000000..a61d6a37
--- /dev/null
+++ b/TelepathyQt/TelepathyQt.pc.in
@@ -0,0 +1,11 @@
+prefix=${CMAKE_INSTALL_PREFIX}
+exec_prefix=${CMAKE_INSTALL_PREFIX}
+libdir=${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}
+includedir=${CMAKE_INSTALL_PREFIX}/${INCLUDE_INSTALL_DIR}
+
+Name: Telepathy-Qt
+Description: Qt utility library for the Telepathy framework
+Version: ${PACKAGE_VERSION}
+Requires.private: QtCore >= 4.5, QtDBus >= 4.5, QtNetwork >= 4.5
+Libs: -L${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR} -ltelepathy-qt
+Cflags: -I${CMAKE_INSTALL_PREFIX}/${INCLUDE_INSTALL_DIR}/telepathy-1.0
diff --git a/TelepathyQt/TelepathyQtFarsight-uninstalled.pc.in b/TelepathyQt/TelepathyQtFarsight-uninstalled.pc.in
new file mode 100644
index 00000000..679ded95
--- /dev/null
+++ b/TelepathyQt/TelepathyQtFarsight-uninstalled.pc.in
@@ -0,0 +1,11 @@
+prefix=/nonexistent
+exec_prefix=/nonexistent
+abs_top_builddir=${CMAKE_BINARY_DIR}
+abs_top_srcdir=${CMAKE_SOURCE_DIR}
+
+Name: Telepathy-Qt-Farsight (uninstalled copy)
+Description: Qt Telepathy Farsight utility library for the Telepathy framework
+Version: ${PACKAGE_VERSION}
+Requires.private: QtCore >= 4.5, QtDBus >= 4.5, telepathy-glib >= 0.7.28, telepathy-farsight >= 0.0.4, TelepathyQt = ${PACKAGE_VERSION}
+Libs: ${CMAKE_BINARY_DIR}/TelepathyQt/Farsight/libtelepathy-qt-farsight.so
+Cflags: -I${CMAKE_SOURCE_DIR} -I${CMAKE_BINARY_DIR}
diff --git a/TelepathyQt/TelepathyQtFarsight.pc.in b/TelepathyQt/TelepathyQtFarsight.pc.in
new file mode 100644
index 00000000..476504cd
--- /dev/null
+++ b/TelepathyQt/TelepathyQtFarsight.pc.in
@@ -0,0 +1,11 @@
+prefix=${CMAKE_INSTALL_PREFIX}
+exec_prefix=${CMAKE_INSTALL_PREFIX}
+libdir=${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}
+includedir=${CMAKE_INSTALL_PREFIX}/${INCLUDE_INSTALL_DIR}
+
+Name: Telepathy-Qt-Farsight
+Description: Qt Telepathy Farsight utility library for the Telepathy framework
+Version: ${PACKAGE_VERSION}
+Requires.private: QtCore >= 4.5, QtDBus >= 4.5, telepathy-glib >= 0.7.28, telepathy-farsight >= 0.0.4, TelepathyQt = ${PACKAGE_VERSION}
+Libs: -L${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR} -ltelepathy-qt-farsight
+Cflags: -I${CMAKE_INSTALL_PREFIX}/${INCLUDE_INSTALL_DIR}/telepathy-1.0
diff --git a/TelepathyQt/TextChannel b/TelepathyQt/TextChannel
new file mode 100644
index 00000000..de71a775
--- /dev/null
+++ b/TelepathyQt/TextChannel
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_TextChannel_HEADER_GUARD_
+#define _TelepathyQt_TextChannel_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/text-channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/TubeChannel b/TelepathyQt/TubeChannel
new file mode 100644
index 00000000..e98f80bb
--- /dev/null
+++ b/TelepathyQt/TubeChannel
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_TubeChannel_HEADER_GUARD_
+#define _TelepathyQt_TubeChannel_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/tube-channel.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/Types b/TelepathyQt/Types
new file mode 100644
index 00000000..ec49c2d6
--- /dev/null
+++ b/TelepathyQt/Types
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_Types_HEADER_GUARD_
+#define _TelepathyQt_Types_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/types.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/Utils b/TelepathyQt/Utils
new file mode 100644
index 00000000..6c3283d6
--- /dev/null
+++ b/TelepathyQt/Utils
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt_Utils_HEADER_GUARD_
+#define _TelepathyQt_Utils_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#define IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/utils.h>
+
+#undef IN_TP_QT_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt/abstract-client.cpp b/TelepathyQt/abstract-client.cpp
new file mode 100644
index 00000000..11a0d74e
--- /dev/null
+++ b/TelepathyQt/abstract-client.cpp
@@ -0,0 +1,988 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/AbstractClient>
+
+#include <QSharedData>
+#include <QString>
+
+#include <TelepathyQt/ChannelClassSpecList>
+
+namespace Tp
+{
+
+/**
+ * \class AbstractClient
+ * \ingroup clientclient
+ * \headerfile TelepathyQt/abstract-client.h <TelepathyQt/AbstractClient>
+ *
+ * \brief The AbstractClient class represents a Telepathy client.
+ *
+ * Clients are programs used to process channels, approving, handling or
+ * observing them. User interface processes are the obvious example of clients,
+ * but they can provide other functionality, such as address-book
+ * synchronization, message logging, etc.
+ *
+ * Each client is either an observer, an approver, a handler, or some
+ * combination of these.
+ *
+ * Clients can be activatable services (those with a D-Bus .service file)
+ * so that they can run in response to channel creation, or non-activatable
+ * services (those that do not register a D-Bus .service file
+ * for their well-known name, but do request it at runtime) so
+ * that they can process channels, but only if they are already
+ * running - for instance, a full-screen media center application might do this.
+ *
+ * As an optimization, service-activatable clients should install a file
+ * $XDG_DATA_DIRS/telepathy/clients/clientname.client containing a cached version
+ * of their immutable properties. The syntax of these files is documented in the <a
+ * href="http://telepathy.freedesktop.org/spec/org.freedesktop.Telepathy.Client.html">
+ * Telepathy specification</a>.
+ *
+ * Non-activatable clients may install a .client file, but there's not much
+ * point in them doing so.
+ *
+ * This is a base class and should not be used directly, use the
+ * specialized classes AbstractClientObserver, AbstractClientApprover and
+ * AbstractClientHandler instead.
+ *
+ * If the same process wants to be either a mix of observer, approver and
+ * handler, or a combination of those it can multiple inherit the specialized
+ * abstract classes.
+ *
+ * \sa AbstractClientObserver, AbstractClientApprover, AbstractClientHandler
+ */
+
+/**
+ * Construct a new AbstractClient object.
+ *
+ * Note that this is a base class and should not be used directly, use the
+ * specialized classes AbstractClientObserver, AbstractClientApprover and
+ * AbstractClientHandler instead.
+ */
+AbstractClient::AbstractClient()
+{
+}
+
+/**
+ * Class destructor.
+ */
+AbstractClient::~AbstractClient()
+{
+}
+
+struct TP_QT_NO_EXPORT AbstractClientObserver::Private
+{
+ Private(const ChannelClassList &channelFilter, bool shouldRecover)
+ : channelFilter(channelFilter),
+ shouldRecover(shouldRecover)
+ {
+ }
+
+ ChannelClassList channelFilter;
+ bool shouldRecover;
+};
+
+/**
+ * \class AbstractClientObserver
+ * \ingroup clientclient
+ * \headerfile TelepathyQt/abstract-client.h <TelepathyQt/AbstractClientObserver>
+ *
+ * \brief The AbstractClientObserver class represents a Telepathy observer.
+ *
+ * Observers are clients that monitor the creation of new channels.
+ * This functionality can be used for things like message logging.
+ *
+ * Observers should not modify the state of a channel except via user
+ * interaction.
+ *
+ * Observers must not carry out actions that exactly one process must take
+ * responsibility for (e.g. acknowledging text messages, or carrying out
+ * the actual file transfer), since arbitrarily many observers can be
+ * activated for each channel. The handler is responsible for such tasks.
+ *
+ * Handlers may, of course, delegate responsibility for these tasks to other
+ * clients (including those run as observers), but this must be done
+ * explicitly via a request from the handler to the observer.
+ *
+ * Whenever a collection of new channels is signalled, the channel dispatcher
+ * will notify all running or activatable observers whose filter indicates that
+ * they are interested in some of the channels.
+ *
+ * Observers are activated for all channels in which they have registered an
+ * interest - incoming, outgoing or automatically created - although of course
+ * the filter property can be set to filter specific channels.
+ *
+ * To become an observer one should inherit AbstractClientObserver and
+ * implement the pure virtual observeChannels() method. After that the object
+ * representing the observer must be registered using
+ * ClientRegistrar::registerClient().
+ *
+ * When new channels in which the observer has registered an interest are
+ * announced, the method observeChannels() is invoked. All observers are
+ * notified simultaneously.
+ *
+ * \section observer_usage_sec Usage
+ *
+ * \subsection observer_create_sec Implementing an observer
+ *
+ * \code
+ *
+ * class MyObserver : public AbstractClientObserver
+ * {
+ * public:
+ * MyObserver(const ChannelClassSpecList &channelFilter);
+ * ~MyObserver() { }
+ *
+ * void observeChannels(const MethodInvocationContextPtr<> &context,
+ * const AccountPtr &account,
+ * const ConnectionPtr &connection,
+ * const QList<ChannelPtr> &channels,
+ * const ChannelDispatchOperationPtr &dispatchOperation,
+ * const QList<ChannelRequestPtr> &requestsSatisfied,
+ * const AbstractClientObserver::ObserverInfo &observerInfo);
+ * };
+ *
+ * MyObserver::MyObserver(const ChannelClassSpecList &channelFilter)
+ * : AbstractClientObserver(channelFilter)
+ * {
+ * }
+ *
+ * void MyObserver::observeChannels(const MethodInvocationContextPtr<> &context,
+ * const AccountPtr &account,
+ * const ConnectionPtr &connection,
+ * const QList<ChannelPtr> &channels,
+ * const ChannelDispatchOperationPtr &dispatchOperation,
+ * const QList<ChannelRequestPtr> &requestsSatisfied,
+ * const AbstractClientObserver::ObserverInfo &observerInfo)
+ * {
+ * // do something, log messages, ...
+ *
+ * context->setFinished();
+ * }
+ *
+ * \endcode
+ *
+ * \subsection observer_register_sec Registering an observer
+ *
+ * \code
+ *
+ * ClientRegistrar registrar = ClientRegistrar::create();
+ * AbstractClientPtr observer = AbstractClientPtr::dynamicCast(
+ * SharedPtr<MyObserver>(new MyObserver(
+ * ChannelClassSpecList() << ChannelClassSpec::textChat())));
+ * registrar->registerClient(observer, "myobserver");
+ *
+ * \endcode
+ *
+ * \sa AbstractClient
+ */
+
+/**
+ * \class AbstractClientObserver::ObserverInfo
+ * \ingroup clientclient
+ * \headerfile TelepathyQt/abstract-client.h <TelepathyQt/AbstractClientObserver>
+ *
+ * \brief The AbstractClientObserver::ObserverInfo class provides a wrapper
+ * around the additional info about the channels passed to observeChannels().
+ *
+ * \sa AbstractClientObserver
+ */
+
+struct TP_QT_NO_EXPORT AbstractClientObserver::ObserverInfo::Private : public QSharedData
+{
+ Private(const QVariantMap &info)
+ : info(info) {}
+
+ QVariantMap info;
+};
+
+AbstractClientObserver::ObserverInfo::ObserverInfo(const QVariantMap &info)
+ : mPriv(new Private(info))
+{
+}
+
+AbstractClientObserver::ObserverInfo::ObserverInfo(const ObserverInfo &other)
+ : mPriv(other.mPriv)
+{
+}
+
+AbstractClientObserver::ObserverInfo::~ObserverInfo()
+{
+}
+
+AbstractClientObserver::ObserverInfo &AbstractClientObserver::ObserverInfo::operator=(
+ const ObserverInfo &other)
+{
+ if (this == &other) {
+ return *this;
+ }
+
+ mPriv = other.mPriv;
+ return *this;
+}
+
+QVariantMap AbstractClientObserver::ObserverInfo::allInfo() const
+{
+ return mPriv->info;
+}
+
+/**
+ * Construct a new AbstractClientObserver object.
+ *
+ * \param channelFilter A specification of the channels in which this observer
+ * is interested.
+ * \param shouldRecover Whether upon the startup of this observer,
+ * observeChannels() will be called for every already
+ * existing channel matching its observerChannelFilter().
+ */
+AbstractClientObserver::AbstractClientObserver(
+ const ChannelClassSpecList &channelFilter,
+ bool shouldRecover)
+ : mPriv(new Private(channelFilter.bareClasses(), shouldRecover))
+ // The channel filter is converted here to the low-level class so that any warnings are
+ // emitted immediately rather than only when the CD introspects this Client
+{
+}
+
+/**
+ * Class destructor.
+ */
+AbstractClientObserver::~AbstractClientObserver()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the property containing a specification of the channels that this
+ * channel observer is interested. The observeChannels() method should be called
+ * by the channel dispatcher whenever any of the newly created channels match
+ * this description.
+ *
+ * See <a
+ * href="http://telepathy.freedesktop.org/spec/org.freedesktop.Telepathy.Client.Observer.html#org.freedesktop.Telepathy.Client.Observer.ObserverChannelFilter">
+ * the Telepathy specification</a> for documentation about the allowed
+ * types and how to define filters.
+ *
+ * This property never changes while the observer process owns its client bus
+ * name. If an observer wants to add extra channels to its list of interests at
+ * runtime, it can register an additional client bus name using
+ * ClientRegistrar::registerClient().
+ * To remove those filters, it can release the bus name using
+ * ClientRegistrar::unregisterClient().
+ *
+ * The same principle is applied to approvers and handlers.
+ *
+ * \return A specification of the channels that this channel observer is
+ * interested as a list of ChannelClassSpec objects.
+ * \sa observeChannels()
+ */
+ChannelClassSpecList AbstractClientObserver::observerFilter() const
+{
+ return ChannelClassSpecList(mPriv->channelFilter);
+}
+
+/**
+ * Return whether upon the startup of this observer, observeChannels()
+ * will be called for every already existing channel matching its
+ * observerChannelFilter().
+ *
+ * \param \c true if this observer observerChannels() will be called for every
+ * already existing channel matching its observerChannelFilter(),
+ * \c false otherwise.
+ */
+bool AbstractClientObserver::shouldRecover() const
+{
+ return mPriv->shouldRecover;
+}
+
+/**
+ * \fn void AbstractClientObserver::observeChannels(
+ * const MethodInvocationContextPtr<> &context,
+ * const AccountPtr &account,
+ * const ConnectionPtr &connection,
+ * const QList<ChannelPtr> &channels,
+ * const ChannelDispatchOperationPtr &dispatchOperation,
+ * const QList<ChannelRequestPtr> &requestsSatisfied,
+ * const ObserverInfo &observerInfo);
+ *
+ * Called by the channel dispatcher when channels in which the observer has
+ * registered an interest are announced.
+ *
+ * If the announced channels contains channels that match the
+ * observerChannelFilter(), and some that do not, then only a subset of the
+ * channels (those that do match the filter) are passed to this method.
+ *
+ * If the channel dispatcher will split up the channels from a single
+ * announcement and dispatch them separately (for instance because no
+ * installed handler can handle all of them), it will call this method
+ * several times.
+ *
+ * The observer must not call MethodInvocationContext::setFinished() until it
+ * is ready for a handler for the channel to run (which may change the
+ * channel's state). For instance the received \a context object should be
+ * stored until this method is finished processing and then
+ * MethodInvocationContext::setFinished() or
+ * MethodInvocationContext::setFinishedWithError() should be called on the
+ * received \a context object.
+ *
+ * Specialized observers must reimplement this method.
+ *
+ * \param context A MethodInvocationContextPtr object that must be used to
+ * indicate whether this method finished processing.
+ * \param account The account with which the channels are associated.
+ * \param connection The connection with which the channels are associated.
+ * \param channels The channels to be observed.
+ * \param dispatchOperation The dispatch operation for these channels.
+ * The object will be invalid (DBusProxy::isValid()
+ * will be false) if there is no dispatch
+ * operation in place (because the channels were
+ * requested, not incoming).
+ * If the Observer calls
+ * ChannelDispatchOperation::claim() or
+ * ChannelDispatchOperation::handleWith() on this
+ * object, it must be careful to avoid deadlock, since
+ * these methods cannot return until the observer has
+ * returned from observeChannels().
+ * \param requestsSatisfied The requests satisfied by these channels.
+ * \param observerInfo Additional information about these channels.
+ */
+
+struct TP_QT_NO_EXPORT AbstractClientApprover::Private
+{
+ Private(const ChannelClassList &channelFilter)
+ : channelFilter(channelFilter)
+ {
+ }
+
+ ChannelClassList channelFilter;
+};
+
+/**
+ * \class AbstractClientApprover
+ * \ingroup clientclient
+ * \headerfile TelepathyQt/abstract-client.h <TelepathyQt/AbstractClientApprover>
+ *
+ * \brief The AbstractClientApprover class represents a Telepathy approver.
+ *
+ * Approvers are clients that notify the user that new channels have been
+ * created, and allow the user to accept or reject those channels.
+ *
+ * Approvers can also select which channel handler will be used for the channel,
+ * for instance by offering the user a list of possible handlers rather than
+ * just an accept/reject choice. However, the channel dispatcher must be able to
+ * prioritize possible handlers on its own using some reasonable heuristic,
+ * probably based on user configuration.
+ *
+ * It is possible (and useful) to have an approver and a channel handler in the
+ * same process; this is particularly useful if a channel handler wants to claim
+ * responsibility for particular channels itself.
+ *
+ * All approvers are notified simultaneously. For instance, in a desktop system,
+ * there might be one approver that displays a notification-area icon, one that
+ * is part of a contact list window and highlights contacts there, and one that
+ * is part of a full-screen media player.
+ *
+ * Any approver can approve the handling of a channel dispatch operation with a
+ * particular channel handler by calling the
+ * ChannelDispatchOperation::handleWith() method. Approvers can also attempt to
+ * claim channels by calling ChannelDispatchOperation::claim(). If this
+ * succeeds, the approver may handle the channels itself (if it is also a
+ * handler), or close the channels in order to reject them.
+ *
+ * Approvers wishing to reject channels should call the
+ * ChannelDispatchOperation::claim() method, then (if it succeeds) close the
+ * channels in any way they see fit.
+ *
+ * The first approver to reply gets its decision acted on; any other approvers
+ * that reply at approximately the same time will get an error, indicating that
+ * the channel has already been dealt with.
+ *
+ * Approvers should usually prompt the user and ask for confirmation, rather
+ * than dispatching the channel to a handler straight away.
+ *
+ * To become an approver one should inherit AbstractClientApprover and
+ * implement the pure virtual addDispatchOperation() method. After that the
+ * object representing the approver must be registered using
+ * ClientRegistrar::registerClient().
+ *
+ * When new channels in which the approver has registered an interest are
+ * ready to be dispatched, the method addDispatchOperation() is invoked.
+ * The new channels are represented by a ChannelDispatchOperation object, which
+ * is passed to the addDispatchOperation() method.
+ * All approvers are notified simultaneously.
+ *
+ * \section approver_usage_sec Usage
+ *
+ * \subsection approver_create_sec Implementing an approver
+ *
+ * \code
+ *
+ * class MyApprover : public AbstractClientApprover
+ * {
+ * public:
+ * MyApprover(const ChannelClassSpecSpecList &channelFilter);
+ * ~MyApprover() { }
+ *
+ * void addDispatchOperation(const MethodInvocationContextPtr<> &context,
+ * const ChannelDispatchOperationPtr &dispatchOperation);
+ * };
+ *
+ * MyApprover::MyApprover(const ChannelClassSpecList &channelFilter)
+ * : AbstractClientApprover(channelFilter)
+ * {
+ * }
+ *
+ * void MyApprover::addDispatchOperation(
+ * const MethodInvocationContextPtr<> &context,
+ * const ChannelDispatchOperationPtr &dispatchOperation)
+ * {
+ * // do something with dispatchOperation
+ *
+ * context->setFinished();
+ * }
+ *
+ * \endcode
+ *
+ * \subsection approver_register_sec Registering an approver
+ *
+ * \code
+ *
+ * ClientRegistrar registrar = ClientRegistrar::create();
+ * AbstractClientPtr approver = AbstractClientPtr::dynamicCast(
+ * SharedPtr<MyApprover>(new MyApprover(
+ * ChannelClassSpecList() << ChannelClassSpec::textChat())));
+ * registrar->registerClient(approver, "myapprover");
+ *
+ * \endcode
+ *
+ * \sa AbstractClient
+ */
+
+/**
+ * Construct a new AbstractClientApprover object.
+ *
+ * \param channelFilter A specification of the channels in which this approver
+ * is interested.
+ */
+AbstractClientApprover::AbstractClientApprover(
+ const ChannelClassSpecList &channelFilter)
+ : mPriv(new Private(channelFilter.bareClasses()))
+{
+}
+
+/**
+ * Class destructor.
+ */
+AbstractClientApprover::~AbstractClientApprover()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the property containing a specification of the channels that this
+ * channel approver is interested. The addDispatchOperation() method should be
+ * called by the channel dispatcher whenever at least one of the channels in
+ * a channel dispatch operation matches this description.
+ *
+ * This method works in exactly the same way as the
+ * AbstractClientObserver::observerChannelFilter() method. In particular, the
+ * returned value cannot change while the handler process continues to own the
+ * corresponding client bus name.
+ *
+ * In the .client file, represented in the same way as observer channel
+ * filter, the group is #TP_QT_IFACE_CLIENT_APPROVER followed by
+ * ApproverChannelFilter instead.
+ *
+ * \return A specification of the channels that this channel approver is
+ * interested as a list of ChannelClassSpec objects.
+ * \sa addDispatchOperation()
+ */
+ChannelClassSpecList AbstractClientApprover::approverFilter() const
+{
+ return ChannelClassSpecList(mPriv->channelFilter);
+}
+
+/**
+ * \fn void AbstractClientApprover::addDispatchOperation(
+ * const MethodInvocationContextPtr<> &context,
+ * const ChannelDispatchOperationPtr &dispatchOperation);
+ *
+ * Called by the channel dispatcher when a dispatch operation in which the
+ * approver has registered an interest is created, or when the approver starts
+ * up while such channel dispatch operations already exist.
+ *
+ * The received \a context object should be stored until this
+ * method is finished processing and then MethodInvocationContext::setFinished()
+ * or MethodInvocationContext::setFinishedWithError() should be called on the
+ * received \a context object.
+ *
+ * Specialized approvers must reimplement this method.
+ *
+ * \param context A MethodInvocationContextPtr object that must be used to
+ * indicate whether this method finished processing.
+ * \param dispatchOperation The dispatch operation to be processed.
+ */
+
+struct TP_QT_NO_EXPORT AbstractClientHandler::Private
+{
+ Private(const ChannelClassList &channelFilter,
+ const Capabilities &capabilities,
+ bool wantsRequestNotification)
+ : channelFilter(channelFilter),
+ capabilities(capabilities),
+ wantsRequestNotification(wantsRequestNotification),
+ registered(false)
+ {
+ }
+
+ ChannelClassList channelFilter;
+ Capabilities capabilities;
+ bool wantsRequestNotification;
+ bool registered;
+};
+
+/**
+ * \class AbstractClientHandler
+ * \ingroup clientclient
+ * \headerfile TelepathyQt/abstract-client.h <TelepathyQt/AbstractClientHandler>
+ *
+ * \brief The AbstractClientHandler class represents a Telepathy handler.
+ *
+ * Handlers are the user interface for a channel. They turn an abstract
+ * channel into something the user wants to see, like a text message
+ * stream or an audio and/or video call.
+ *
+ * For its entire lifetime, each channel on a connection known to the channel
+ * dispatcher is either being processed by the channel dispatcher, or being
+ * handled by precisely one handler.
+ *
+ * Because each channel is only handled by one handler, handlers may perform
+ * actions that only make sense to do once, such as acknowledging text messages,
+ * transferring the file, etc.
+ *
+ * When a new incoming channel is offered to approvers by the channel
+ * dispatcher, it also offers the approvers a list of all the running or
+ * activatable handlers whose filter indicates that they are able to handle
+ * the channel. The approvers can choose one of those channel handlers to
+ * handle the channel.
+ *
+ * When a new outgoing channel appears, the channel dispatcher passes it to
+ * an appropriate channel handler automatically.
+ *
+ * To become an handler one should inherit AbstractClientHandler and
+ * implement the pure virtual bypassApproval() and handleChannels() methods.
+ * After that the object representing the handler must be registered using
+ * ClientRegistrar::registerClient().
+ *
+ * When new channels in which the approver has registered an interest are
+ * ready to be handled, the method handleChannels() is invoked.
+ *
+ * \section handler_usage_sec Usage
+ *
+ * \subsection handler_create_sec Implementing a handler
+ *
+ * \code
+ *
+ * class MyHandler : public AbstractClientHandler
+ * {
+ * public:
+ * MyHandler(const ChannelClassSpecList &channelFilter);
+ * ~MyHandler() { }
+ *
+ * void bypassApproval() const;
+ *
+ * void handleChannels(const MethodInvocationContextPtr<> &context,
+ * const AccountPtr &account,
+ * const ConnectionPtr &connection,
+ * const QList<ChannelPtr> &channels,
+ * const QList<ChannelRequestPtr> &requestsSatisfied,
+ * const QDateTime &userActionTime,
+ * const AbstractClientHandler::HandlerInfo &handlerInfo);
+ * };
+ *
+ * MyHandler::MyHandler(const ChannelClassSpecList &channelFilter)
+ * : AbstractClientHandler(channelFilter)
+ * {
+ * }
+ *
+ * void MyHandler::bypassApproval() const
+ * {
+ * return false;
+ * }
+ *
+ * void MyHandler::handleChannels(const MethodInvocationContextPtr<> &context,
+ * const AccountPtr &account,
+ * const ConnectionPtr &connection,
+ * const QList<ChannelPtr> &channels,
+ * const QList<ChannelRequestPtr> &requestsSatisfied,
+ * const QDateTime &userActionTime,
+ * const AbstractClientHandler::HandlerInfo &handlerInfo)
+ * {
+ * // do something
+ *
+ * context->setFinished();
+ * }
+ *
+ * \endcode
+ *
+ * \subsection handler_register_sec Registering a handler
+ *
+ * \code
+ *
+ * ClientRegistrar registrar = ClientRegistrar::create();
+ * AbstractClientPtr handler = AbstractClientPtr::dynamicCast(
+ * SharedPtr<MyHandler>(new MyHandler(
+ * ChannelClassSpecList() << ChannelClassSpec::textChat())));
+ * registrar->registerClient(handler, "myhandler");
+ *
+ * \endcode
+ *
+ * \sa AbstractClient
+ */
+
+/**
+ * \class AbstractClientHandler::Capabilities
+ * \ingroup clientclient
+ * \headerfile TelepathyQt/abstract-client.h <TelepathyQt/AbstractClientHandler>
+ *
+ * \brief The AbstractClientHandler::Capabilities class provides a wrapper
+ * around the capabilities of a handler.
+ *
+ * \sa AbstractClientHandler
+ */
+
+/**
+ * \class AbstractClientHandler::HandlerInfo
+ * \ingroup clientclient
+ * \headerfile TelepathyQt/abstract-client.h <TelepathyQt/AbstractClientHandler>
+ *
+ * \brief The AbstractClientHandler::HandlerInfo class provides a wrapper
+ * around the additional info about the channels passed to handleChannels().
+ *
+ * \sa AbstractClientHandler
+ */
+
+struct TP_QT_NO_EXPORT AbstractClientHandler::Capabilities::Private : public QSharedData
+{
+ Private(const QStringList &tokens)
+ : tokens(QSet<QString>::fromList(tokens)) {}
+
+ QSet<QString> tokens;
+};
+
+AbstractClientHandler::Capabilities::Capabilities(const QStringList &tokens)
+ : mPriv(new Private(tokens))
+{
+}
+
+AbstractClientHandler::Capabilities::Capabilities(const Capabilities &other)
+ : mPriv(other.mPriv)
+{
+}
+
+AbstractClientHandler::Capabilities::~Capabilities()
+{
+}
+
+AbstractClientHandler::Capabilities &AbstractClientHandler::Capabilities::operator=(
+ const Capabilities &other)
+{
+ if (this == &other) {
+ return *this;
+ }
+
+ mPriv = other.mPriv;
+ return *this;
+}
+
+bool AbstractClientHandler::Capabilities::hasToken(const QString &token) const
+{
+ return mPriv->tokens.contains(token);
+}
+
+void AbstractClientHandler::Capabilities::setToken(const QString &token)
+{
+ mPriv->tokens.insert(token);
+}
+
+void AbstractClientHandler::Capabilities::unsetToken(const QString &token)
+{
+ mPriv->tokens.remove(token);
+}
+
+QStringList AbstractClientHandler::Capabilities::allTokens() const
+{
+ return mPriv->tokens.toList();
+}
+
+struct TP_QT_NO_EXPORT AbstractClientHandler::HandlerInfo::Private : public QSharedData
+{
+ Private(const QVariantMap &info)
+ : info(info) {}
+
+ QVariantMap info;
+};
+
+AbstractClientHandler::HandlerInfo::HandlerInfo(const QVariantMap &info)
+ : mPriv(new Private(info))
+{
+}
+
+AbstractClientHandler::HandlerInfo::HandlerInfo(const HandlerInfo &other)
+ : mPriv(other.mPriv)
+{
+}
+
+AbstractClientHandler::HandlerInfo::~HandlerInfo()
+{
+}
+
+AbstractClientHandler::HandlerInfo &AbstractClientHandler::HandlerInfo::operator=(
+ const HandlerInfo &other)
+{
+ if (this == &other) {
+ return *this;
+ }
+
+ mPriv = other.mPriv;
+ return *this;
+}
+
+QVariantMap AbstractClientHandler::HandlerInfo::allInfo() const
+{
+ return mPriv->info;
+}
+
+/**
+ * Construct a new AbstractClientHandler object.
+ *
+ * \param channelFilter A specification of the channels in which this observer
+ * is interested.
+ * \param wantsRequestNotification Whether this handler wants to receive channel
+ * requests notification via addRequest() and
+ * removeRequest().
+ * \param capabilities The set of additional capabilities supported by this
+ * handler.
+ */
+AbstractClientHandler::AbstractClientHandler(const ChannelClassSpecList &channelFilter,
+ const Capabilities &capabilities,
+ bool wantsRequestNotification)
+ : mPriv(new Private(channelFilter.bareClasses(), capabilities, wantsRequestNotification))
+{
+}
+
+/**
+ * Class destructor.
+ */
+AbstractClientHandler::~AbstractClientHandler()
+{
+ delete mPriv;
+}
+
+/**
+ * Return whether this handler is registered.
+ *
+ * \return \c true if registered, \c false otherwise.
+ */
+bool AbstractClientHandler::isRegistered() const
+{
+ return mPriv->registered;
+}
+
+/**
+ * Return the property containing a specification of the channels that this
+ * channel handler can deal with. It will be offered to approvers as a potential
+ * channel handler for bundles that contain only suitable channels, or for
+ * suitable channels that must be handled separately.
+ *
+ * This method works in exactly the same way as the
+ * AbstractClientObserver::observerChannelFilter() method. In particular, the
+ * returned value cannot change while the handler process continues to own the
+ * corresponding client bus name.
+ *
+ * In the .client file, represented in the same way as observer channel
+ * filter, the group is #TP_QT_IFACE_CLIENT_HANDLER suffixed
+ * by HandlerChannelFilter instead.
+ *
+ * \return A specification of the channels that this channel handler can deal
+ * with as a list of ChannelClassSpec objects.
+ */
+ChannelClassSpecList AbstractClientHandler::handlerFilter() const
+{
+ return ChannelClassSpecList(mPriv->channelFilter);
+}
+
+/**
+ * Return the set of additional capabilities supported by this handler.
+ *
+ * \return The capabilities as an AbstractClientHandler::Capabilities object.
+ */
+AbstractClientHandler::Capabilities AbstractClientHandler::handlerCapabilities() const
+{
+ return mPriv->capabilities;
+}
+
+/**
+ * \fn bool AbstractClientHandler::bypassApproval() const;
+ *
+ * Return whether channels destined for this handler are automatically
+ * handled, without invoking approvers.
+ *
+ * \return \c true if automatically handled, \c false otherwise.
+ */
+
+/**
+ * \fn void AbstractClientHandler::handleChannels(
+ * const MethodInvocationContextPtr<> &context,
+ * const AccountPtr &account,
+ * const ConnectionPtr &connection,
+ * const QList<ChannelPtr> &channels,
+ * const QList<ChannelRequestPtr> &requestsSatisfied,
+ * const QDateTime &userActionTime,
+ * const HandlerInfo &handlerInfo);
+ *
+ * Called by the channel dispatcher when this handler should handle these
+ * channels, or when this handler should present channels that it is already
+ * handling to the user (e.g. bring them into the foreground).
+ *
+ * Clients are expected to know what channels they're already handling, and
+ * which channel object corresponds to which window or tab.
+ *
+ * After handleChannels() replies successfully by calling
+ * MethodInvocationContext::setFinished(), the client process is considered
+ * to be responsible for the channel until it its unique name disappears from
+ * the bus.
+ *
+ * If a process has multiple client bus names - some temporary and some
+ * long-lived - and drops one of the temporary bus names in order to reduce the
+ * set of channels that it will handle, any channels that it is already handling
+ * will remain unaffected.
+ *
+ * The received \a context object should be stored until this
+ * method is finished processing and then MethodInvocationContext::setFinished()
+ * or MethodInvocationContext::setFinishedWithError() should be called on the
+ * received \a context object.
+ *
+ * Specialized handlers must reimplement this method.
+ *
+ * \param context A MethodInvocationContextPtr object that must be used to
+ * indicate whether this method finished processing.
+ * \param account The account with which the channels are associated.
+ * \param connection The connection with which the channels are associated.
+ * \param channels The channels to be handled.
+ * \param dispatchOperation The dispatch operation for these channels.
+ * The object will be invalid (DBusProxy::isValid()
+ * will be false) if there is no dispatch
+ * operation in place (because the channels were
+ * requested, not incoming).
+ * \param requestsSatisfied The requests satisfied by these channels.
+ * \param userActionTime The time at which user action occurred, or 0 if this
+ * channel is to be handled for some reason not involving
+ * user action. Handlers should use this for
+ * focus-stealing prevention, if applicable.
+ * \param handlerInfo Additional information about these channels.
+ */
+
+/**
+ * Return whether this handler wants to receive notification of channel requests
+ * via addRequest() and removeRequest().
+ *
+ * This property is set by the constructor and cannot be changed after that.
+ *
+ * \return \c true if receiving channel requests notification is desired,
+ * \c false otherwise.
+ */
+bool AbstractClientHandler::wantsRequestNotification() const
+{
+ return mPriv->wantsRequestNotification;
+}
+
+/**
+ * Called by the channel dispatcher to indicate that channels have been
+ * requested, and that if the request is successful, they will probably be
+ * handled by this handler.
+ *
+ * This allows the UI to start preparing to handle the channels in advance
+ * (e.g. render a window with an "in progress" message), improving perceived
+ * responsiveness.
+ *
+ * If the request succeeds and is given to the expected handler, the
+ * requestsSatisfied parameter to handleChannels() can be used to match the
+ * channel to a previous addRequest() call.
+ *
+ * This lets the UI direct the channels to the window that it already opened.
+ *
+ * If the request fails, the expected handler is notified by the channel
+ * dispatcher calling its removeRequest() method.
+ *
+ * This lets the UI close the window or display the error.
+ *
+ * The channel dispatcher will attempt to ensure that handleChannels() is called
+ * on the same handler that received addRequest(). If that isn't possible,
+ * removeRequest() will be called on the handler that previously received
+ * addRequest(), with the special error #TP_QT_ERROR_NOT_YOURS, which
+ * indicates that some other handler received the channel instead.
+ *
+ * Expected handling is for the UI to close the window it previously opened.
+ *
+ * Specialized handlers that want to be notified of newly requested channel
+ * should reimplement this method.
+ *
+ * \param channelRequest The newly created channel request.
+ * \sa removeRequest()
+ */
+void AbstractClientHandler::addRequest(
+ const ChannelRequestPtr &channelRequest)
+{
+ // do nothing, subclasses that want to listen requests should reimplement
+ // this method
+}
+
+/**
+ * Called by the ChannelDispatcher to indicate that a request previously passed
+ * to addRequest() has failed and should be disregarded.
+ *
+ * Specialized handlers that want to be notified of removed channel requests
+ * should reimplement this method.
+ *
+ * \param channelRequest The channel request that failed.
+ * \param errorName The name of the D-Bus error with which the request failed.
+ * If this is #TP_QT_ERROR_NOT_YOURS, this indicates that
+ * the request succeeded, but all the resulting channels were
+ * given to some other handler.
+ * \param errorMessage Any message supplied with the D-Bus error.
+ */
+void AbstractClientHandler::removeRequest(
+ const ChannelRequestPtr &channelRequest,
+ const QString &errorName, const QString &errorMessage)
+{
+ // do nothing, subclasses that want to listen requests should reimplement
+ // this method
+}
+
+void AbstractClientHandler::setRegistered(bool registered)
+{
+ mPriv->registered = registered;
+}
+
+} // Tp
diff --git a/TelepathyQt/abstract-client.h b/TelepathyQt/abstract-client.h
new file mode 100644
index 00000000..aef615f6
--- /dev/null
+++ b/TelepathyQt/abstract-client.h
@@ -0,0 +1,323 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_abstract_client_h_HEADER_GUARD_
+#define _TelepathyQt_abstract_client_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/SharedPtr>
+#include <TelepathyQt/Types>
+
+#include <QList>
+#include <QObject>
+#include <QSharedDataPointer>
+#include <QString>
+#include <QVariantMap>
+
+namespace Tp
+{
+
+class ClientRegistrar;
+class ChannelClassSpecList;
+
+class TP_QT_EXPORT AbstractClient : public RefCounted
+{
+ Q_DISABLE_COPY(AbstractClient)
+
+public:
+ AbstractClient();
+ virtual ~AbstractClient();
+};
+
+class TP_QT_EXPORT AbstractClientObserver : public virtual AbstractClient
+{
+ Q_DISABLE_COPY(AbstractClientObserver)
+
+public:
+ class ObserverInfo
+ {
+ public:
+ ObserverInfo(const QVariantMap &info = QVariantMap());
+ ObserverInfo(const ObserverInfo &other);
+ ~ObserverInfo();
+
+ ObserverInfo &operator=(const ObserverInfo &other);
+
+ bool isRecovering() const { return qdbus_cast<bool>(allInfo().value(QLatin1String("recovering"))); }
+
+ QVariantMap allInfo() const;
+
+ private:
+ struct Private;
+ QSharedDataPointer<Private> mPriv;
+ };
+
+ virtual ~AbstractClientObserver();
+
+ ChannelClassSpecList observerFilter() const;
+
+ bool shouldRecover() const;
+
+ virtual void observeChannels(const MethodInvocationContextPtr<> &context,
+ const AccountPtr &account,
+ const ConnectionPtr &connection,
+ const QList<ChannelPtr> &channels,
+ const ChannelDispatchOperationPtr &dispatchOperation,
+ const QList<ChannelRequestPtr> &requestsSatisfied,
+ const ObserverInfo &observerInfo) = 0;
+
+protected:
+ AbstractClientObserver(const ChannelClassSpecList &channelFilter, bool shouldRecover = false);
+
+private:
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+class TP_QT_EXPORT AbstractClientApprover : public virtual AbstractClient
+{
+ Q_DISABLE_COPY(AbstractClientApprover)
+
+public:
+ virtual ~AbstractClientApprover();
+
+ ChannelClassSpecList approverFilter() const;
+
+ virtual void addDispatchOperation(const MethodInvocationContextPtr<> &context,
+ const ChannelDispatchOperationPtr &dispatchOperation) = 0;
+
+protected:
+ AbstractClientApprover(const ChannelClassSpecList &channelFilter);
+
+private:
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+/*
+ * TODO: use case specific subclasses:
+ * - StreamTubeHandler(QString(List) protocol(s))
+ * - handleTube(DBusTubeChannelPtr, userActionTime)
+ * - DBusTubeHandler(QString(List) serviceName(s))
+ * - handleTube(DBusTubeChannelPtr, userActionTime)
+ */
+class TP_QT_EXPORT AbstractClientHandler : public virtual AbstractClient
+{
+ Q_DISABLE_COPY(AbstractClientHandler)
+
+public:
+ class Capabilities
+ {
+ public:
+ Capabilities(const QStringList &tokens = QStringList());
+ Capabilities(const Capabilities &other);
+ ~Capabilities();
+
+ Capabilities &operator=(const Capabilities &other);
+
+ bool hasGTalkP2PNATTraversalToken() const
+ {
+ return hasToken(TP_QT_IFACE_CHANNEL_INTERFACE_MEDIA_SIGNALLING +
+ QLatin1String("/gtalk-p2p"));
+ }
+
+ void setGTalkP2PNATTraversalToken()
+ {
+ setToken(TP_QT_IFACE_CHANNEL_INTERFACE_MEDIA_SIGNALLING +
+ QLatin1String("/gtalk-p2p"));
+ }
+
+ void unsetGTalkP2PNATTraversalToken()
+ {
+ unsetToken(TP_QT_IFACE_CHANNEL_INTERFACE_MEDIA_SIGNALLING +
+ QLatin1String("/gtalk-p2p"));
+ }
+
+ bool hasICEUDPNATTraversalToken() const
+ {
+ return hasToken(TP_QT_IFACE_CHANNEL_INTERFACE_MEDIA_SIGNALLING +
+ QLatin1String("/ice-udp"));
+ }
+
+ void setICEUDPNATTraversalToken()
+ {
+ setToken(TP_QT_IFACE_CHANNEL_INTERFACE_MEDIA_SIGNALLING +
+ QLatin1String("/ice-udp"));
+ }
+
+ void unsetICEUDPNATTraversalToken()
+ {
+ unsetToken(TP_QT_IFACE_CHANNEL_INTERFACE_MEDIA_SIGNALLING +
+ QLatin1String("/ice-udp"));
+ }
+
+ bool hasWLM85NATTraversalToken() const
+ {
+ return hasToken(TP_QT_IFACE_CHANNEL_INTERFACE_MEDIA_SIGNALLING +
+ QLatin1String("/wlm-8.5"));
+ }
+
+ void setWLM85NATTraversalToken()
+ {
+ setToken(TP_QT_IFACE_CHANNEL_INTERFACE_MEDIA_SIGNALLING +
+ QLatin1String("/wlm-8.5"));
+ }
+
+ void unsetWLM85NATTraversalToken()
+ {
+ unsetToken(TP_QT_IFACE_CHANNEL_INTERFACE_MEDIA_SIGNALLING +
+ QLatin1String("/wlm-8.5"));
+ }
+
+ bool hasWLM2009NATTraversalToken() const
+ {
+ return hasToken(TP_QT_IFACE_CHANNEL_INTERFACE_MEDIA_SIGNALLING +
+ QLatin1String("/wlm-2009"));
+ }
+
+ void setWLM2009NATTraversalToken()
+ {
+ setToken(TP_QT_IFACE_CHANNEL_INTERFACE_MEDIA_SIGNALLING +
+ QLatin1String("/wlm-2009"));
+ }
+
+ void unsetWLM2009NATTraversalToken()
+ {
+ unsetToken(TP_QT_IFACE_CHANNEL_INTERFACE_MEDIA_SIGNALLING +
+ QLatin1String("/wlm-2009"));
+ }
+
+ bool hasAudioCodecToken(const QString &mimeSubType) const
+ {
+ return hasToken(TP_QT_IFACE_CHANNEL_INTERFACE_MEDIA_SIGNALLING +
+ QLatin1String("/audio/") + mimeSubType.toLower());
+ }
+
+ void setAudioCodecToken(const QString &mimeSubType)
+ {
+ setToken(TP_QT_IFACE_CHANNEL_INTERFACE_MEDIA_SIGNALLING +
+ QLatin1String("/audio/") + mimeSubType.toLower());
+ }
+
+ void unsetAudioCodecToken(const QString &mimeSubType)
+ {
+ unsetToken(TP_QT_IFACE_CHANNEL_INTERFACE_MEDIA_SIGNALLING +
+ QLatin1String("/audio/") + mimeSubType.toLower());
+ }
+
+ bool hasVideoCodecToken(const QString &mimeSubType) const
+ {
+ return hasToken(TP_QT_IFACE_CHANNEL_INTERFACE_MEDIA_SIGNALLING +
+ QLatin1String("/video/") + mimeSubType.toLower());
+ }
+
+ void setVideoCodecToken(const QString &mimeSubType)
+ {
+ setToken(TP_QT_IFACE_CHANNEL_INTERFACE_MEDIA_SIGNALLING +
+ QLatin1String("/video/") + mimeSubType.toLower());
+ }
+
+ void unsetVideoCodecToken(const QString &mimeSubType)
+ {
+ unsetToken(TP_QT_IFACE_CHANNEL_INTERFACE_MEDIA_SIGNALLING +
+ QLatin1String("/video/") + mimeSubType.toLower());
+ }
+
+ bool hasToken(const QString &token) const;
+ void setToken(const QString &token);
+ void unsetToken(const QString &token);
+
+ QStringList allTokens() const;
+
+ private:
+ struct Private;
+ QSharedDataPointer<Private> mPriv;
+ };
+
+ class HandlerInfo
+ {
+ public:
+ HandlerInfo(const QVariantMap &info = QVariantMap());
+ HandlerInfo(const HandlerInfo &other);
+ ~HandlerInfo();
+
+ HandlerInfo &operator=(const HandlerInfo &other);
+
+ QVariantMap allInfo() const;
+
+ private:
+ struct Private;
+ QSharedDataPointer<Private> mPriv;
+ };
+
+ virtual ~AbstractClientHandler();
+
+ // FIXME (API/ABI break) Move isRegistered/setRegistered to AbstractClient
+ bool isRegistered() const;
+
+ ChannelClassSpecList handlerFilter() const;
+
+ Capabilities handlerCapabilities() const;
+
+ virtual bool bypassApproval() const = 0;
+
+ virtual void handleChannels(const MethodInvocationContextPtr<> &context,
+ const AccountPtr &account,
+ const ConnectionPtr &connection,
+ const QList<ChannelPtr> &channels,
+ const QList<ChannelRequestPtr> &requestsSatisfied,
+ const QDateTime &userActionTime,
+ const HandlerInfo &handlerInfo) = 0;
+
+ bool wantsRequestNotification() const;
+ virtual void addRequest(const ChannelRequestPtr &request);
+ virtual void removeRequest(const ChannelRequestPtr &request,
+ const QString &errorName, const QString &errorMessage);
+
+protected:
+ AbstractClientHandler(const ChannelClassSpecList &channelFilter,
+ const Capabilities &capabilities = Capabilities(),
+ bool wantsRequestNotification = false);
+
+private:
+ friend class ClientRegistrar;
+
+ void setRegistered(bool registered);
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+Q_DECLARE_METATYPE(Tp::AbstractClientObserver::ObserverInfo);
+Q_DECLARE_METATYPE(Tp::AbstractClientHandler::Capabilities);
+Q_DECLARE_METATYPE(Tp::AbstractClientHandler::HandlerInfo);
+
+#endif
diff --git a/TelepathyQt/abstract-interface.cpp b/TelepathyQt/abstract-interface.cpp
new file mode 100644
index 00000000..bdaf677b
--- /dev/null
+++ b/TelepathyQt/abstract-interface.cpp
@@ -0,0 +1,136 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/AbstractInterface>
+
+#include "TelepathyQt/_gen/abstract-interface.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/DBusProxy>
+#include <TelepathyQt/PendingVariant>
+#include <TelepathyQt/PendingVariantMap>
+#include <TelepathyQt/PendingVoid>
+#include <TelepathyQt/Types>
+
+#include <QDBusPendingCall>
+#include <QDBusVariant>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT AbstractInterface::Private
+{
+ QString mError;
+ QString mMessage;
+};
+
+/**
+ * \class AbstractInterface
+ * \ingroup clientsideproxies
+ * \headerfile TelepathyQt/abstract-interface.h <TelepathyQt/AbstractInterface>
+ *
+ * \brief The AbstractInterface class is the base class for all client side
+ * D-Bus interfaces, allowing access to remote methods/properties/signals.
+ */
+
+AbstractInterface::AbstractInterface(const QString &busName,
+ const QString &path, const QLatin1String &interface,
+ const QDBusConnection &dbusConnection, QObject *parent)
+ : QDBusAbstractInterface(busName, path, interface.latin1(), dbusConnection, parent),
+ mPriv(new Private)
+{
+}
+
+AbstractInterface::AbstractInterface(DBusProxy *parent, const QLatin1String &interface)
+ : QDBusAbstractInterface(parent->busName(), parent->objectPath(),
+ interface.latin1(), parent->dbusConnection(), parent),
+ mPriv(new Private)
+{
+ connect(parent, SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
+ this, SLOT(invalidate(Tp::DBusProxy*,QString,QString)));
+}
+
+AbstractInterface::~AbstractInterface()
+{
+ delete mPriv;
+}
+
+bool AbstractInterface::isValid() const
+{
+ return QDBusAbstractInterface::isValid() && mPriv->mError.isEmpty();
+}
+
+QString AbstractInterface::invalidationReason() const
+{
+ return mPriv->mError;
+}
+
+QString AbstractInterface::invalidationMessage() const
+{
+ return mPriv->mMessage;
+}
+
+void AbstractInterface::invalidate(DBusProxy *proxy,
+ const QString &error, const QString &message)
+{
+ Q_ASSERT(!error.isEmpty());
+
+ if (mPriv->mError.isEmpty()) {
+ mPriv->mError = error;
+ mPriv->mMessage = message;
+ }
+}
+
+PendingVariant *AbstractInterface::internalRequestProperty(const QString &name) const
+{
+ QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(),
+ TP_QT_IFACE_PROPERTIES, QLatin1String("Get"));
+ msg << interface() << name;
+ QDBusPendingCall pendingCall = connection().asyncCall(msg);
+ DBusProxy *proxy = qobject_cast<DBusProxy*>(parent());
+ return new PendingVariant(pendingCall, DBusProxyPtr(proxy));
+}
+
+PendingOperation *AbstractInterface::internalSetProperty(const QString &name,
+ const QVariant &newValue)
+{
+ QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(),
+ TP_QT_IFACE_PROPERTIES, QLatin1String("Set"));
+ msg << interface() << name << QVariant::fromValue(QDBusVariant(newValue));
+ QDBusPendingCall pendingCall = connection().asyncCall(msg);
+ DBusProxy *proxy = qobject_cast<DBusProxy*>(parent());
+ return new PendingVoid(pendingCall, DBusProxyPtr(proxy));
+}
+
+PendingVariantMap *AbstractInterface::internalRequestAllProperties() const
+{
+ QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(),
+ TP_QT_IFACE_PROPERTIES, QLatin1String("GetAll"));
+ msg << interface();
+ QDBusPendingCall pendingCall = connection().asyncCall(msg);
+ DBusProxy *proxy = qobject_cast<DBusProxy*>(parent());
+ return new PendingVariantMap(pendingCall, DBusProxyPtr(proxy));
+}
+
+} // Tp
diff --git a/TelepathyQt/abstract-interface.h b/TelepathyQt/abstract-interface.h
new file mode 100644
index 00000000..53205e07
--- /dev/null
+++ b/TelepathyQt/abstract-interface.h
@@ -0,0 +1,76 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_abstract_interface_h_HEADER_GUARD_
+#define _TelepathyQt_abstract_interface_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Global>
+
+#include <QDBusAbstractInterface>
+
+namespace Tp
+{
+
+class DBusProxy;
+class PendingVariant;
+class PendingOperation;
+class PendingVariantMap;
+
+class TP_QT_EXPORT AbstractInterface : public QDBusAbstractInterface
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(AbstractInterface)
+
+public:
+ virtual ~AbstractInterface();
+
+ bool isValid() const;
+ QString invalidationReason() const;
+ QString invalidationMessage() const;
+
+protected Q_SLOTS:
+ virtual void invalidate(Tp::DBusProxy *proxy,
+ const QString &error, const QString &message);
+
+protected:
+ AbstractInterface(DBusProxy *proxy, const QLatin1String &interface);
+ AbstractInterface(const QString &busName, const QString &path,
+ const QLatin1String &interface, const QDBusConnection &connection,
+ QObject *parent);
+
+ PendingVariant *internalRequestProperty(const QString &name) const;
+ PendingOperation *internalSetProperty(const QString &name, const QVariant &newValue);
+ PendingVariantMap *internalRequestAllProperties() const;
+
+private:
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/account-capability-filter.dox b/TelepathyQt/account-capability-filter.dox
new file mode 100644
index 00000000..7148993e
--- /dev/null
+++ b/TelepathyQt/account-capability-filter.dox
@@ -0,0 +1,30 @@
+/*
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * \class Tp::AccountCapabilityFilter
+ * \ingroup utils
+ * \headerfile TelepathyQt/account-capability-filter.h <TelepathyQt/AccountCapabilityFilter>
+ *
+ * \brief The AccountCapabilityFilter class provides a filter object to be used
+ * to filter accounts by capabilities.
+ */
diff --git a/TelepathyQt/account-capability-filter.h b/TelepathyQt/account-capability-filter.h
new file mode 100644
index 00000000..b5d3cdaa
--- /dev/null
+++ b/TelepathyQt/account-capability-filter.h
@@ -0,0 +1,39 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_account_capability_filter_h_HEADER_GUARD_
+#define _TelepathyQt_account_capability_filter_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/GenericCapabilityFilter>
+
+namespace Tp
+{
+
+typedef GenericCapabilityFilter<Account> AccountCapabilityFilter;
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/account-factory.cpp b/TelepathyQt/account-factory.cpp
new file mode 100644
index 00000000..4f76278e
--- /dev/null
+++ b/TelepathyQt/account-factory.cpp
@@ -0,0 +1,157 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/AccountFactory>
+
+#include "TelepathyQt/_gen/account-factory.moc.hpp"
+
+#include <TelepathyQt/Account>
+
+namespace Tp
+{
+
+/**
+ * \class AccountFactory
+ * \ingroup utils
+ * \headerfile TelepathyQt/account-factory.h <TelepathyQt/AccountFactory>
+ *
+ * \brief The AccountFactory class is responsible for constructing Account
+ * objects according to application-defined settings.
+ *
+ * The class is used by AccountManager and other classes which construct Account
+ * proxy instances to enable sharing instances of application-defined Account
+ * subclasses with certain features always ready.
+ */
+
+/**
+ * Create a new AccountFactory object.
+ *
+ * Optionally, the \a features to make ready on all constructed proxies can be specified. The
+ * default is to make no features ready. It should be noted that unlike Account::becomeReady(),
+ * FeatureCore isn't assumed. If no features are specified, which is the default behavior, no
+ * Account::becomeReady() call is made at all and the proxy won't be Account::isReady().
+ *
+ * \param bus The QDBusConnection for proxies constructed using this factory to use.
+ * \param features The features to make ready on constructed Accounts.
+ * \return An AccountFactoryPtr object pointing to the newly created
+ * AccountFactory object.
+ */
+AccountFactoryPtr AccountFactory::create(const QDBusConnection &bus, const Features &features)
+{
+ return AccountFactoryPtr(new AccountFactory(bus, features));
+}
+
+/**
+ * Construct a new AccountFactory object.
+ *
+ * As in create(), it should be noted that unlike Account::becomeReady(), FeatureCore isn't assumed.
+ * If no \a features are specified, no Account::becomeReady() call is made at all and the proxy
+ * won't be Account::isReady().
+ *
+ * \param bus The QDBusConnection for proxies constructed using this factory to use.
+ * \param features The features to make ready on constructed Accounts.
+ */
+AccountFactory::AccountFactory(const QDBusConnection &bus, const Features &features)
+ : FixedFeatureFactory(bus)
+{
+ addFeatures(features);
+}
+
+/**
+ * Class destructor.
+ */
+AccountFactory::~AccountFactory()
+{
+}
+
+/**
+ * Constructs an Account proxy and begins making it ready.
+ *
+ * If a valid proxy already exists in the factory cache for the given combination of \a busName and
+ * \a objectPath, it is returned instead. All newly created proxies are automatically cached until
+ * they're either DBusProxy::invalidated() or the last reference to them outside the factory has
+ * been dropped.
+ *
+ * The proxy can be accessed immediately after this function returns using PendingReady::proxy().
+ * The ready operation only finishes, however, when the features specified by features(), if any,
+ * are made ready as much as possible. If the service doesn't support a given feature, they won't
+ * obviously be ready even if the operation finished successfully, as is the case for
+ * Account::becomeReady().
+ *
+ * \param busName The bus/service name of the D-Bus account object the proxy is constructed for.
+ * (Usually #TP_QT_ACCOUNT_MANAGER_BUS_NAME).
+ * \param objectPath The object path of the account.
+ * \param connFactory The connection factory to use for the Account.
+ * \param chanFactory The channel factory to use for the Account.
+ * \param contactFactory The channel factory to use for the Account.
+ * \return A PendingReady operation with the proxy in PendingReady::proxy().
+ */
+PendingReady *AccountFactory::proxy(const QString &busName, const QString &objectPath,
+ const ConnectionFactoryConstPtr &connFactory,
+ const ChannelFactoryConstPtr &chanFactory,
+ const ContactFactoryConstPtr &contactFactory) const
+{
+ DBusProxyPtr proxy = cachedProxy(busName, objectPath);
+ if (proxy.isNull()) {
+ proxy = construct(busName, objectPath, connFactory, chanFactory, contactFactory);
+ }
+
+ return nowHaveProxy(proxy);
+}
+
+/**
+ * Can be used by subclasses to override the Account subclass constructed by the factory.
+ *
+ * This is automatically called by proxy() to construct proxy instances if no valid cached proxy is
+ * found.
+ *
+ * The default implementation constructs Tp::Account objects.
+ *
+ * \param busName The bus/service name of the D-Bus account object the proxy is constructed for.
+ * (Usually #TP_QT_ACCOUNT_MANAGER_BUS_NAME).
+ * \param objectPath The object path of the account.
+ * \param connFactory The connection factory to use for the Account.
+ * \param chanFactory The channel factory to use for the Account.
+ * \param contactFactory The channel factory to use for the Account.
+ * \return A pointer to the constructed Account object.
+ */
+AccountPtr AccountFactory::construct(const QString &busName, const QString &objectPath,
+ const ConnectionFactoryConstPtr &connFactory,
+ const ChannelFactoryConstPtr &chanFactory,
+ const ContactFactoryConstPtr &contactFactory) const
+{
+ return Account::create(dbusConnection(), busName, objectPath, connFactory, chanFactory,
+ contactFactory);
+}
+
+/**
+ * Identity transform, as is appropriate for Account objects.
+ *
+ * \param uniqueOrWellKnown The name to transform.
+ * \return \a uniqueOrWellKnown
+ */
+QString AccountFactory::finalBusNameFrom(const QString &uniqueOrWellKnown) const
+{
+ return uniqueOrWellKnown;
+}
+
+}
diff --git a/TelepathyQt/account-factory.h b/TelepathyQt/account-factory.h
new file mode 100644
index 00000000..cb59f129
--- /dev/null
+++ b/TelepathyQt/account-factory.h
@@ -0,0 +1,79 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_account_factory_h_HEADER_GUARD_
+#define _TelepathyQt_account_factory_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Global>
+#include <TelepathyQt/SharedPtr>
+#include <TelepathyQt/Types>
+
+#include <TelepathyQt/Account>
+#include <TelepathyQt/Feature>
+#include <TelepathyQt/FixedFeatureFactory>
+
+class QDBusConnection;
+
+namespace Tp
+{
+
+class PendingReady;
+
+class TP_QT_EXPORT AccountFactory : public FixedFeatureFactory
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(AccountFactory)
+
+public:
+ static AccountFactoryPtr create(const QDBusConnection &bus,
+ const Features &features = Features());
+
+ virtual ~AccountFactory();
+
+ PendingReady *proxy(const QString &busName, const QString &objectPath,
+ const ConnectionFactoryConstPtr &connFactory,
+ const ChannelFactoryConstPtr &chanFactory,
+ const ContactFactoryConstPtr &contactFactory) const;
+
+protected:
+ AccountFactory(const QDBusConnection &bus, const Features &features);
+
+ virtual AccountPtr construct(const QString &busName, const QString &objectPath,
+ const ConnectionFactoryConstPtr &connFactory,
+ const ChannelFactoryConstPtr &chanFactory,
+ const ContactFactoryConstPtr &contactFactory) const;
+ virtual QString finalBusNameFrom(const QString &uniqueOrWellKnown) const;
+ // Nothing we'd like to prepare()
+ // Fixed features
+
+private:
+ struct Private;
+ Private *mPriv; // Currently unused, just for future-proofing
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/account-filter.h b/TelepathyQt/account-filter.h
new file mode 100644
index 00000000..cc13c52e
--- /dev/null
+++ b/TelepathyQt/account-filter.h
@@ -0,0 +1,39 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_account_filter_h_HEADER_GUARD_
+#define _TelepathyQt_account_filter_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Filter>
+
+namespace Tp
+{
+
+typedef Filter<Account> AccountFilter;
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/account-manager.cpp b/TelepathyQt/account-manager.cpp
new file mode 100644
index 00000000..025301ab
--- /dev/null
+++ b/TelepathyQt/account-manager.cpp
@@ -0,0 +1,1115 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/AccountManager>
+
+#include "TelepathyQt/_gen/account-manager.moc.hpp"
+#include "TelepathyQt/_gen/cli-account-manager.moc.hpp"
+#include "TelepathyQt/_gen/cli-account-manager-body.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/AccountCapabilityFilter>
+#include <TelepathyQt/AccountFilter>
+#include <TelepathyQt/AccountSet>
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/PendingAccount>
+#include <TelepathyQt/PendingComposite>
+#include <TelepathyQt/PendingReady>
+#include <TelepathyQt/ReadinessHelper>
+
+#include <QQueue>
+#include <QSet>
+#include <QTimer>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT AccountManager::Private
+{
+ Private(AccountManager *parent, const AccountFactoryConstPtr &accFactory,
+ const ConnectionFactoryConstPtr &connFactory,
+ const ChannelFactoryConstPtr &chanFactory,
+ const ContactFactoryConstPtr &contactFactory);
+ ~Private();
+
+ void init();
+
+ static void introspectMain(Private *self);
+
+ void checkIntrospectionCompleted();
+
+ QSet<QString> getAccountPathsFromProp(const QVariant &prop);
+ QSet<QString> getAccountPathsFromProps(const QVariantMap &props);
+ void addAccountForPath(const QString &accountObjectPath);
+
+ // Public object
+ AccountManager *parent;
+
+ // Instance of generated interface class
+ Client::AccountManagerInterface *baseInterface;
+
+ // Mandatory properties interface proxy
+ Client::DBus::PropertiesInterface *properties;
+
+ ReadinessHelper *readinessHelper;
+
+ AccountFactoryConstPtr accFactory;
+ ConnectionFactoryConstPtr connFactory;
+ ChannelFactoryConstPtr chanFactory;
+ ContactFactoryConstPtr contactFactory;
+
+ // Introspection
+ int reintrospectionRetries;
+ bool gotInitialAccounts;
+ QHash<QString, AccountPtr> incompleteAccounts;
+ QHash<QString, AccountPtr> accounts;
+ QStringList supportedAccountProperties;
+};
+
+static const int maxReintrospectionRetries = 5;
+static const int reintrospectionRetryInterval = 3;
+
+AccountManager::Private::Private(AccountManager *parent,
+ const AccountFactoryConstPtr &accFactory, const ConnectionFactoryConstPtr &connFactory,
+ const ChannelFactoryConstPtr &chanFactory, const ContactFactoryConstPtr &contactFactory)
+ : parent(parent),
+ baseInterface(new Client::AccountManagerInterface(parent)),
+ properties(parent->interface<Client::DBus::PropertiesInterface>()),
+ readinessHelper(parent->readinessHelper()),
+ accFactory(accFactory),
+ connFactory(connFactory),
+ chanFactory(chanFactory),
+ contactFactory(contactFactory),
+ reintrospectionRetries(0),
+ gotInitialAccounts(false)
+{
+ debug() << "Creating new AccountManager:" << parent->busName();
+
+ if (accFactory->dbusConnection().name() != parent->dbusConnection().name()) {
+ warning() << " The D-Bus connection in the account factory is not the proxy connection";
+ }
+
+ if (connFactory->dbusConnection().name() != parent->dbusConnection().name()) {
+ warning() << " The D-Bus connection in the connection factory is not the proxy connection";
+ }
+
+ if (chanFactory->dbusConnection().name() != parent->dbusConnection().name()) {
+ warning() << " The D-Bus connection in the channel factory is not the proxy connection";
+ }
+
+ ReadinessHelper::Introspectables introspectables;
+
+ // As AccountManager does not have predefined statuses let's simulate one (0)
+ ReadinessHelper::Introspectable introspectableCore(
+ QSet<uint>() << 0, // makesSenseForStatuses
+ Features(), // dependsOnFeatures
+ QStringList(), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &Private::introspectMain,
+ this);
+ introspectables[FeatureCore] = introspectableCore;
+
+ readinessHelper->addIntrospectables(introspectables);
+ readinessHelper->becomeReady(Features() << FeatureCore);
+
+ init();
+}
+
+AccountManager::Private::~Private()
+{
+ delete baseInterface;
+}
+
+void AccountManager::Private::init()
+{
+ if (!parent->isValid()) {
+ return;
+ }
+
+ parent->connect(baseInterface,
+ SIGNAL(AccountValidityChanged(QDBusObjectPath,bool)),
+ SLOT(onAccountValidityChanged(QDBusObjectPath,bool)));
+ parent->connect(baseInterface,
+ SIGNAL(AccountRemoved(QDBusObjectPath)),
+ SLOT(onAccountRemoved(QDBusObjectPath)));
+}
+
+void AccountManager::Private::introspectMain(AccountManager::Private *self)
+{
+ debug() << "Calling Properties::GetAll(AccountManager)";
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+ self->properties->GetAll(
+ QLatin1String(TELEPATHY_INTERFACE_ACCOUNT_MANAGER)),
+ self->parent);
+ self->parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotMainProperties(QDBusPendingCallWatcher*)));
+}
+
+void AccountManager::Private::checkIntrospectionCompleted()
+{
+ if (!parent->isReady(FeatureCore) &&
+ incompleteAccounts.size() == 0) {
+ readinessHelper->setIntrospectCompleted(FeatureCore, true);
+ }
+}
+
+QSet<QString> AccountManager::Private::getAccountPathsFromProp(
+ const QVariant &prop)
+{
+ QSet<QString> set;
+
+ ObjectPathList paths = qdbus_cast<ObjectPathList>(prop);
+ if (paths.size() == 0) {
+ /* maybe the AccountManager is buggy, like Mission Control
+ * 5.0.beta45, and returns an array of strings rather than
+ * an array of object paths? */
+ QStringList wronglyTypedPaths = qdbus_cast<QStringList>(prop);
+ if (wronglyTypedPaths.size() > 0) {
+ warning() << "AccountManager returned wrong type for"
+ "Valid/InvalidAccounts (expected 'ao', got 'as'); "
+ "working around it";
+ foreach (QString path, wronglyTypedPaths) {
+ set << path;
+ }
+ }
+ } else {
+ foreach (const QDBusObjectPath &path, paths) {
+ set << path.path();
+ }
+ }
+
+ return set;
+}
+
+QSet<QString> AccountManager::Private::getAccountPathsFromProps(
+ const QVariantMap &props)
+{
+ return getAccountPathsFromProp(props[QLatin1String("ValidAccounts")]).unite(
+ getAccountPathsFromProp(props[QLatin1String("InvalidAccounts")]));
+}
+
+void AccountManager::Private::addAccountForPath(const QString &path)
+{
+ // Also check incompleteAccounts, because otherwise we end up introspecting an account twice
+ // when getting an AccountValidityChanged signal for a new account before we get the initial
+ // introspection accounts list from the GetAll return (the GetAll return function
+ // unconditionally calls addAccountForPath
+ if (accounts.contains(path) || incompleteAccounts.contains(path)) {
+ return;
+ }
+
+ PendingReady *readyOp = accFactory->proxy(parent->busName(), path, connFactory,
+ chanFactory, contactFactory);
+ AccountPtr account(AccountPtr::qObjectCast(readyOp->proxy()));
+ Q_ASSERT(!account.isNull());
+
+ parent->connect(readyOp,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onAccountReady(Tp::PendingOperation*)));
+ incompleteAccounts.insert(path, account);
+}
+
+/**
+ * \class AccountManager
+ * \ingroup clientam
+ * \headerfile TelepathyQt/account-manager.h <TelepathyQt/AccountManager>
+ *
+ * \brief The AccountManager class represents a Telepathy account manager.
+ *
+ * The remote object accessor functions on this object (allAccounts(),
+ * validAccounts(), and so on) don't make any D-Bus calls; instead, they return/use
+ * values cached from a previous introspection run. The introspection process
+ * populates their values in the most efficient way possible based on what the
+ * service implements.
+ *
+ * To avoid unnecessary D-Bus traffic, some accessors only return valid
+ * information after AccountManager::FeatureCore has been enabled.
+ * See the individual methods descriptions for more details.
+ *
+ * AccountManager features can be enabled by calling becomeReady()
+ * with the desired set of features as an argument (currently only AccountManager::FeatureCore is
+ * supported), and waiting for the resulting PendingOperation to finish.
+ *
+ * All accounts returned by AccountManager are guaranteed to have the features set in the
+ * AccountFactory used by it ready.
+ *
+ * A signal is emitted to indicate that accounts are added. See newCreated() for more details.
+ *
+ * \section am_usage_sec Usage
+ *
+ * \subsection am_create_sec Creating an AccountManager object
+ *
+ * One way to create an AccountManager object is to just call the create method.
+ * For example:
+ *
+ * \code AccountManagerPtr am = AccountManager::create(); \endcode
+ *
+ * An AccountManagerPtr object is returned, which will automatically keep
+ * track of object lifetime.
+ *
+ * You can also provide a D-Bus connection as a QDBusConnection:
+ *
+ * \code AccountManagerPtr am = AccountManager::create(QDBusConnection::sessionBus()); \endcode
+ *
+ * \subsection am_ready_sec Making AccountManager ready to use
+ *
+ * An AccountManager object needs to become ready before usage, meaning that the
+ * introspection process finished and the object accessors can be used.
+ *
+ * To make the object ready, use becomeReady() and wait for the
+ * PendingOperation::finished() signal to be emitted.
+ *
+ * \code
+ *
+ * class MyClass : public QObject
+ * {
+ * QOBJECT
+ *
+ * public:
+ * MyClass(QObject *parent = 0);
+ * ~MyClass() { }
+ *
+ * private Q_SLOTS:
+ * void onAccountManagerReady(Tp::PendingOperation*);
+ *
+ * private:
+ * AccountManagerPtr mAM;
+ * };
+ *
+ * MyClass::MyClass(QObject *parent)
+ * : QObject(parent)
+ * mAM(AccountManager::create())
+ * {
+ * connect(mAM->becomeReady(),
+ * SIGNAL(finished(Tp::PendingOperation*)),
+ * SLOT(onAccountManagerReady(Tp::PendingOperation*)));
+ * }
+ *
+ * void MyClass::onAccountManagerReady(Tp::PendingOperation *op)
+ * {
+ * if (op->isError()) {
+ * qWarning() << "Account manager cannot become ready:" <<
+ * op->errorName() << "-" << op->errorMessage();
+ * return;
+ * }
+ *
+ * // AccountManager is now ready
+ * qDebug() << "All accounts:";
+ * foreach (const Tp::AccountPtr &acc, mAM->allAccounts()) {
+ * qDebug() << " path:" << acc->objectPath();
+ * }
+ * }
+ *
+ * \endcode
+ *
+ * See \ref async_model, \ref shared_ptr
+ */
+
+/**
+ * Feature representing the core that needs to become ready to make the
+ * AccountManager object usable.
+ *
+ * Note that this feature must be enabled in order to use most AccountManager
+ * methods.
+ *
+ * When calling isReady(), becomeReady(), this feature is implicitly added
+ * to the requested features.
+ */
+const Feature AccountManager::FeatureCore = Feature(QLatin1String(AccountManager::staticMetaObject.className()), 0, true);
+
+/**
+ * Create a new AccountManager object using the given \a bus.
+ *
+ * The instance will use an account factory creating Tp::Account objects with Account::FeatureCore
+ * ready, a connection factory creating Tp::Connection objects with no features ready, a channel
+ * factory creating stock Tp::Channel subclasses, as appropriate, with no features ready, and a
+ * contact factory creating Tp::Contact objects with no features ready.
+ *
+ * \param bus QDBusConnection to use.
+ * \return An AccountManagerPtr object pointing to the newly created
+ * AccountManager object.
+ */
+AccountManagerPtr AccountManager::create(const QDBusConnection &bus)
+{
+ return AccountManagerPtr(new AccountManager(bus,
+ AccountFactory::create(bus, Account::FeatureCore), ConnectionFactory::create(bus),
+ ChannelFactory::create(bus), ContactFactory::create(),
+ AccountManager::FeatureCore));
+}
+
+/**
+ * Create a new AccountManager using QDBusConnection::sessionBus() and the given factories.
+ *
+ * The connection, channel and contact factories are passed to any Account objects created by this
+ * account manager object. In fact, they're not used directly by AccountManager at all.
+ *
+ * A warning is printed if the factories are for a bus different from QDBusConnection::sessionBus().
+ *
+ * \param accountFactory The account factory to use.
+ * \param connectionFactory The connection factory to use.
+ * \param channelFactory The channel factory to use.
+ * \param contactFactory The contact factory to use.
+ * \return An AccountManagerPtr object pointing to the newly created
+ * AccountManager object.
+ */
+AccountManagerPtr AccountManager::create(const AccountFactoryConstPtr &accountFactory,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory)
+{
+ return AccountManagerPtr(new AccountManager(QDBusConnection::sessionBus(),
+ accountFactory, connectionFactory, channelFactory, contactFactory,
+ AccountManager::FeatureCore));
+}
+
+/**
+ * Create a new AccountManager using the given \a bus and the given factories.
+ *
+ * The connection, channel and contact factories are passed to any Account objects created by this
+ * account manager object. In fact, they're not used directly by AccountManager at all.
+ *
+ * A warning is printed if the factories are not for \a bus.
+ *
+ * \param bus QDBusConnection to use.
+ * \param accountFactory The account factory to use.
+ * \param connectionFactory The connection factory to use.
+ * \param channelFactory The channel factory to use.
+ * \param contactFactory The contact factory to use.
+ * \return An AccountManagerPtr object pointing to the newly created
+ * AccountManager object.
+ */
+AccountManagerPtr AccountManager::create(const QDBusConnection &bus,
+ const AccountFactoryConstPtr &accountFactory,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory)
+{
+ return AccountManagerPtr(new AccountManager(bus,
+ accountFactory, connectionFactory, channelFactory, contactFactory,
+ AccountManager::FeatureCore));
+}
+
+/**
+ * Construct a new AccountManager object using the given \a bus and the given factories.
+ *
+ * The connection, channel and contact factories are passed to any Account objects created by this
+ * account manager object. In fact, they're not used directly by AccountManager at all.
+ *
+ * A warning is printed if the factories are not for \a bus.
+ *
+ * \param bus QDBusConnection to use.
+ * \param accountFactory The account factory to use.
+ * \param connectionFactory The connection factory to use.
+ * \param channelFactory The channel factory to use.
+ * \param contactFactory The contact factory to use.
+ * \param coreFeature The core feature of the Account subclass. The corresponding introspectable
+ * should depend on AccountManager::FeatureCore.
+ */
+AccountManager::AccountManager(const QDBusConnection &bus,
+ const AccountFactoryConstPtr &accountFactory,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory,
+ const Feature &coreFeature)
+ : StatelessDBusProxy(bus,
+ TP_QT_ACCOUNT_MANAGER_BUS_NAME,
+ TP_QT_ACCOUNT_MANAGER_OBJECT_PATH, coreFeature),
+ OptionalInterfaceFactory<AccountManager>(this),
+ mPriv(new Private(this, accountFactory, connectionFactory, channelFactory, contactFactory))
+{
+}
+
+/**
+ * Class destructor.
+ */
+AccountManager::~AccountManager()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the account factory used by this account manager.
+ *
+ * Only read access is provided. This allows constructing object instances and examining the object
+ * construction settings, but not changing settings. Allowing changes would lead to tricky
+ * situations where objects constructed at different times by the manager would have unpredictably
+ * different construction settings (eg. subclass).
+ *
+ * \return A read-only pointer to the AccountFactory object.
+ */
+AccountFactoryConstPtr AccountManager::accountFactory() const
+{
+ return mPriv->accFactory;
+}
+
+/**
+ * Return the connection factory used by this account manager.
+ *
+ * Only read access is provided. This allows constructing object instances and examining the object
+ * construction settings, but not changing settings. Allowing changes would lead to tricky
+ * situations where objects constructed at different times by the manager would have unpredictably
+ * different construction settings (eg. subclass).
+ *
+ * \return A read-only pointer to the ConnectionFactory object.
+ */
+ConnectionFactoryConstPtr AccountManager::connectionFactory() const
+{
+ return mPriv->connFactory;
+}
+
+/**
+ * Return the channel factory used by this account manager.
+ *
+ * Only read access is provided. This allows constructing object instances and examining the object
+ * construction settings, but not changing settings. Allowing changes would lead to tricky
+ * situations where objects constructed at different times by the manager would have unpredictably
+ * different construction settings (eg. subclass).
+ *
+ * \return A read-only pointer to the ChannelFactory object.
+ */
+ChannelFactoryConstPtr AccountManager::channelFactory() const
+{
+ return mPriv->chanFactory;
+}
+
+/**
+ * Return the contact factory used by this account manager.
+ *
+ * Only read access is provided. This allows constructing object instances and examining the object
+ * construction settings, but not changing settings. Allowing changes would lead to tricky
+ * situations where objects constructed at different times by the manager would have unpredictably
+ * different construction settings (eg. subclass).
+ *
+ * \return A read-only pointer to the ContactFactory object.
+ */
+ContactFactoryConstPtr AccountManager::contactFactory() const
+{
+ return mPriv->contactFactory;
+}
+
+/**
+ * Return a list containing all accounts.
+ *
+ * Newly accounts added and/or discovered are signaled via newAccount().
+ *
+ * This method requires AccountManager::FeatureCore to be ready.
+ *
+ * \return A list of pointers to Account objects.
+ */
+QList<AccountPtr> AccountManager::allAccounts() const
+{
+ QList<AccountPtr> ret;
+ foreach (const AccountPtr &account, mPriv->accounts) {
+ ret << account;
+ }
+ return ret;
+}
+
+/**
+ * Return a set of accounts containing all valid accounts.
+ *
+ * This method requires AccountManager::FeatureCore to be ready.
+ *
+ * \return A pointer to an AccountSet object containing the matching accounts.
+ */
+AccountSetPtr AccountManager::validAccounts() const
+{
+ QVariantMap filter;
+ filter.insert(QLatin1String("valid"), true);
+ return filterAccounts(filter);
+}
+
+/**
+ * Return a set of accounts containing all invalid accounts.
+ *
+ * This method requires AccountManager::FeatureCore to be ready.
+ *
+ * \return A pointer to an AccountSet object containing the matching accounts.
+ */
+AccountSetPtr AccountManager::invalidAccounts() const
+{
+ QVariantMap filter;
+ filter.insert(QLatin1String("valid"), false);
+ return filterAccounts(filter);
+}
+
+/**
+ * Return a set of accounts containing all enabled accounts.
+ *
+ * This method requires AccountManager::FeatureCore to be ready.
+ *
+ * \return A pointer to an AccountSet object containing the matching accounts.
+ */
+AccountSetPtr AccountManager::enabledAccounts() const
+{
+ QVariantMap filter;
+ filter.insert(QLatin1String("enabled"), true);
+ return filterAccounts(filter);
+}
+
+/**
+ * Return a set of accounts containing all disabled accounts.
+ *
+ * This method requires AccountManager::FeatureCore to be ready.
+ *
+ * \return A pointer to an AccountSet object containing the matching accounts.
+ */
+AccountSetPtr AccountManager::disabledAccounts() const
+{
+ QVariantMap filter;
+ filter.insert(QLatin1String("enabled"), false);
+ return filterAccounts(filter);
+}
+
+/**
+ * Return a set of accounts containing all online accounts.
+ *
+ * This method requires AccountManager::FeatureCore to be ready.
+ *
+ * \return A pointer to an AccountSet object containing the matching accounts.
+ */
+AccountSetPtr AccountManager::onlineAccounts() const
+{
+ QVariantMap filter;
+ filter.insert(QLatin1String("online"), true);
+ return filterAccounts(filter);
+}
+
+/**
+ * Return a set of accounts containing all offline accounts.
+ *
+ * This method requires AccountManager::FeatureCore to be ready.
+ *
+ * \return A pointer to an AccountSet object containing the matching accounts.
+ */
+AccountSetPtr AccountManager::offlineAccounts() const
+{
+ QVariantMap filter;
+ filter.insert(QLatin1String("online"), false);
+ return filterAccounts(filter);
+}
+
+/**
+ * Return a set of accounts containing all accounts that support text chats by
+ * providing a contact identifier.
+ *
+ * For this method to work, you must use an AccountFactory which makes Account::FeatureCapabilities
+ * ready.
+ *
+ * This method requires AccountManager::FeatureCore to be ready.
+ *
+ * \return A pointer to an AccountSet object containing the matching accounts.
+ */
+AccountSetPtr AccountManager::textChatAccounts() const
+{
+ if (!accountFactory()->features().contains(Account::FeatureCapabilities)) {
+ warning() << "Account filtering by capabilities can only be used with an AccountFactory"
+ << "which makes Account::FeatureCapabilities ready";
+ return filterAccounts(AccountFilterConstPtr());
+ }
+
+ AccountCapabilityFilterPtr filter = AccountCapabilityFilter::create();
+ filter->addRequestableChannelClassSubset(RequestableChannelClassSpec::textChat());
+ return filterAccounts(filter);
+}
+
+/**
+ * Return a set of accounts containing all accounts that support text chat
+ * rooms.
+ *
+ * For this method to work, you must use an AccountFactory which makes Account::FeatureCapabilities
+ * ready.
+ *
+ * This method requires AccountManager::FeatureCore to be ready.
+ *
+ * \return A pointer to an AccountSet object containing the matching accounts.
+ */
+AccountSetPtr AccountManager::textChatroomAccounts() const
+{
+ if (!accountFactory()->features().contains(Account::FeatureCapabilities)) {
+ warning() << "Account filtering by capabilities can only be used with an AccountFactory"
+ << "which makes Account::FeatureCapabilities ready";
+ return filterAccounts(AccountFilterConstPtr());
+ }
+
+ AccountCapabilityFilterPtr filter = AccountCapabilityFilter::create();
+ filter->addRequestableChannelClassSubset(RequestableChannelClassSpec::textChatroom());
+ return filterAccounts(filter);
+}
+
+/**
+ * Return a set of accounts containing all accounts that support media calls (using the
+ * StreamedMedia interface) by providing a contact identifier.
+ *
+ * For this method to work, you must use an AccountFactory which makes Account::FeatureCapabilities
+ * ready.
+ *
+ * This method requires AccountManager::FeatureCore to be ready.
+ *
+ * \return A pointer to an AccountSet object containing the matching accounts.
+ */
+AccountSetPtr AccountManager::streamedMediaCallAccounts() const
+{
+ if (!accountFactory()->features().contains(Account::FeatureCapabilities)) {
+ warning() << "Account filtering by capabilities can only be used with an AccountFactory"
+ << "which makes Account::FeatureCapabilities ready";
+ return filterAccounts(AccountFilterConstPtr());
+ }
+
+ AccountCapabilityFilterPtr filter = AccountCapabilityFilter::create();
+ filter->addRequestableChannelClassSubset(RequestableChannelClassSpec::streamedMediaCall());
+ return filterAccounts(filter);
+}
+
+/**
+ * Return a set of accounts containing all accounts that support audio calls (using the
+ * StreamedMedia interface) by providing a contact identifier.
+ *
+ * For this method to work, you must use an AccountFactory which makes Account::FeatureCapabilities
+ * ready.
+ *
+ * This method requires AccountManager::FeatureCore to be ready.
+ *
+ * \return A pointer to an AccountSet object containing the matching accounts.
+ */
+AccountSetPtr AccountManager::streamedMediaAudioCallAccounts() const
+{
+ if (!accountFactory()->features().contains(Account::FeatureCapabilities)) {
+ warning() << "Account filtering by capabilities can only be used with an AccountFactory"
+ << "which makes Account::FeatureCapabilities ready";
+ return filterAccounts(AccountFilterConstPtr());
+ }
+
+ AccountCapabilityFilterPtr filter = AccountCapabilityFilter::create();
+ filter->addRequestableChannelClassSubset(RequestableChannelClassSpec::streamedMediaAudioCall());
+ return filterAccounts(filter);
+}
+
+/**
+ * Return a set of accounts containing all accounts that support video calls (using the
+ * StreamedMedia interface) by providing a contact identifier.
+ *
+ * For this method to work, you must use an AccountFactory which makes Account::FeatureCapabilities
+ * ready.
+ *
+ * This method requires AccountManager::FeatureCore to be ready.
+ *
+ * \return A pointer to an AccountSet object containing the matching accounts.
+ */
+AccountSetPtr AccountManager::streamedMediaVideoCallAccounts() const
+{
+ if (!accountFactory()->features().contains(Account::FeatureCapabilities)) {
+ warning() << "Account filtering by capabilities can only be used with an AccountFactory"
+ << "which makes Account::FeatureCapabilities ready";
+ return filterAccounts(AccountFilterConstPtr());
+ }
+
+ AccountCapabilityFilterPtr filter = AccountCapabilityFilter::create();
+ filter->addRequestableChannelClassSubset(RequestableChannelClassSpec::streamedMediaVideoCall());
+ return filterAccounts(filter);
+}
+
+/**
+ * Return a set of accounts containing all accounts that support video calls with audio (using the
+ * StreamedMedia interface) by providing a contact identifier.
+ *
+ * For this method to work, you must use an AccountFactory which makes Account::FeatureCapabilities
+ * ready.
+ *
+ * This method requires AccountManager::FeatureCore to be ready.
+ *
+ * \return A pointer to an AccountSet object containing the matching accounts.
+ */
+AccountSetPtr AccountManager::streamedMediaVideoCallWithAudioAccounts() const
+{
+ if (!accountFactory()->features().contains(Account::FeatureCapabilities)) {
+ warning() << "Account filtering by capabilities can only be used with an AccountFactory"
+ << "which makes Account::FeatureCapabilities ready";
+ return filterAccounts(AccountFilterConstPtr());
+ }
+
+ AccountCapabilityFilterPtr filter = AccountCapabilityFilter::create();
+ filter->addRequestableChannelClassSubset(
+ RequestableChannelClassSpec::streamedMediaVideoCallWithAudio());
+ return filterAccounts(filter);
+}
+
+/**
+ * Return a set of accounts containing all accounts that support file transfers by
+ * providing a contact identifier.
+ *
+ * For this method to work, you must use an AccountFactory which makes Account::FeatureCapabilities
+ * ready.
+ *
+ * This method requires AccountManager::FeatureCore to be ready.
+ *
+ * \return A pointer to an AccountSet object containing the matching accounts.
+ */
+AccountSetPtr AccountManager::fileTransferAccounts() const
+{
+ if (!accountFactory()->features().contains(Account::FeatureCapabilities)) {
+ warning() << "Account filtering by capabilities can only be used with an AccountFactory"
+ << "which makes Account::FeatureCapabilities ready";
+ return filterAccounts(AccountFilterConstPtr());
+ }
+
+ AccountCapabilityFilterPtr filter = AccountCapabilityFilter::create();
+ filter->addRequestableChannelClassSubset(RequestableChannelClassSpec::fileTransfer());
+ return filterAccounts(filter);
+}
+
+/**
+ * Return a set of accounts containing all accounts for the given \a
+ * protocolName.
+ *
+ * This method requires AccountManager::FeatureCore to be ready.
+ *
+ * \param protocolName The name of the protocol used to filter accounts.
+ * \return A pointer to an AccountSet object containing the matching accounts.
+ */
+AccountSetPtr AccountManager::accountsByProtocol(
+ const QString &protocolName) const
+{
+ if (!isReady(FeatureCore)) {
+ warning() << "Account filtering requires AccountManager to be ready";
+ return filterAccounts(AccountFilterConstPtr());
+ }
+
+ QVariantMap filter;
+ filter.insert(QLatin1String("protocolName"), protocolName);
+ return filterAccounts(filter);
+}
+
+/**
+ * Return a set of accounts containing all accounts that match the given \a
+ * filter criteria.
+ *
+ * For AccountCapabilityFilter filtering, an AccountFactory which makes
+ * Account::FeatureCapabilities ready must be used.
+ *
+ * See AccountSet documentation for more details.
+ *
+ * This method requires AccountManager::FeatureCore to be ready.
+ *
+ * \param filter The desired filter.
+ * \return A pointer to an AccountSet object containing the matching accounts.
+ */
+AccountSetPtr AccountManager::filterAccounts(const AccountFilterConstPtr &filter) const
+{
+ if (!isReady(FeatureCore)) {
+ warning() << "Account filtering requires AccountManager to be ready";
+ return AccountSetPtr(new AccountSet(AccountManagerPtr(
+ (AccountManager *) this), AccountFilterConstPtr()));
+ }
+
+ return AccountSetPtr(new AccountSet(AccountManagerPtr(
+ (AccountManager *) this), filter));
+}
+
+/**
+ * Return a set of accounts containing all accounts that match the given \a
+ * filter criteria.
+ *
+ * The \a filter is composed by Account property names and values as map items.
+ *
+ * The following example will return all jabber accounts that are enabled:
+ *
+ * \code
+ *
+ * void MyClass::init()
+ * {
+ * mAM = AccountManager::create();
+ * connect(mAM->becomeReady(),
+ * SIGNAL(finished(Tp::PendingOperation*)),
+ * SLOT(onAccountManagerReady(Tp::PendingOperation*)));
+ * }
+ *
+ * void MyClass::onAccountManagerReady(Tp::PendingOperation *op)
+ * {
+ * if (op->isError()) {
+ * qWarning() << "Account manager cannot become ready:" <<
+ * op->errorName() << "-" << op->errorMessage();
+ * return;
+ * }
+ *
+ * QVariantMap filter;
+ * filter.insert(QLatin1String("protocolName"), QLatin1String("jabber"));
+ * filter.insert(QLatin1String("enabled"), true);
+ * filteredAccountSet = mAM->filterAccounts(filter);
+ * // connect to AccountSet::accountAdded/accountRemoved signals
+ * QList<AccountPtr> accounts = filteredAccountSet->accounts();
+ * // do something with accounts
+ * }
+ *
+ * \endcode
+ *
+ * See AccountSet documentation for more details.
+ *
+ * This method requires AccountManager::FeatureCore to be ready.
+ *
+ * \param filter The desired filter.
+ * \return A pointer to an AccountSet object containing the matching accounts.
+ */
+AccountSetPtr AccountManager::filterAccounts(const QVariantMap &filter) const
+{
+ if (!isReady(FeatureCore)) {
+ warning() << "Account filtering requires AccountManager to be ready";
+ return AccountSetPtr(new AccountSet(AccountManagerPtr(
+ (AccountManager *) this), QVariantMap()));
+ }
+
+ return AccountSetPtr(new AccountSet(AccountManagerPtr(
+ (AccountManager *) this), filter));
+}
+
+/**
+ * Return the account for the given \a path.
+ *
+ * This method requires AccountManager::FeatureCore to be ready.
+ *
+ * \param path The account object path.
+ * \return A pointer to an AccountSet object containing the matching accounts.
+ * \sa allAccounts(), accountsForPaths()
+ */
+AccountPtr AccountManager::accountForPath(const QString &path) const
+{
+ if (!isReady(FeatureCore)) {
+ return AccountPtr();
+ }
+
+ return mPriv->accounts.value(path);
+}
+
+/**
+ * Return a list of accounts for the given \a paths.
+ *
+ * The returned list will have one AccountPtr object for each given path. If
+ * a given path is invalid the returned AccountPtr object will point to 0.
+ * AccountPtr::isNull() will return true.
+ *
+ * This method requires AccountManager::FeatureCore to be ready.
+ *
+ * \param paths List of accounts object paths.
+ * \return A list of pointers to Account objects for the given
+ * \a paths. Null AccountPtr objects will be used as list elements for each invalid path.
+ * \sa allAccounts(), accountForPath()
+ */
+QList<AccountPtr> AccountManager::accountsForPaths(const QStringList &paths) const
+{
+ if (!isReady(FeatureCore)) {
+ return QList<AccountPtr>();
+ }
+
+ QList<AccountPtr> result;
+ foreach (const QString &path, paths) {
+ result << accountForPath(path);
+ }
+ return result;
+}
+
+/**
+ * Return a list of the fully qualified names of properties that can be set
+ * when calling createAccount().
+ *
+ * \return A list of fully qualified D-Bus property names,
+ * such as "org.freedesktop.Telepathy.Account.Enabled".
+ * \sa createAccount()
+ */
+QStringList AccountManager::supportedAccountProperties() const
+{
+ return mPriv->supportedAccountProperties;
+}
+
+/**
+ * Create an account with the given parameters.
+ *
+ * The optional \a properties argument can be used to set any property listed in
+ * supportedAccountProperties() at the time the account is created.
+ *
+ * \param connectionManager The name of the connection manager to create the account
+ * for.
+ * \param protocol The name of the protocol to create the account for.
+ * \param displayName The account display name.
+ * \param parameters The account parameters.
+ * \param properties An optional map from fully qualified D-Bus property
+ * names such as "org.freedesktop.Telepathy.Account.Enabled"
+ * to their values.
+ * \return A PendingAccount object which will emit PendingAccount::finished
+ * when the account has been created of failed its creation process.
+ * \sa supportedAccountProperties()
+ */
+PendingAccount *AccountManager::createAccount(const QString &connectionManager,
+ const QString &protocol, const QString &displayName,
+ const QVariantMap &parameters, const QVariantMap &properties)
+{
+ return new PendingAccount(AccountManagerPtr(this), connectionManager,
+ protocol, displayName, parameters, properties);
+}
+
+/**
+ * Return the Client::AccountManagerInterface interface proxy object for this
+ * account manager. This method is protected since the convenience methods
+ * provided by this class should generally be used instead of calling D-Bus
+ * methods directly.
+ *
+ * \return A pointer to the existing Client::AccountManagerInterface object for
+ * this AccountManager object.
+ */
+Client::AccountManagerInterface *AccountManager::baseInterface() const
+{
+ return mPriv->baseInterface;
+}
+
+void AccountManager::introspectMain()
+{
+ mPriv->introspectMain(mPriv);
+}
+
+void AccountManager::gotMainProperties(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QVariantMap> reply = *watcher;
+ QVariantMap props;
+
+ if (!reply.isError()) {
+ mPriv->gotInitialAccounts = true;
+
+ debug() << "Got reply to Properties.GetAll(AccountManager)";
+ props = reply.value();
+
+ if (props.contains(QLatin1String("Interfaces"))) {
+ setInterfaces(qdbus_cast<QStringList>(props[QLatin1String("Interfaces")]));
+ mPriv->readinessHelper->setInterfaces(interfaces());
+ }
+
+ if (props.contains(QLatin1String("SupportedAccountProperties"))) {
+ mPriv->supportedAccountProperties =
+ qdbus_cast<QStringList>(props[QLatin1String("SupportedAccountProperties")]);
+ }
+
+ QSet<QString> paths = mPriv->getAccountPathsFromProps(props);
+ foreach (const QString &path, paths) {
+ mPriv->addAccountForPath(path);
+ }
+
+ mPriv->checkIntrospectionCompleted();
+ } else {
+ if (mPriv->reintrospectionRetries++ < maxReintrospectionRetries) {
+ int retryInterval = reintrospectionRetryInterval;
+ if (reply.error().type() == QDBusError::TimedOut) {
+ retryInterval = 0;
+ }
+ QTimer::singleShot(retryInterval, this, SLOT(introspectMain()));
+ } else {
+ warning() << "GetAll(AccountManager) failed with" <<
+ reply.error().name() << ":" << reply.error().message();
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureCore,
+ false, reply.error());
+ }
+ }
+
+ watcher->deleteLater();
+}
+
+void AccountManager::onAccountReady(Tp::PendingOperation *op)
+{
+ PendingReady *pr = qobject_cast<PendingReady*>(op);
+ AccountPtr account(AccountPtr::qObjectCast(pr->proxy()));
+ QString path = account->objectPath();
+
+ /* Some error occurred or the account was removed before become ready */
+ if (op->isError() || !mPriv->incompleteAccounts.contains(path)) {
+ mPriv->incompleteAccounts.remove(path);
+ mPriv->checkIntrospectionCompleted();
+ return;
+ }
+
+ mPriv->incompleteAccounts.remove(path);
+
+ // We shouldn't end up here twice for the same account - that would also mean newAccount being
+ // emitted twice for an account, and AccountSets getting confused as a result
+ Q_ASSERT(!mPriv->accounts.contains(path));
+ mPriv->accounts.insert(path, account);
+
+ if (isReady(FeatureCore)) {
+ emit newAccount(account);
+ }
+
+ mPriv->checkIntrospectionCompleted();
+}
+
+void AccountManager::onAccountValidityChanged(const QDBusObjectPath &objectPath,
+ bool valid)
+{
+ if (!mPriv->gotInitialAccounts) {
+ return;
+ }
+
+ QString path = objectPath.path();
+
+ if (!mPriv->incompleteAccounts.contains(path) &&
+ !mPriv->accounts.contains(path)) {
+ debug() << "New account" << path;
+ mPriv->addAccountForPath(path);
+ }
+}
+
+void AccountManager::onAccountRemoved(const QDBusObjectPath &objectPath)
+{
+ if (!mPriv->gotInitialAccounts) {
+ return;
+ }
+
+ QString path = objectPath.path();
+
+ /* the account is either in mPriv->incompleteAccounts or mPriv->accounts */
+ if (mPriv->accounts.contains(path)) {
+ mPriv->accounts.remove(path);
+
+ if (isReady(FeatureCore)) {
+ debug() << "Account" << path << "removed";
+ } else {
+ debug() << "Account" << path << "removed while the AM "
+ "was not completely introspected";
+ }
+ } else if (mPriv->incompleteAccounts.contains(path)) {
+ mPriv->incompleteAccounts.remove(path);
+ debug() << "Account" << path << "was removed, but it was "
+ "not completely introspected, ignoring";
+ } else {
+ debug() << "Got AccountRemoved for unknown account" << path << ", ignoring";
+ }
+}
+
+/**
+ * \fn void AccountManager::newAccount(const Tp::AccountPtr &account)
+ *
+ * Emitted when a new account is created.
+ *
+ * The new \a account will have the features set in the AccountFactory used by this
+ * account manager ready and the same connection, channel and contact factories as used by this
+ * account manager.
+ *
+ * \param account The newly created account.
+ */
+
+} // Tp
diff --git a/TelepathyQt/account-manager.h b/TelepathyQt/account-manager.h
new file mode 100644
index 00000000..53ab2cd7
--- /dev/null
+++ b/TelepathyQt/account-manager.h
@@ -0,0 +1,152 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_account_manager_h_HEADER_GUARD_
+#define _TelepathyQt_account_manager_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/_gen/cli-account-manager.h>
+
+#include <TelepathyQt/Account>
+#include <TelepathyQt/AccountFactory>
+#include <TelepathyQt/ChannelFactory>
+#include <TelepathyQt/ConnectionFactory>
+#include <TelepathyQt/ContactFactory>
+#include <TelepathyQt/StatelessDBusProxy>
+#include <TelepathyQt/Filter>
+#include <TelepathyQt/OptionalInterfaceFactory>
+#include <TelepathyQt/SharedPtr>
+#include <TelepathyQt/Types>
+
+#include <QDBusObjectPath>
+#include <QSet>
+#include <QString>
+#include <QVariantMap>
+
+namespace Tp
+{
+
+class PendingAccount;
+
+class TP_QT_EXPORT AccountManager : public StatelessDBusProxy,
+ public OptionalInterfaceFactory<AccountManager>
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(AccountManager)
+
+public:
+ static const Feature FeatureCore;
+
+ static AccountManagerPtr create(const QDBusConnection &bus);
+ static AccountManagerPtr create(
+ const AccountFactoryConstPtr &accountFactory =
+ AccountFactory::create(QDBusConnection::sessionBus(), Account::FeatureCore),
+ const ConnectionFactoryConstPtr &connectionFactory =
+ ConnectionFactory::create(QDBusConnection::sessionBus()),
+ const ChannelFactoryConstPtr &channelFactory =
+ ChannelFactory::create(QDBusConnection::sessionBus()),
+ const ContactFactoryConstPtr &contactFactory =
+ ContactFactory::create());
+ static AccountManagerPtr create(const QDBusConnection &bus,
+ const AccountFactoryConstPtr &accountFactory,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory =
+ ContactFactory::create());
+
+ virtual ~AccountManager();
+
+ AccountFactoryConstPtr accountFactory() const;
+ ConnectionFactoryConstPtr connectionFactory() const;
+ ChannelFactoryConstPtr channelFactory() const;
+ ContactFactoryConstPtr contactFactory() const;
+
+ QList<AccountPtr> allAccounts() const;
+
+ AccountSetPtr validAccounts() const;
+ AccountSetPtr invalidAccounts() const;
+
+ AccountSetPtr enabledAccounts() const;
+ AccountSetPtr disabledAccounts() const;
+
+ AccountSetPtr onlineAccounts() const;
+ AccountSetPtr offlineAccounts() const;
+
+ AccountSetPtr textChatAccounts() const;
+ AccountSetPtr textChatroomAccounts() const;
+
+ AccountSetPtr streamedMediaCallAccounts() const;
+ AccountSetPtr streamedMediaAudioCallAccounts() const;
+ AccountSetPtr streamedMediaVideoCallAccounts() const;
+ AccountSetPtr streamedMediaVideoCallWithAudioAccounts() const;
+
+ AccountSetPtr fileTransferAccounts() const;
+
+ AccountSetPtr accountsByProtocol(const QString &protocolName) const;
+
+ AccountSetPtr filterAccounts(const AccountFilterConstPtr &filter) const;
+ AccountSetPtr filterAccounts(const QVariantMap &filter) const;
+
+ AccountPtr accountForPath(const QString &path) const;
+ QList<AccountPtr> accountsForPaths(const QStringList &paths) const;
+
+ QStringList supportedAccountProperties() const;
+ PendingAccount *createAccount(const QString &connectionManager,
+ const QString &protocol, const QString &displayName,
+ const QVariantMap &parameters,
+ const QVariantMap &properties = QVariantMap());
+
+Q_SIGNALS:
+ void newAccount(const Tp::AccountPtr &account);
+
+protected:
+ AccountManager(const QDBusConnection &bus,
+ const AccountFactoryConstPtr &accountFactory,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory,
+ const Feature &coreFeature);
+
+ Client::AccountManagerInterface *baseInterface() const;
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void introspectMain();
+ TP_QT_NO_EXPORT void gotMainProperties(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void onAccountReady(Tp::PendingOperation *op);
+ TP_QT_NO_EXPORT void onAccountValidityChanged(const QDBusObjectPath &objectPath,
+ bool valid);
+ TP_QT_NO_EXPORT void onAccountRemoved(const QDBusObjectPath &objectPath);
+
+private:
+ friend class PendingAccount;
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/account-manager.xml b/TelepathyQt/account-manager.xml
new file mode 100644
index 00000000..278f4276
--- /dev/null
+++ b/TelepathyQt/account-manager.xml
@@ -0,0 +1,9 @@
+<tp:spec
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<tp:title>Account Manager interfaces</tp:title>
+
+<xi:include href="../spec/Account_Manager.xml"/>
+
+</tp:spec>
diff --git a/TelepathyQt/account-property-filter.cpp b/TelepathyQt/account-property-filter.cpp
new file mode 100644
index 00000000..10c5ba77
--- /dev/null
+++ b/TelepathyQt/account-property-filter.cpp
@@ -0,0 +1,94 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/AccountPropertyFilter>
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <QLatin1String>
+#include <QStringList>
+#include <QMetaObject>
+#include <QVariantMap>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT AccountPropertyFilter::Private
+{
+ Private()
+ {
+ if (supportedAccountProperties.isEmpty()) {
+ const QMetaObject metaObject = Account::staticMetaObject;
+ for (int i = metaObject.propertyOffset(); i < metaObject.propertyCount(); ++i) {
+ supportedAccountProperties << QLatin1String(metaObject.property(i).name());
+ }
+ }
+ }
+
+ static QStringList supportedAccountProperties;
+};
+
+QStringList AccountPropertyFilter::Private::supportedAccountProperties;
+
+/**
+ * \class Tp::AccountPropertyFilter
+ * \ingroup utils
+ * \headerfile TelepathyQt/account-property-filter.h <TelepathyQt/AccountPropertyFilter>
+ *
+ * \brief The AccountPropertyFilter class provides a filter object to be used
+ * to filter accounts by properties.
+ */
+
+AccountPropertyFilter::AccountPropertyFilter()
+ : GenericPropertyFilter<Account>(),
+ mPriv(new Private())
+{
+}
+
+AccountPropertyFilter::~AccountPropertyFilter()
+{
+ delete mPriv;
+}
+
+bool AccountPropertyFilter::isValid() const
+{
+ QVariantMap mFilter = filter();
+ if (mFilter.isEmpty()) {
+ return false;
+ }
+
+ QVariantMap::const_iterator i = mFilter.constBegin();
+ QVariantMap::const_iterator end = mFilter.constEnd();
+ while (i != end) {
+ QString propertyName = i.key();
+ if (!mPriv->supportedAccountProperties.contains(propertyName)) {
+ warning() << "Invalid filter key" << propertyName <<
+ "while filtering account by properties";
+ return false;
+ }
+ ++i;
+ }
+
+ return true;
+}
+
+} // Tp
diff --git a/TelepathyQt/account-property-filter.h b/TelepathyQt/account-property-filter.h
new file mode 100644
index 00000000..5694831e
--- /dev/null
+++ b/TelepathyQt/account-property-filter.h
@@ -0,0 +1,59 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_account_property_filter_h_HEADER_GUARD_
+#define _TelepathyQt_account_property_filter_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Account>
+#include <TelepathyQt/GenericPropertyFilter>
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+class TP_QT_EXPORT AccountPropertyFilter : public GenericPropertyFilter<Account>
+{
+public:
+ static AccountPropertyFilterPtr create()
+ {
+ return AccountPropertyFilterPtr(new AccountPropertyFilter);
+ }
+
+ ~AccountPropertyFilter();
+
+ bool isValid() const;
+
+private:
+ AccountPropertyFilter();
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/account-set-internal.h b/TelepathyQt/account-set-internal.h
new file mode 100644
index 00000000..f7dbf9e0
--- /dev/null
+++ b/TelepathyQt/account-set-internal.h
@@ -0,0 +1,82 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/AccountPropertyFilter>
+
+namespace Tp
+{
+
+class ConnectionCapabilities;
+
+struct TP_QT_NO_EXPORT AccountSet::Private
+{
+ class AccountWrapper;
+
+ Private(AccountSet *parent, const AccountManagerPtr &accountManager,
+ const AccountFilterConstPtr &filter);
+ Private(AccountSet *parent, const AccountManagerPtr &accountManager,
+ const QVariantMap &filter);
+
+ void init();
+ void connectSignals();
+ void insertAccounts();
+ void insertAccount(const AccountPtr &account);
+ void removeAccount(const AccountPtr &account);
+ void wrapAccount(const AccountPtr &account);
+ void filterAccount(const AccountPtr &account);
+ bool accountMatchFilter(AccountWrapper *account);
+
+ AccountSet *parent;
+ AccountManagerPtr accountManager;
+ AccountFilterConstPtr filter;
+ QHash<QString, AccountWrapper *> wrappers;
+ QHash<QString, AccountPtr> accounts;
+ bool ready;
+};
+
+class TP_QT_NO_EXPORT AccountSet::Private::AccountWrapper : public QObject
+{
+ Q_OBJECT
+
+public:
+ AccountWrapper(const AccountPtr &account, QObject *parent = 0);
+ ~AccountWrapper();
+
+ AccountPtr account() const { return mAccount; }
+
+Q_SIGNALS:
+ void accountRemoved(const Tp::AccountPtr &account);
+ void accountPropertyChanged(const Tp::AccountPtr &account,
+ const QString &propertyName);
+ void accountCapabilitiesChanged(const Tp::AccountPtr &account,
+ const Tp::ConnectionCapabilities &capabilities);
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onAccountRemoved();
+ TP_QT_NO_EXPORT void onAccountPropertyChanged(const QString &propertyName);
+ TP_QT_NO_EXPORT void onAccountCapalitiesChanged(const Tp::ConnectionCapabilities &capabilities);
+
+private:
+ AccountPtr mAccount;
+};
+
+} // Tp
diff --git a/TelepathyQt/account-set.cpp b/TelepathyQt/account-set.cpp
new file mode 100644
index 00000000..fce92a39
--- /dev/null
+++ b/TelepathyQt/account-set.cpp
@@ -0,0 +1,418 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/AccountSet>
+#include "TelepathyQt/account-set-internal.h"
+
+#include "TelepathyQt/_gen/account-set.moc.hpp"
+#include "TelepathyQt/_gen/account-set-internal.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/Account>
+#include <TelepathyQt/AccountFilter>
+#include <TelepathyQt/AccountManager>
+#include <TelepathyQt/ConnectionCapabilities>
+#include <TelepathyQt/ConnectionManager>
+
+namespace Tp
+{
+
+AccountSet::Private::Private(AccountSet *parent,
+ const AccountManagerPtr &accountManager,
+ const AccountFilterConstPtr &filter)
+ : parent(parent),
+ accountManager(accountManager),
+ filter(filter),
+ ready(false)
+{
+ init();
+}
+
+AccountSet::Private::Private(AccountSet *parent,
+ const AccountManagerPtr &accountManager,
+ const QVariantMap &filterMap)
+ : parent(parent),
+ accountManager(accountManager),
+ ready(false)
+{
+ AccountPropertyFilterPtr propertyFilter = AccountPropertyFilter::create();
+ for (QVariantMap::const_iterator i = filterMap.constBegin();
+ i != filterMap.constEnd(); ++i) {
+ propertyFilter->addProperty(i.key(), i.value());
+ }
+ filter = AccountFilterPtr::dynamicCast(propertyFilter);
+ init();
+}
+
+void AccountSet::Private::init()
+{
+ if (filter->isValid()) {
+ connectSignals();
+ insertAccounts();
+ ready = true;
+ }
+}
+
+void AccountSet::Private::connectSignals()
+{
+ parent->connect(accountManager.data(),
+ SIGNAL(newAccount(Tp::AccountPtr)),
+ SLOT(onNewAccount(Tp::AccountPtr)));
+}
+
+void AccountSet::Private::insertAccounts()
+{
+ foreach (const Tp::AccountPtr &account, accountManager->allAccounts()) {
+ insertAccount(account);
+ }
+}
+
+void AccountSet::Private::insertAccount(const Tp::AccountPtr &account)
+{
+ QString accountPath = account->objectPath();
+ Q_ASSERT(!wrappers.contains(accountPath));
+ wrapAccount(account);
+ filterAccount(account);
+}
+
+void AccountSet::Private::removeAccount(const Tp::AccountPtr &account)
+{
+ QString accountPath = account->objectPath();
+ Q_ASSERT(wrappers.contains(accountPath));
+ accounts.remove(accountPath);
+
+ AccountWrapper *wrapper = wrappers.take(accountPath);
+ Q_ASSERT(wrapper->disconnect(parent));
+ wrapper->deleteLater();
+
+ emit parent->accountRemoved(account);
+}
+
+void AccountSet::Private::wrapAccount(const AccountPtr &account)
+{
+ AccountWrapper *wrapper = new AccountWrapper(account, parent);
+ parent->connect(wrapper,
+ SIGNAL(accountRemoved(Tp::AccountPtr)),
+ SLOT(onAccountRemoved(Tp::AccountPtr)));
+ parent->connect(wrapper,
+ SIGNAL(accountPropertyChanged(Tp::AccountPtr,QString)),
+ SLOT(onAccountChanged(Tp::AccountPtr)));
+ parent->connect(wrapper,
+ SIGNAL(accountCapabilitiesChanged(Tp::AccountPtr,Tp::ConnectionCapabilities)),
+ SLOT(onAccountChanged(Tp::AccountPtr)));
+ wrappers.insert(account->objectPath(), wrapper);
+}
+
+void AccountSet::Private::filterAccount(const AccountPtr &account)
+{
+ QString accountPath = account->objectPath();
+ Q_ASSERT(wrappers.contains(accountPath));
+ AccountWrapper *wrapper = wrappers[accountPath];
+
+ /* account changed, let's check if it matches filter */
+ if (accountMatchFilter(wrapper)) {
+ if (!accounts.contains(account->objectPath())) {
+ accounts.insert(account->objectPath(), account);
+ if (ready) {
+ emit parent->accountAdded(account);
+ }
+ }
+ } else {
+ if (accounts.contains(account->objectPath())) {
+ accounts.remove(account->objectPath());
+ if (ready) {
+ emit parent->accountRemoved(account);
+ }
+ }
+ }
+}
+
+bool AccountSet::Private::accountMatchFilter(AccountWrapper *wrapper)
+{
+ if (!filter) {
+ return true;
+ }
+
+ return filter->matches(wrapper->account());
+}
+
+AccountSet::Private::AccountWrapper::AccountWrapper(
+ const AccountPtr &account, QObject *parent)
+ : QObject(parent),
+ mAccount(account)
+{
+ connect(account.data(),
+ SIGNAL(removed()),
+ SLOT(onAccountRemoved()));
+ connect(account.data(),
+ SIGNAL(propertyChanged(QString)),
+ SLOT(onAccountPropertyChanged(QString)));
+ connect(account.data(),
+ SIGNAL(capabilitiesChanged(Tp::ConnectionCapabilities)),
+ SLOT(onAccountCapalitiesChanged(Tp::ConnectionCapabilities)));
+}
+
+AccountSet::Private::AccountWrapper::~AccountWrapper()
+{
+}
+
+void AccountSet::Private::AccountWrapper::onAccountRemoved()
+{
+ emit accountRemoved(mAccount);
+}
+
+void AccountSet::Private::AccountWrapper::onAccountPropertyChanged(
+ const QString &propertyName)
+{
+ emit accountPropertyChanged(mAccount, propertyName);
+}
+
+void AccountSet::Private::AccountWrapper::onAccountCapalitiesChanged(
+ const ConnectionCapabilities &caps)
+{
+ emit accountCapabilitiesChanged(mAccount, caps);
+}
+
+/**
+ * \class AccountSet
+ * \ingroup clientaccount
+ * \headerfile TelepathyQt/account-set.h <TelepathyQt/AccountSet>
+ *
+ * \brief The AccountSet class represents a set of Telepathy accounts
+ * filtered by a given criteria.
+ *
+ * AccountSet is automatically updated whenever accounts that match the given
+ * criteria are added, removed or updated.
+ *
+ * \section account_set_usage_sec Usage
+ *
+ * \subsection account_set_create_sec Creating an AccountSet object
+ *
+ * The easiest way to create AccountSet objects is through AccountManager. One
+ * can just use the AccountManager convenience methods such as
+ * AccountManager::validAccounts() to get a set of account objects
+ * representing valid accounts.
+ *
+ * For example:
+ *
+ * \code
+ *
+ * class MyClass : public QObject
+ * {
+ * QOBJECT
+ *
+ * public:
+ * MyClass(QObject *parent = 0);
+ * ~MyClass() { }
+ *
+ * private Q_SLOTS:
+ * void onAccountManagerReady(Tp::PendingOperation *);
+ * void onValidAccountAdded(const Tp::AccountPtr &);
+ * void onValidAccountRemoved(const Tp::AccountPtr &);
+ *
+ * private:
+ * AccountManagerPtr am;
+ * AccountSetPtr validAccountsSet;
+ * };
+ *
+ * MyClass::MyClass(QObject *parent)
+ * : QObject(parent)
+ * am(AccountManager::create())
+ * {
+ * connect(am->becomeReady(),
+ * SIGNAL(finished(Tp::PendingOperation*)),
+ * SLOT(onAccountManagerReady(Tp::PendingOperation*)));
+ * }
+ *
+ * void MyClass::onAccountManagerReady(Tp::PendingOperation *op)
+ * {
+ * if (op->isError()) {
+ * qWarning() << "Account manager cannot become ready:" <<
+ * op->errorName() << "-" << op->errorMessage();
+ * return;
+ * }
+ *
+ * validAccountsSet = am->validAccounts();
+ * connect(validAccountsSet.data(),
+ * SIGNAL(accountAdded(const Tp::AccountPtr &)),
+ * SLOT(onValidAccountAdded(const Tp::AccountPtr &)));
+ * connect(validAccountsSet.data(),
+ * SIGNAL(accountRemoved(const Tp::AccountPtr &)),
+ * SLOT(onValidAccountRemoved(const Tp::AccountPtr &)));
+ *
+ * QList<AccountPtr> accounts = validAccountsSet->accounts();
+ * // do something with accounts
+ * }
+ *
+ * void MyClass::onValidAccountAdded(const Tp::AccountPtr &account)
+ * {
+ * // do something with account
+ * }
+ *
+ * void MyClass::onValidAccountRemoved(const Tp::AccountPtr &account)
+ * {
+ * // do something with account
+ * }
+ *
+ * \endcode
+ *
+ * You can also define your own filter using AccountManager::filterAccounts:
+ *
+ * \code
+ *
+ * void MyClass::onAccountManagerReady(Tp::PendingOperation *op)
+ * {
+ * ...
+ *
+ * AccountPropertyFilterPtr filter = AccountPropertyFilter::create();
+ * filter->addProperty(QLatin1String("protocolName"), QLatin1String("jabber"));
+ * filter->addProperty(QLatin1String("enabled"), true);
+ *
+ * AccountSetPtr filteredAccountSet = am->filterAccounts(filter);
+ * // connect to AccountSet::accountAdded/accountRemoved signals
+ * QList<AccountPtr> accounts = filteredAccountSet->accounts();
+ * // do something with accounts
+ *
+ * ....
+ * }
+ *
+ * \endcode
+ *
+ * Note that for AccountSet to property work with AccountCapabilityFilter
+ * objects, the feature Account::FeatureCapabilities need to be enabled in all
+ * accounts return by the AccountManager passed as param in the constructor.
+ * The easiest way to do this is to enable AccountManager feature
+ * AccountManager::FeatureFilterByCapabilities.
+ *
+ * AccountSet can also be instantiated directly, but when doing it,
+ * the AccountManager object passed as param in the constructor must be ready
+ * for AccountSet properly work.
+ */
+
+/**
+ * Construct a new AccountSet object.
+ *
+ * \param accountManager An account manager object used to filter accounts.
+ * The account manager object must be ready.
+ * \param filter The desired filter.
+ */
+AccountSet::AccountSet(const AccountManagerPtr &accountManager,
+ const AccountFilterConstPtr &filter)
+ : Object(),
+ mPriv(new Private(this, accountManager, filter))
+{
+}
+
+/**
+ * Construct a new AccountSet object.
+ *
+ * The \a filter must contain Account property names and values as map items.
+ *
+ * \param accountManager An account manager object used to filter accounts.
+ * The account manager object must be ready.
+ * \param filter The desired filter.
+ */
+AccountSet::AccountSet(const AccountManagerPtr &accountManager,
+ const QVariantMap &filter)
+ : Object(),
+ mPriv(new Private(this, accountManager, filter))
+{
+}
+
+/**
+ * Class destructor.
+ */
+AccountSet::~AccountSet()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the account manager object used to filter accounts.
+ *
+ * \return A pointer to the AccountManager object.
+ */
+AccountManagerPtr AccountSet::accountManager() const
+{
+ return mPriv->accountManager;
+}
+
+/**
+ * Return the filter used to filter accounts.
+ *
+ * \return A read-only pointer the AccountFilter object.
+ */
+AccountFilterConstPtr AccountSet::filter() const
+{
+ return mPriv->filter;
+}
+
+/**
+ * Return a list of account objects that match filter.
+ *
+ * Change notification is via the accountAdded() and accountRemoved() signals.
+ *
+ * \return A list of pointers to Account objects.
+ * \sa accountAdded(), accountRemoved()
+ */
+QList<AccountPtr> AccountSet::accounts() const
+{
+ return mPriv->accounts.values();
+}
+
+/**
+ * \fn void AccountSet::accountAdded(const Tp::AccountPtr &account)
+ *
+ * Emitted whenever an account that matches filter is added to
+ * this set.
+ *
+ * \param account The account that was added to this set.
+ * \sa accounts()
+ */
+
+/**
+ * \fn void AccountSet::accountRemoved(const Tp::AccountPtr &account)
+ *
+ * Emitted whenever an account that matches filter is removed
+ * from this set.
+ *
+ * \param account The account that was removed from this set.
+ * \sa accounts()
+ */
+
+void AccountSet::onNewAccount(const AccountPtr &account)
+{
+ mPriv->insertAccount(account);
+}
+
+void AccountSet::onAccountRemoved(const AccountPtr &account)
+{
+ mPriv->removeAccount(account);
+}
+
+void AccountSet::onAccountChanged(const AccountPtr &account)
+{
+ mPriv->filterAccount(account);
+}
+
+} // Tp
diff --git a/TelepathyQt/account-set.h b/TelepathyQt/account-set.h
new file mode 100644
index 00000000..40dd79af
--- /dev/null
+++ b/TelepathyQt/account-set.h
@@ -0,0 +1,79 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_account_set_h_HEADER_GUARD_
+#define _TelepathyQt_account_set_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Filter>
+#include <TelepathyQt/Object>
+#include <TelepathyQt/Types>
+
+#include <QList>
+#include <QString>
+#include <QVariantMap>
+
+namespace Tp
+{
+
+class TP_QT_EXPORT AccountSet : public Object
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(AccountSet)
+ Q_PROPERTY(AccountManagerPtr accountManager READ accountManager)
+ Q_PROPERTY(AccountFilterConstPtr filter READ filter)
+ Q_PROPERTY(QList<AccountPtr> accounts READ accounts)
+
+public:
+ AccountSet(const AccountManagerPtr &accountManager,
+ const AccountFilterConstPtr &filter);
+ AccountSet(const AccountManagerPtr &accountManager,
+ const QVariantMap &filter);
+ virtual ~AccountSet();
+
+ AccountManagerPtr accountManager() const;
+
+ AccountFilterConstPtr filter() const;
+
+ QList<AccountPtr> accounts() const;
+
+Q_SIGNALS:
+ void accountAdded(const Tp::AccountPtr &account);
+ void accountRemoved(const Tp::AccountPtr &account);
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onNewAccount(const Tp::AccountPtr &account);
+ TP_QT_NO_EXPORT void onAccountRemoved(const Tp::AccountPtr &account);
+ TP_QT_NO_EXPORT void onAccountChanged(const Tp::AccountPtr &account);
+
+private:
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/account.cpp b/TelepathyQt/account.cpp
new file mode 100644
index 00000000..e9c864a2
--- /dev/null
+++ b/TelepathyQt/account.cpp
@@ -0,0 +1,4472 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/Account>
+
+#include "TelepathyQt/_gen/account.moc.hpp"
+#include "TelepathyQt/_gen/cli-account.moc.hpp"
+#include "TelepathyQt/_gen/cli-account-body.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include "TelepathyQt/connection-internal.h"
+
+#include <TelepathyQt/AccountManager>
+#include <TelepathyQt/Channel>
+#include <TelepathyQt/ChannelDispatcherInterface>
+#include <TelepathyQt/ConnectionCapabilities>
+#include <TelepathyQt/ConnectionLowlevel>
+#include <TelepathyQt/ConnectionManager>
+#include <TelepathyQt/PendingChannel>
+#include <TelepathyQt/PendingChannelRequest>
+#include <TelepathyQt/PendingFailure>
+#include <TelepathyQt/PendingReady>
+#include <TelepathyQt/PendingStringList>
+#include <TelepathyQt/PendingVariant>
+#include <TelepathyQt/PendingVoid>
+#include <TelepathyQt/Profile>
+#include <TelepathyQt/ReferencedHandles>
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/Debug>
+
+#include <QQueue>
+#include <QRegExp>
+#include <QSharedPointer>
+#include <QTimer>
+#include <QWeakPointer>
+
+#include <string.h>
+
+namespace Tp
+{
+
+namespace
+{
+
+struct PresenceStatusInfo
+{
+ QString name;
+ Tp::SimpleStatusSpec spec;
+};
+
+Tp::ConnectionPresenceType presenceTypeForStatus(const QString &status, bool &maySetOnSelf)
+{
+ static PresenceStatusInfo statuses[] = {
+ { QLatin1String("available"), { Tp::ConnectionPresenceTypeAvailable, true, true } },
+ { QLatin1String("chat"), { Tp::ConnectionPresenceTypeAvailable, true, true } },
+ { QLatin1String("chatty"), { Tp::ConnectionPresenceTypeAvailable, true, true } },
+ { QLatin1String("away"), { Tp::ConnectionPresenceTypeAway, true, true } },
+ { QLatin1String("brb"), { Tp::ConnectionPresenceTypeAway, true, true } },
+ { QLatin1String("out-to-lunch"), { Tp::ConnectionPresenceTypeAway, true, true } },
+ { QLatin1String("xa"), { Tp::ConnectionPresenceTypeExtendedAway, true, true } },
+ { QLatin1String("hidden"), { Tp::ConnectionPresenceTypeHidden, true, true } },
+ { QLatin1String("invisible"), { Tp::ConnectionPresenceTypeHidden, true, true } },
+ { QLatin1String("offline"), { Tp::ConnectionPresenceTypeOffline, true, false } },
+ { QLatin1String("unknown"), { Tp::ConnectionPresenceTypeUnknown, false, false } },
+ { QLatin1String("error"), { Tp::ConnectionPresenceTypeError, false, false } }
+ };
+
+ for (uint i = 0; i < sizeof(statuses) / sizeof(PresenceStatusInfo); ++i) {
+ if (status == statuses[i].name) {
+ maySetOnSelf = statuses[i].spec.maySetOnSelf;
+ return (Tp::ConnectionPresenceType) statuses[i].spec.type;
+ }
+ }
+
+ // fallback to type away if we don't know status
+ maySetOnSelf = true;
+ return Tp::ConnectionPresenceTypeAway;
+}
+
+Tp::PresenceSpec presenceSpecForStatus(const QString &status, bool canHaveStatusMessage)
+{
+ Tp::SimpleStatusSpec spec;
+ spec.type = presenceTypeForStatus(status, spec.maySetOnSelf);
+ spec.canHaveMessage = canHaveStatusMessage;
+ return Tp::PresenceSpec(status, spec);
+}
+
+QVariantMap textChatCommonRequest()
+{
+ QVariantMap request;
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType"),
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_TEXT));
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType"),
+ (uint) Tp::HandleTypeContact);
+ return request;
+}
+
+QVariantMap textChatRequest(const QString &contactIdentifier)
+{
+ QVariantMap request = textChatCommonRequest();
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetID"),
+ contactIdentifier);
+ return request;
+}
+
+QVariantMap textChatRequest(const Tp::ContactPtr &contact)
+{
+ QVariantMap request = textChatCommonRequest();
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandle"),
+ contact ? contact->handle().at(0) : (uint) 0);
+ return request;
+}
+
+QVariantMap textChatroomRequest(const QString &roomName)
+{
+ QVariantMap request;
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType"),
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_TEXT));
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType"),
+ (uint) Tp::HandleTypeRoom);
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetID"),
+ roomName);
+ return request;
+}
+
+QVariantMap streamedMediaCallCommonRequest()
+{
+ QVariantMap request;
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType"),
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA));
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType"),
+ (uint) Tp::HandleTypeContact);
+ return request;
+}
+
+QVariantMap streamedMediaCallRequest(const QString &contactIdentifier)
+{
+ QVariantMap request = streamedMediaCallCommonRequest();
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetID"),
+ contactIdentifier);
+ return request;
+}
+
+QVariantMap streamedMediaCallRequest(const Tp::ContactPtr &contact)
+{
+ QVariantMap request = streamedMediaCallCommonRequest();
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandle"),
+ contact ? contact->handle().at(0) : (uint) 0);
+ return request;
+}
+
+QVariantMap streamedMediaAudioCallRequest(const QString &contactIdentifier)
+{
+ QVariantMap request = streamedMediaCallRequest(contactIdentifier);
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialAudio"),
+ true);
+ return request;
+}
+
+QVariantMap streamedMediaAudioCallRequest(const Tp::ContactPtr &contact)
+{
+ QVariantMap request = streamedMediaCallRequest(contact);
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialAudio"),
+ true);
+ return request;
+}
+
+QVariantMap streamedMediaVideoCallRequest(const QString &contactIdentifier, bool withAudio)
+{
+ QVariantMap request = streamedMediaCallRequest(contactIdentifier);
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialVideo"),
+ true);
+ if (withAudio) {
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialAudio"),
+ true);
+ }
+ return request;
+}
+
+QVariantMap streamedMediaVideoCallRequest(const Tp::ContactPtr &contact, bool withAudio)
+{
+ QVariantMap request = streamedMediaCallRequest(contact);
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialVideo"),
+ true);
+ if (withAudio) {
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialAudio"),
+ true);
+ }
+ return request;
+}
+
+QVariantMap fileTransferCommonRequest(const Tp::FileTransferChannelCreationProperties &properties)
+{
+ if (!properties.isValid()) {
+ warning() << "Invalid file transfer creation properties";
+ return QVariantMap();
+ }
+
+ QVariantMap request;
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType"),
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_FILE_TRANSFER));
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType"),
+ (uint) Tp::HandleTypeContact);
+
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_FILE_TRANSFER ".Filename"),
+ properties.suggestedFileName());
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_FILE_TRANSFER ".ContentType"),
+ properties.contentType());
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_FILE_TRANSFER ".Size"),
+ properties.size());
+
+ if (properties.hasContentHash()) {
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_FILE_TRANSFER ".ContentHashType"),
+ (uint) properties.contentHashType());
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_FILE_TRANSFER ".ContentHash"),
+ properties.contentHash());
+ }
+
+ if (properties.hasDescription()) {
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_FILE_TRANSFER ".Description"),
+ properties.description());
+ }
+
+ if (properties.hasLastModificationTime()) {
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_FILE_TRANSFER ".Date"),
+ (qulonglong) properties.lastModificationTime().toTime_t());
+ }
+
+ if (properties.hasUri()) {
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_FILE_TRANSFER ".URI"),
+ properties.uri());
+ }
+
+ return request;
+}
+
+QVariantMap fileTransferRequest(const QString &contactIdentifier,
+ const Tp::FileTransferChannelCreationProperties &properties)
+{
+ QVariantMap request = fileTransferCommonRequest(properties);
+
+ if (!request.isEmpty()) {
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetID"),
+ contactIdentifier);
+ }
+
+ return request;
+}
+
+QVariantMap fileTransferRequest(const Tp::ContactPtr &contact,
+ const Tp::FileTransferChannelCreationProperties &properties)
+{
+ QVariantMap request = fileTransferCommonRequest(properties);
+
+ if (!request.isEmpty()) {
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandle"),
+ contact ? contact->handle().at(0) : (uint) 0);
+ }
+
+ return request;
+}
+
+QVariantMap streamTubeCommonRequest(const QString &service)
+{
+ QVariantMap request;
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType"),
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAM_TUBE));
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType"),
+ (uint) Tp::HandleTypeContact);
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAM_TUBE ".Service"),
+ service);
+ return request;
+}
+
+QVariantMap streamTubeRequest(const QString &contactIdentifier, const QString &service)
+{
+ QVariantMap request = streamTubeCommonRequest(service);
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetID"),
+ contactIdentifier);
+ return request;
+}
+
+QVariantMap streamTubeRequest(const Tp::ContactPtr &contact, const QString &service)
+{
+ QVariantMap request = streamTubeCommonRequest(service);
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandle"),
+ contact ? contact->handle().at(0) : (uint) 0);
+ return request;
+}
+
+QVariantMap conferenceCommonRequest(const char *channelType, Tp::HandleType targetHandleType,
+ const QList<Tp::ChannelPtr> &channels)
+{
+ QVariantMap request;
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType"),
+ QLatin1String(channelType));
+ if (targetHandleType != Tp::HandleTypeNone) {
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType"),
+ (uint) targetHandleType);
+ }
+
+ Tp::ObjectPathList objectPaths;
+ foreach (const Tp::ChannelPtr &channel, channels) {
+ objectPaths << QDBusObjectPath(channel->objectPath());
+ }
+
+ request.insert(TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE + QLatin1String(".InitialChannels"),
+ qVariantFromValue(objectPaths));
+ return request;
+}
+
+QVariantMap conferenceRequest(const char *channelType, Tp::HandleType targetHandleType,
+ const QList<Tp::ChannelPtr> &channels, const QStringList &initialInviteeContactsIdentifiers)
+{
+ QVariantMap request = conferenceCommonRequest(channelType, targetHandleType, channels);
+ if (!initialInviteeContactsIdentifiers.isEmpty()) {
+ request.insert(TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE + QLatin1String(".InitialInviteeIDs"),
+ initialInviteeContactsIdentifiers);
+ }
+ return request;
+}
+
+QVariantMap conferenceRequest(const char *channelType, Tp::HandleType targetHandleType,
+ const QList<Tp::ChannelPtr> &channels, const QList<Tp::ContactPtr> &initialInviteeContacts)
+{
+ QVariantMap request = conferenceCommonRequest(channelType, targetHandleType, channels);
+ if (!initialInviteeContacts.isEmpty()) {
+ Tp::UIntList handles;
+ foreach (const Tp::ContactPtr &contact, initialInviteeContacts) {
+ if (!contact) {
+ continue;
+ }
+ handles << contact->handle()[0];
+ }
+ if (!handles.isEmpty()) {
+ request.insert(TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE +
+ QLatin1String(".InitialInviteeHandles"), qVariantFromValue(handles));
+ }
+ }
+ return request;
+}
+
+QVariantMap conferenceTextChatRequest(const QList<Tp::ChannelPtr> &channels,
+ const QStringList &initialInviteeContactsIdentifiers)
+{
+ QVariantMap request = conferenceRequest(TELEPATHY_INTERFACE_CHANNEL_TYPE_TEXT,
+ Tp::HandleTypeNone, channels, initialInviteeContactsIdentifiers);
+ return request;
+}
+
+QVariantMap conferenceTextChatRequest(const QList<Tp::ChannelPtr> &channels,
+ const QList<Tp::ContactPtr> &initialInviteeContacts)
+{
+ QVariantMap request = conferenceRequest(TELEPATHY_INTERFACE_CHANNEL_TYPE_TEXT,
+ Tp::HandleTypeNone, channels, initialInviteeContacts);
+ return request;
+}
+
+QVariantMap conferenceTextChatroomRequest(const QString &roomName,
+ const QList<Tp::ChannelPtr> &channels,
+ const QStringList &initialInviteeContactsIdentifiers)
+{
+ QVariantMap request = conferenceRequest(TELEPATHY_INTERFACE_CHANNEL_TYPE_TEXT,
+ Tp::HandleTypeRoom, channels, initialInviteeContactsIdentifiers);
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetID"), roomName);
+ return request;
+}
+
+QVariantMap conferenceTextChatroomRequest(const QString &roomName,
+ const QList<Tp::ChannelPtr> &channels,
+ const QList<Tp::ContactPtr> &initialInviteeContacts)
+{
+ QVariantMap request = conferenceRequest(TELEPATHY_INTERFACE_CHANNEL_TYPE_TEXT,
+ Tp::HandleTypeRoom, channels, initialInviteeContacts);
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetID"), roomName);
+ return request;
+}
+
+QVariantMap conferenceStreamedMediaCallRequest(const QList<Tp::ChannelPtr> &channels,
+ const QStringList &initialInviteeContactsIdentifiers)
+{
+ QVariantMap request = conferenceRequest(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA,
+ Tp::HandleTypeNone, channels, initialInviteeContactsIdentifiers);
+ return request;
+}
+
+QVariantMap conferenceStreamedMediaCallRequest(const QList<Tp::ChannelPtr> &channels,
+ const QList<Tp::ContactPtr> &initialInviteeContacts)
+{
+ QVariantMap request = conferenceRequest(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA,
+ Tp::HandleTypeNone, channels, initialInviteeContacts);
+ return request;
+}
+
+QVariantMap contactSearchRequest(const ConnectionCapabilities &capabilities,
+ const QString &server, uint limit)
+{
+ QVariantMap request;
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType"),
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_CONTACT_SEARCH));
+ if (capabilities.contactSearchesWithSpecificServer()) {
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_CONTACT_SEARCH ".Server"),
+ server);
+ } else if (!server.isEmpty()) {
+ warning() << "Ignoring Server parameter for contact search, since the protocol does not support it.";
+ }
+ if (capabilities.contactSearchesWithLimit()) {
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_CONTACT_SEARCH ".Limit"), limit);
+ } else if (limit > 0) {
+ warning() << "Ignoring Limit parameter for contact search, since the protocol does not support it.";
+ }
+ return request;
+}
+
+} // anonymous namespace
+
+struct TP_QT_NO_EXPORT Account::Private
+{
+ Private(Account *parent, const ConnectionFactoryConstPtr &connFactory,
+ const ChannelFactoryConstPtr &chanFactory,
+ const ContactFactoryConstPtr &contactFactory);
+ ~Private();
+
+ void init();
+
+ static void introspectMain(Private *self);
+ static void introspectAvatar(Private *self);
+ static void introspectProtocolInfo(Private *self);
+ static void introspectCapabilities(Private *self);
+
+ void updateProperties(const QVariantMap &props);
+ void retrieveAvatar();
+ bool processConnQueue();
+
+ bool checkCapabilitiesChanged(bool profileChanged);
+
+ QString connectionObjectPath() const;
+
+ // Public object
+ Account *parent;
+
+ // Factories
+ ConnectionFactoryConstPtr connFactory;
+ ChannelFactoryConstPtr chanFactory;
+ ContactFactoryConstPtr contactFactory;
+
+ // Instance of generated interface class
+ Client::AccountInterface *baseInterface;
+
+ // Mandatory properties interface proxy
+ Client::DBus::PropertiesInterface *properties;
+
+ ReadinessHelper *readinessHelper;
+
+ // Introspection
+ QVariantMap parameters;
+ bool valid;
+ bool enabled;
+ bool connectsAutomatically;
+ bool hasBeenOnline;
+ bool changingPresence;
+ QString cmName;
+ QString protocolName;
+ QString serviceName;
+ ProfilePtr profile;
+ QString displayName;
+ QString nickname;
+ QString iconName;
+ QQueue<QString> connObjPathQueue;
+ ConnectionPtr connection;
+ bool mayFinishCore, coreFinished;
+ QString normalizedName;
+ Avatar avatar;
+ ConnectionManagerPtr cm;
+ ConnectionStatus connectionStatus;
+ ConnectionStatusReason connectionStatusReason;
+ QString connectionError;
+ Connection::ErrorDetails connectionErrorDetails;
+ Presence automaticPresence;
+ Presence currentPresence;
+ Presence requestedPresence;
+ bool usingConnectionCaps;
+ ConnectionCapabilities customCaps;
+
+ // The contexts should never be removed from the map, to guarantee O(1) CD introspections per bus
+ struct DispatcherContext;
+ static QMap<QString, QSharedPointer<DispatcherContext> > dispatcherContexts;
+ QSharedPointer<DispatcherContext> dispatcherContext;
+};
+
+struct Account::Private::DispatcherContext
+{
+ DispatcherContext(const QDBusConnection &bus)
+ : iface(new Client::ChannelDispatcherInterface(bus, TP_QT_CHANNEL_DISPATCHER_BUS_NAME, TP_QT_CHANNEL_DISPATCHER_OBJECT_PATH)),
+ introspected(false), supportsHints(false)
+ {
+ }
+
+ ~DispatcherContext()
+ {
+ delete iface;
+ }
+
+ Client::ChannelDispatcherInterface *iface;
+
+ bool introspected, supportsHints;
+ QWeakPointer<PendingVariant> introspectOp;
+
+private:
+ DispatcherContext(const DispatcherContext &);
+ void operator=(const DispatcherContext &);
+};
+
+Account::Private::Private(Account *parent, const ConnectionFactoryConstPtr &connFactory,
+ const ChannelFactoryConstPtr &chanFactory, const ContactFactoryConstPtr &contactFactory)
+ : parent(parent),
+ connFactory(connFactory),
+ chanFactory(chanFactory),
+ contactFactory(contactFactory),
+ baseInterface(new Client::AccountInterface(parent)),
+ properties(parent->interface<Client::DBus::PropertiesInterface>()),
+ readinessHelper(parent->readinessHelper()),
+ valid(false),
+ enabled(false),
+ connectsAutomatically(false),
+ hasBeenOnline(false),
+ changingPresence(false),
+ mayFinishCore(false),
+ coreFinished(false),
+ connectionStatus(ConnectionStatusDisconnected),
+ connectionStatusReason(ConnectionStatusReasonNoneSpecified),
+ usingConnectionCaps(false),
+ dispatcherContext(dispatcherContexts.value(parent->dbusConnection().name()))
+{
+ // FIXME: QRegExp probably isn't the most efficient possible way to parse
+ // this :-)
+ QRegExp rx(QLatin1String("^") + TP_QT_ACCOUNT_OBJECT_PATH_BASE +
+ QLatin1String("/([_A-Za-z][_A-Za-z0-9]*)" // cap(1) is the CM
+ "/([_A-Za-z][_A-Za-z0-9]*)" // cap(2) is the protocol
+ "/([_A-Za-z][_A-Za-z0-9]*)" // account-specific part
+ ));
+
+ if (rx.exactMatch(parent->objectPath())) {
+ cmName = rx.cap(1);
+ protocolName = rx.cap(2).replace(QLatin1Char('_'), QLatin1Char('-'));
+ } else {
+ warning() << "Account object path is not spec-compliant, "
+ "trying again with a different account-specific part check";
+
+ rx = QRegExp(QLatin1String("^") + TP_QT_ACCOUNT_OBJECT_PATH_BASE +
+ QLatin1String("/([_A-Za-z][_A-Za-z0-9]*)" // cap(1) is the CM
+ "/([_A-Za-z][_A-Za-z0-9]*)" // cap(2) is the protocol
+ "/([_A-Za-z0-9]*)" // account-specific part
+ ));
+ if (rx.exactMatch(parent->objectPath())) {
+ cmName = rx.cap(1);
+ protocolName = rx.cap(2).replace(QLatin1Char('_'), QLatin1Char('-'));
+ } else {
+ warning() << "Not a valid Account object path:" <<
+ parent->objectPath();
+ }
+ }
+
+ ReadinessHelper::Introspectables introspectables;
+
+ // As Account does not have predefined statuses let's simulate one (0)
+ ReadinessHelper::Introspectable introspectableCore(
+ QSet<uint>() << 0, // makesSenseForStatuses
+ Features(), // dependsOnFeatures
+ QStringList(), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &Private::introspectMain,
+ this);
+ introspectables[FeatureCore] = introspectableCore;
+
+ ReadinessHelper::Introspectable introspectableAvatar(
+ QSet<uint>() << 0, // makesSenseForStatuses
+ Features() << FeatureCore, // dependsOnFeatures (core)
+ QStringList() << QLatin1String(TELEPATHY_INTERFACE_ACCOUNT_INTERFACE_AVATAR), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &Private::introspectAvatar,
+ this);
+ introspectables[FeatureAvatar] = introspectableAvatar;
+
+ ReadinessHelper::Introspectable introspectableProtocolInfo(
+ QSet<uint>() << 0, // makesSenseForStatuses
+ Features() << FeatureCore, // dependsOnFeatures (core)
+ QStringList(), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &Private::introspectProtocolInfo,
+ this);
+ introspectables[FeatureProtocolInfo] = introspectableProtocolInfo;
+
+ ReadinessHelper::Introspectable introspectableCapabilities(
+ QSet<uint>() << 0, // makesSenseForStatuses
+ Features() << FeatureCore << FeatureProtocolInfo << FeatureProfile, // dependsOnFeatures
+ QStringList(), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &Private::introspectCapabilities,
+ this);
+ introspectables[FeatureCapabilities] = introspectableCapabilities;
+
+ readinessHelper->addIntrospectables(introspectables);
+
+ if (connFactory->dbusConnection().name() != parent->dbusConnection().name()) {
+ warning() << " The D-Bus connection in the conn factory is not the proxy connection for"
+ << parent->objectPath();
+ }
+
+ if (chanFactory->dbusConnection().name() != parent->dbusConnection().name()) {
+ warning() << " The D-Bus connection in the channel factory is not the proxy connection for"
+ << parent->objectPath();
+ }
+
+ if (!dispatcherContext) {
+ dispatcherContext = QSharedPointer<DispatcherContext>(new DispatcherContext(parent->dbusConnection()));
+ dispatcherContexts.insert(parent->dbusConnection().name(), dispatcherContext);
+ }
+
+ init();
+}
+
+Account::Private::~Private()
+{
+}
+
+bool Account::Private::checkCapabilitiesChanged(bool profileChanged)
+{
+ /* when the capabilities changed:
+ *
+ * - We were using the connection caps and now we don't have connection or
+ * the connection we have is not connected (changed to CM caps)
+ * - We were using the CM caps and now we have a connected connection
+ * (changed to new connection caps)
+ */
+ bool changed = false;
+
+ if (usingConnectionCaps &&
+ (parent->connection().isNull() ||
+ connection->status() != ConnectionStatusConnected)) {
+ usingConnectionCaps = false;
+ changed = true;
+ } else if (!usingConnectionCaps &&
+ !parent->connection().isNull() &&
+ connection->status() == ConnectionStatusConnected) {
+ usingConnectionCaps = true;
+ changed = true;
+ } else if (!usingConnectionCaps && profileChanged) {
+ changed = true;
+ }
+
+ if (changed && parent->isReady(FeatureCapabilities)) {
+ emit parent->capabilitiesChanged(parent->capabilities());
+ }
+
+ return changed;
+}
+
+QString Account::Private::connectionObjectPath() const
+{
+ return !connection.isNull() ? connection->objectPath() : QString();
+}
+
+QMap<QString, QSharedPointer<Account::Private::DispatcherContext> > Account::Private::dispatcherContexts;
+
+/**
+ * \class Account
+ * \ingroup clientaccount
+ * \headerfile TelepathyQt/account.h <TelepathyQt/Account>
+ *
+ * \brief The Account class represents a Telepathy account.
+ *
+ * The remote object accessor functions on this object (isValidAccount(),
+ * isEnabled(), and so on) don't make any D-Bus calls; instead, they return/use
+ * values cached from a previous introspection run. The introspection process
+ * populates their values in the most efficient way possible based on what the
+ * service implements.
+ *
+ * To avoid unnecessary D-Bus traffic, some accessors only return valid
+ * information after specific features have been enabled.
+ * For instance, to retrieve the account protocol information, it is necessary to
+ * enable the feature Account::FeatureProtocolInfo.
+ * See the individual methods descriptions for more details.
+ *
+ * Account features can be enabled by constructing an AccountFactory and enabling
+ * the desired features, and passing it to AccountManager or ClientRegistrar
+ * when creating them as appropriate. However, if a particular
+ * feature is only ever used in a specific circumstance, such as an user opening
+ * some settings dialog separate from the general view of the application,
+ * features can be later enabled as needed by calling becomeReady() with the additional
+ * features, and waiting for the resulting PendingOperation to finish.
+ *
+ * As an addition to accessors, signals are emitted to indicate that properties have
+ * changed, for example displayNameChanged(), iconNameChanged(), etc.
+ *
+ * Convenience methods to create channels using the channel dispatcher such as
+ * ensureTextChat(), createFileTransfer() are also provided.
+ *
+ * If the account is deleted from the AccountManager, this object
+ * will not be deleted automatically; however, it will emit invalidated()
+ * with error code #TP_QT_ERROR_OBJECT_REMOVED and will cease to
+ * be useful.
+ *
+ * \section account_usage_sec Usage
+ *
+ * \subsection account_create_sec Creating an account object
+ *
+ * The easiest way to create account objects is through AccountManager. One can
+ * just use the AccountManager convenience methods such as
+ * AccountManager::validAccounts() to get a list of account objects representing
+ * valid accounts.
+ *
+ * If you already know the object path, you can just call create().
+ * For example:
+ *
+ * \code AccountPtr acc = Account::create(busName, objectPath); \endcode
+ *
+ * An AccountPtr object is returned, which will automatically keep
+ * track of object lifetime.
+ *
+ * You can also provide a D-Bus connection as a QDBusConnection:
+ *
+ * \code
+ *
+ * AccountPtr acc = Account::create(QDBusConnection::sessionBus(),
+ * busName, objectPath);
+ *
+ * \endcode
+ *
+ * \subsection account_ready_sec Making account ready to use
+ *
+ * An Account object needs to become ready before usage, meaning that the
+ * introspection process finished and the object accessors can be used.
+ *
+ * To make the object ready, use becomeReady() and wait for the
+ * PendingOperation::finished() signal to be emitted.
+ *
+ * \code
+ *
+ * class MyClass : public QObject
+ * {
+ * QOBJECT
+ *
+ * public:
+ * MyClass(QObject *parent = 0);
+ * ~MyClass() { }
+ *
+ * private Q_SLOTS:
+ * void onAccountReady(Tp::PendingOperation*);
+ *
+ * private:
+ * AccountPtr acc;
+ * };
+ *
+ * MyClass::MyClass(const QString &busName, const QString &objectPath,
+ * QObject *parent)
+ * : QObject(parent)
+ * acc(Account::create(busName, objectPath))
+ * {
+ * connect(acc->becomeReady(),
+ * SIGNAL(finished(Tp::PendingOperation*)),
+ * SLOT(onAccountReady(Tp::PendingOperation*)));
+ * }
+ *
+ * void MyClass::onAccountReady(Tp::PendingOperation *op)
+ * {
+ * if (op->isError()) {
+ * qWarning() << "Account cannot become ready:" <<
+ * op->errorName() << "-" << op->errorMessage();
+ * return;
+ * }
+ *
+ * // Account is now ready
+ * qDebug() << "Display name:" << acc->displayName();
+ * }
+ *
+ * \endcode
+ *
+ * See \ref async_model, \ref shared_ptr
+ */
+
+/**
+ * Feature representing the core that needs to become ready to make the Account
+ * object usable.
+ *
+ * Note that this feature must be enabled in order to use most Account methods.
+ * See specific methods documentation for more details.
+ *
+ * When calling isReady(), becomeReady(), this feature is implicitly added
+ * to the requested features.
+ */
+const Feature Account::FeatureCore = Feature(QLatin1String(Account::staticMetaObject.className()), 0, true);
+
+/**
+ * Feature used in order to access account avatar info.
+ *
+ * See avatar specific methods' documentation for more details.
+ *
+ * \sa avatar(), avatarChanged()
+ */
+const Feature Account::FeatureAvatar = Feature(QLatin1String(Account::staticMetaObject.className()), 1);
+
+/**
+ * Feature used in order to access account protocol info.
+ *
+ * See protocol info specific methods' documentation for more details.
+ *
+ * \sa protocolInfo()
+ */
+const Feature Account::FeatureProtocolInfo = Feature(QLatin1String(Account::staticMetaObject.className()), 2);
+
+/**
+ * Feature used in order to access account capabilities.
+ *
+ * Enabling this feature will also enable FeatureProtocolInfo and FeatureProfile.
+ *
+ * See capabilities specific methods' documentation for more details.
+ *
+ * \sa capabilities(), capabilitiesChanged()
+ */
+const Feature Account::FeatureCapabilities = Feature(QLatin1String(Account::staticMetaObject.className()), 3);
+
+/**
+ * Feature used in order to access account profile info.
+ *
+ * See profile specific methods' documentation for more details.
+ *
+ * \sa profile(), profileChanged()
+ */
+const Feature Account::FeatureProfile = FeatureProtocolInfo;
+// FeatureProfile is the same as FeatureProtocolInfo for now, as it only needs
+// the protocol info, cm name and protocol name to build a fake profile. Make it
+// a full-featured feature if needed later.
+
+/**
+ * Create a new Account object using QDBusConnection::sessionBus() and the given factories.
+ *
+ * A warning is printed if the factories are not for QDBusConnection::sessionBus().
+ *
+ * \param busName The account well-known bus name (sometimes called a "service
+ * name"). This is usually the same as the account manager
+ * bus name #TP_QT_ACCOUNT_MANAGER_BUS_NAME.
+ * \param objectPath The account object path.
+ * \param connectionFactory The connection factory to use.
+ * \param channelFactory The channel factory to use.
+ * \param contactFactory The contact factory to use.
+ * \return An AccountPtr object pointing to the newly created Account object.
+ */
+AccountPtr Account::create(const QString &busName, const QString &objectPath,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory)
+{
+ return AccountPtr(new Account(QDBusConnection::sessionBus(), busName, objectPath,
+ connectionFactory, channelFactory, contactFactory, Account::FeatureCore));
+}
+
+/**
+ * Create a new Account object using the given \a bus and the given factories.
+ *
+ * A warning is printed if the factories are not for \a bus.
+ *
+ * \param bus QDBusConnection to use.
+ * \param busName The account well-known bus name (sometimes called a "service
+ * name"). This is usually the same as the account manager
+ * bus name #TP_QT_ACCOUNT_MANAGER_BUS_NAME.
+ * \param objectPath The account object path.
+ * \param connectionFactory The connection factory to use.
+ * \param channelFactory The channel factory to use.
+ * \param contactFactory The contact factory to use.
+ * \return An AccountPtr object pointing to the newly created Account object.
+ */
+AccountPtr Account::create(const QDBusConnection &bus,
+ const QString &busName, const QString &objectPath,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory)
+{
+ return AccountPtr(new Account(bus, busName, objectPath, connectionFactory, channelFactory,
+ contactFactory, Account::FeatureCore));
+}
+
+/**
+ * Construct a new Account object using the given \a bus and the given factories.
+ *
+ * A warning is printed if the factories are not for \a bus.
+ *
+ * \param bus QDBusConnection to use.
+ * \param busName The account well-known bus name (sometimes called a "service
+ * name"). This is usually the same as the account manager
+ * bus name #TP_QT_ACCOUNT_MANAGER_BUS_NAME.
+ * \param objectPath The account object path.
+ * \param connectionFactory The connection factory to use.
+ * \param channelFactory The channel factory to use.
+ * \param contactFactory The contact factory to use.
+ * \param coreFeature The core feature of the Account subclass. The corresponding introspectable
+ * should depend on Account::FeatureCore.
+ */
+Account::Account(const QDBusConnection &bus,
+ const QString &busName, const QString &objectPath,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory,
+ const Feature &coreFeature)
+ : StatelessDBusProxy(bus, busName, objectPath, coreFeature),
+ OptionalInterfaceFactory<Account>(this),
+ mPriv(new Private(this, connectionFactory, channelFactory, contactFactory))
+{
+}
+
+/**
+ * Class destructor.
+ */
+Account::~Account()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the connection factory used by this account.
+ *
+ * Only read access is provided. This allows constructing object instances and examining the object
+ * construction settings, but not changing settings. Allowing changes would lead to tricky
+ * situations where objects constructed at different times by the account would have unpredictably
+ * different construction settings (eg. subclass).
+ *
+ * \return A read-only pointer to the ConnectionFactory object.
+ */
+ConnectionFactoryConstPtr Account::connectionFactory() const
+{
+ return mPriv->connFactory;
+}
+
+/**
+ * Return the channel factory used by this account.
+ *
+ * Only read access is provided. This allows constructing object instances and examining the object
+ * construction settings, but not changing settings. Allowing changes would lead to tricky
+ * situations where objects constructed at different times by the account would have unpredictably
+ * different construction settings (eg. subclass).
+ *
+ * \return A read-only pointer to the ChannelFactory object.
+ */
+ChannelFactoryConstPtr Account::channelFactory() const
+{
+ return mPriv->chanFactory;
+}
+
+/**
+ * Return the contact factory used by this account.
+ *
+ * Only read access is provided. This allows constructing object instances and examining the object
+ * construction settings, but not changing settings. Allowing changes would lead to tricky
+ * situations where objects constructed at different times by the account would have unpredictably
+ * different construction settings (eg. subclass).
+ *
+ * \return A read-only pointer to the ContactFactory object.
+ */
+ContactFactoryConstPtr Account::contactFactory() const
+{
+ return mPriv->contactFactory;
+}
+
+/**
+ * Return whether this account is valid.
+ *
+ * If \c true, this account is considered by the account manager to be complete
+ * and usable. If \c false, user action is required to make it usable, and it will
+ * never attempt to connect. For instance, this might be caused by the absence
+ * or misconfiguration of a required parameter, in which case updateParameters()
+ * may be used to properly set the parameters values.
+ *
+ * Change notification is via the validityChanged() signal.
+ *
+ * This method requires Account::FeatureCore to be ready.
+ *
+ * \return \c true if valid, \c false otherwise.
+ * \sa validityChanged(), updateParameters()
+ */
+bool Account::isValidAccount() const
+{
+ return mPriv->valid;
+}
+
+/**
+ * Return whether this account is enabled.
+ *
+ * Change notification is via the stateChanged() signal.
+ *
+ * This method requires Account::FeatureCore to be ready.
+ *
+ * \return \c true if enabled, \c false otherwise.
+ * \sa stateChanged(), setEnabled()
+ */
+bool Account::isEnabled() const
+{
+ return mPriv->enabled;
+}
+
+/**
+ * Set whether this account should be enabled or disabled.
+ *
+ * This gives users the possibility to prevent an account from
+ * being used.
+ *
+ * Note that changing this property won't change the validity of the account.
+ *
+ * \param value Whether this account should be enabled or disabled.
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the request has been made.
+ * \sa stateChanged(), isEnabled()
+ */
+PendingOperation *Account::setEnabled(bool value)
+{
+ return new PendingVoid(
+ mPriv->properties->Set(
+ QLatin1String(TELEPATHY_INTERFACE_ACCOUNT),
+ QLatin1String("Enabled"),
+ QDBusVariant(value)),
+ AccountPtr(this));
+}
+
+/**
+ * Return the connection manager name of this account.
+ *
+ * \return The connection manager name.
+ */
+QString Account::cmName() const
+{
+ return mPriv->cmName;
+}
+
+/**
+ * Return the protocol name of this account.
+ *
+ * \return The protocol name.
+ */
+QString Account::protocolName() const
+{
+ return mPriv->protocolName;
+}
+
+/**
+ * Return the service name of this account.
+ *
+ * Note that this method will fallback to protocolName() if service name
+ * is not known.
+ *
+ * Change notification is via the serviceNameChanged() signal.
+ *
+ * This method requires Account::FeatureCore to be ready.
+ *
+ * \return The service name.
+ * \sa serviceNameChanged(), setServiceName(), protocolName()
+ */
+QString Account::serviceName() const
+{
+ if (mPriv->serviceName.isEmpty()) {
+ return mPriv->protocolName;
+ }
+ return mPriv->serviceName;
+}
+
+/**
+ * Set the service name of this account.
+ *
+ * Some protocols, like XMPP and SIP, are used by various different user-recognised brands,
+ * such as Google Talk. On accounts for such services, this method can be used
+ * to set the name describing the service, which must consist only of ASCII letters, numbers and
+ * hyphen/minus signs, and start with a letter.
+ * For the jabber protocol, one of the following service names should be used if possible:
+ *
+ * google-talk (for Google's IM service)
+ * facebook (for Facebook's IM service)
+ * lj-talk (for LiveJournal's IM service)
+ *
+ * The service name may also be set, if appropriate, when creating the account. See
+ * AccountManager::createAccount() for more details.
+ *
+ * Note that changing this property may also change the profile() used by this account,
+ * in which case profileChanged() will be emitted in addition to serviceNameChanged(), if
+ * Account::FeatureProfile is enabled.
+ *
+ * \param value The service name of this account.
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the request has been made.
+ * \sa serviceNameChanged(), serviceName()
+ */
+PendingOperation *Account::setServiceName(const QString &value)
+{
+ return new PendingVoid(
+ mPriv->properties->Set(
+ QLatin1String(TELEPATHY_INTERFACE_ACCOUNT),
+ QLatin1String("Service"),
+ QDBusVariant(value)),
+ AccountPtr(this));
+}
+
+/**
+ * Return the profile used by this account.
+ *
+ * Profiles are intended to describe variants of the basic protocols supported by Telepathy
+ * connection managers.
+ * An example of this would be Google Talk vs Facebook chat vs Jabber/XMPP. Google Talk is a
+ * specific case of XMPP with well-known capabilities, quirks and settings, and Facebook chat is
+ * a subset of the standard XMPP offering.
+ *
+ * This method will return the profile for this account based on the service used by it.
+ *
+ * Note that if a profile for serviceName() is not available, a fake profile
+ * (Profile::isFake() is \c true) will be returned in case protocolInfo() is valid.
+ *
+ * The fake profile will contain the following info:
+ * - Profile::type() will return "IM"
+ * - Profile::provider() will return an empty string
+ * - Profile::serviceName() will return "cmName()-serviceName()"
+ * - Profile::name() and Profile::protocolName() will return protocolName()
+ * - Profile::iconName() will return "im-protocolName()"
+ * - Profile::cmName() will return cmName()
+ * - Profile::parameters() will return a list matching CM default parameters for protocol with name
+ * protocolName()
+ * - Profile::presences() will return an empty list and
+ * Profile::allowOtherPresences() will return \c true, meaning that CM
+ * presences should be used
+ * - Profile::unsupportedChannelClassSpecs() will return an empty list
+ *
+ * Change notification is via the profileChanged() signal.
+ *
+ * This method requires Account::FeatureProfile to be ready.
+ *
+ * \return A pointer to the Profile object.
+ * \sa profileChanged(), serviceName()
+ */
+ProfilePtr Account::profile() const
+{
+ if (!isReady(FeatureProfile)) {
+ warning() << "Account::profile() requires Account::FeatureProfile to be ready";
+ return ProfilePtr();
+ }
+
+ if (!mPriv->profile) {
+ mPriv->profile = Profile::createForServiceName(serviceName());
+ if (!mPriv->profile->isValid()) {
+ if (protocolInfo().isValid()) {
+ mPriv->profile = ProfilePtr(new Profile(
+ QString(QLatin1String("%1-%2")).arg(mPriv->cmName).arg(serviceName()),
+ mPriv->cmName,
+ mPriv->protocolName,
+ protocolInfo()));
+ } else {
+ warning() << "Cannot create profile as neither a .profile is installed for service" <<
+ serviceName() << "nor protocol info can be retrieved";
+ }
+ }
+ }
+ return mPriv->profile;
+}
+
+/**
+ * Return the display name of this account.
+ *
+ * Change notification is via the displayNameChanged() signal.
+ *
+ * This method requires Account::FeatureCore to be ready.
+ *
+ * \return The display name.
+ * \sa displayNameChanged(), setDisplayName()
+ */
+QString Account::displayName() const
+{
+ return mPriv->displayName;
+}
+
+/**
+ * Set the display name of this account.
+ *
+ * The display name is the user-visible name of this account.
+ * This is usually chosen by the user at account creation time.
+ * See AccountManager::createAccount() for more details.
+ *
+ * \param value The display name of this account.
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the request has been made.
+ * \sa displayNameChanged(), displayName()
+ */
+PendingOperation *Account::setDisplayName(const QString &value)
+{
+ return new PendingVoid(
+ mPriv->properties->Set(
+ QLatin1String(TELEPATHY_INTERFACE_ACCOUNT),
+ QLatin1String("DisplayName"),
+ QDBusVariant(value)),
+ AccountPtr(this));
+}
+
+/**
+ * Return the icon name of this account.
+ *
+ * If the account has no icon, and Account::FeatureProfile is enabled, the icon from the result of
+ * profile() will be used.
+ *
+ * If neither the account nor the profile has an icon, and Account::FeatureProtocolInfo is
+ * enabled, the icon from protocolInfo() will be used if set.
+ *
+ * As a last resort, "im-" + protocolName() will be returned.
+ *
+ * This matches the fallbacks recommended by the \telepathy_spec.
+ *
+ * Change notification is via the iconNameChanged() signal.
+ *
+ * This method requires Account::FeatureCore to be ready.
+ *
+ * \return The icon name.
+ * \sa iconNameChanged(), setIconName()
+ */
+QString Account::iconName() const
+{
+ if (mPriv->iconName.isEmpty()) {
+ if (isReady(FeatureProfile)) {
+ ProfilePtr pr = profile();
+ if (pr && pr->isValid()) {
+ QString iconName = pr->iconName();
+ if (!iconName.isEmpty()) {
+ return iconName;
+ }
+ }
+ }
+
+ if (isReady(FeatureProtocolInfo) && protocolInfo().isValid()) {
+ return protocolInfo().iconName();
+ }
+
+ return QString(QLatin1String("im-%1")).arg(protocolName());
+ }
+
+ return mPriv->iconName;
+}
+
+/**
+ * Set the icon name of this account.
+ *
+ * \param value The icon name of this account.
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the request has been made.
+ * \sa iconNameChanged(), iconName()
+ */
+PendingOperation *Account::setIconName(const QString &value)
+{
+ return new PendingVoid(
+ mPriv->properties->Set(
+ QLatin1String(TELEPATHY_INTERFACE_ACCOUNT),
+ QLatin1String("Icon"),
+ QDBusVariant(value)),
+ AccountPtr(this));
+}
+
+/**
+ * Return the nickname of this account.
+ *
+ * Change notification is via the nicknameChanged() signal.
+ *
+ * This method requires Account::FeatureCore to be ready.
+ *
+ * \return The nickname.
+ * \sa nicknameChanged(), setNickname()
+ */
+QString Account::nickname() const
+{
+ return mPriv->nickname;
+}
+
+/**
+ * Set the nickname of this account as seen to other contacts.
+ *
+ * \param value The nickname of this account.
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the request has been made.
+ * \sa nicknameChanged(), nickname()
+ */
+PendingOperation *Account::setNickname(const QString &value)
+{
+ return new PendingVoid(
+ mPriv->properties->Set(
+ QLatin1String(TELEPATHY_INTERFACE_ACCOUNT),
+ QLatin1String("Nickname"),
+ QDBusVariant(value)),
+ AccountPtr(this));
+}
+
+/**
+ * Return the avatar requirements (size limits, supported MIME types, etc)
+ * for avatars passed to setAvatar() on this account.
+ *
+ * For now this method will only return the avatar requirements found in protocolInfo() if
+ * Account::FeatureProtocolInfo is ready, otherwise an invalid AvatarSpec instance is returned.
+ *
+ * \return The requirements as an AvatarSpec object.
+ * \sa avatar(), setAvatar()
+ */
+AvatarSpec Account::avatarRequirements() const
+{
+ // TODO Once connection has support for avatar requirements use it if the connection is usable
+ ProtocolInfo pi = protocolInfo();
+ if (pi.isValid()) {
+ return pi.avatarRequirements();
+ }
+ return AvatarSpec();
+}
+
+/**
+ * Return the avatar of this account.
+ *
+ * Change notification is via the avatarChanged() signal.
+ *
+ * This method requires Account::FeatureAvatar to be ready.
+ *
+ * \return The avatar as an Avatar object.
+ * \sa avatarChanged(), setAvatar()
+ */
+const Avatar &Account::avatar() const
+{
+ if (!isReady(Features() << FeatureAvatar)) {
+ warning() << "Trying to retrieve avatar from account, but "
+ "avatar is not supported or was not requested. "
+ "Use becomeReady(FeatureAvatar)";
+ }
+
+ return mPriv->avatar;
+}
+
+/**
+ * Set avatar of this account as seen to other contacts.
+ *
+ * Note that \a avatar must match the requirements as returned by avatarRequirements().
+ *
+ * \param avatar The avatar of this account.
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the request has been made.
+ * \sa avatarChanged(), avatar(), avatarRequirements()
+ */
+PendingOperation *Account::setAvatar(const Avatar &avatar)
+{
+ if (!interfaces().contains(QLatin1String(TELEPATHY_INTERFACE_ACCOUNT_INTERFACE_AVATAR))) {
+ return new PendingFailure(
+ QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("Account does not support Avatar"),
+ AccountPtr(this));
+ }
+
+ return new PendingVoid(
+ mPriv->properties->Set(
+ QLatin1String(TELEPATHY_INTERFACE_ACCOUNT_INTERFACE_AVATAR),
+ QLatin1String("Avatar"),
+ QDBusVariant(QVariant::fromValue(avatar))),
+ AccountPtr(this));
+}
+
+/**
+ * Return the parameters of this account.
+ *
+ * The account parameters are represented as a map from connection manager parameter names
+ * to their values.
+ *
+ * Change notification is via the parametersChanged() signal.
+ *
+ * This method requires Account::FeatureCore to be ready.
+ *
+ * \return The parameters as QVariantMap.
+ * \sa parametersChanged(), updateParameters()
+ */
+QVariantMap Account::parameters() const
+{
+ return mPriv->parameters;
+}
+
+/**
+ * Update this account parameters.
+ *
+ * On success, the PendingOperation returned by this method will produce a
+ * list of strings, which are the names of parameters whose changes will not
+ * take effect until the account is disconnected and reconnected (for instance
+ * by calling reconnect()).
+ *
+ * \param set Parameters to set.
+ * \param unset Parameters to unset.
+ * \return A PendingStringList which will emit PendingStringList::finished
+ * when the request has been made
+ * \sa parametersChanged(), parameters(), reconnect()
+ */
+PendingStringList *Account::updateParameters(const QVariantMap &set,
+ const QStringList &unset)
+{
+ return new PendingStringList(
+ baseInterface()->UpdateParameters(set, unset),
+ AccountPtr(this));
+}
+
+/**
+ * Return the protocol info of this account protocol.
+ *
+ * This method requires Account::FeatureProtocolInfo to be ready.
+ *
+ * \return The protocol info as a ProtocolInfo object.
+ */
+ProtocolInfo Account::protocolInfo() const
+{
+ if (!isReady(Features() << FeatureProtocolInfo)) {
+ warning() << "Trying to retrieve protocol info from account, but "
+ "protocol info is not supported or was not requested. "
+ "Use becomeReady(FeatureProtocolInfo)";
+ return ProtocolInfo();
+ }
+
+ return mPriv->cm->protocol(mPriv->protocolName);
+}
+
+/**
+ * Return the capabilities for this account.
+ *
+ * Note that this method will return the connection() capabilities if the
+ * account is online and ready. If the account is disconnected, it will fallback
+ * to return the subtraction of the protocolInfo() capabilities and the profile() unsupported
+ * capabilities.
+ *
+ * Change notification is via the capabilitiesChanged() signal.
+ *
+ * This method requires Account::FeatureCapabilities to be ready.
+ *
+ * \return The capabilities as a ConnectionCapabilities object.
+ * \sa capabilitiesChanged(), protocolInfo(), profile()
+ */
+ConnectionCapabilities Account::capabilities() const
+{
+ if (!isReady(FeatureCapabilities)) {
+ warning() << "Trying to retrieve capabilities from account, but "
+ "FeatureCapabilities was not requested. "
+ "Use becomeReady(FeatureCapabilities)";
+ return ConnectionCapabilities();
+ }
+
+ // if the connection is online and ready use its caps
+ if (mPriv->connection &&
+ mPriv->connection->status() == ConnectionStatusConnected) {
+ return mPriv->connection->capabilities();
+ }
+
+ // if we are here it means FeatureProtocolInfo and FeatureProfile are ready, as
+ // FeatureCapabilities depend on them, so let's use the subtraction of protocol info caps rccs
+ // and profile unsupported rccs.
+ //
+ // However, if we failed to introspect the CM (eg. this is a test), then let's not try to use
+ // the protocolInfo because it'll be NULL! Profile may also be NULL in case a .profile for
+ // serviceName() is not present and protocolInfo is NULL.
+ ProtocolInfo pi = protocolInfo();
+ if (!pi.isValid()) {
+ return ConnectionCapabilities();
+ }
+ ProfilePtr pr;
+ if (isReady(FeatureProfile)) {
+ pr = profile();
+ }
+ if (!pr || !pr->isValid()) {
+ return pi.capabilities();
+ }
+
+ RequestableChannelClassSpecList piClassSpecs = pi.capabilities().allClassSpecs();
+ RequestableChannelClassSpecList prUnsupportedClassSpecs = pr->unsupportedChannelClassSpecs();
+ RequestableChannelClassSpecList classSpecs;
+ bool unsupported;
+ foreach (const RequestableChannelClassSpec &piClassSpec, piClassSpecs) {
+ unsupported = false;
+ foreach (const RequestableChannelClassSpec &prUnsuportedClassSpec, prUnsupportedClassSpecs) {
+ // Here we check the following:
+ // - If the unsupported spec has no allowed property it means it does not support any
+ // class whose fixed properties match.
+ // E.g: Doesn't support any media calls, be it audio or video.
+ // - If the unsupported spec has allowed properties it means it does not support a
+ // specific class whose fixed properties and allowed properties should match.
+ // E.g: Doesn't support video calls but does support audio calls.
+ if (prUnsuportedClassSpec.allowedProperties().isEmpty()) {
+ if (piClassSpec.fixedProperties() == prUnsuportedClassSpec.fixedProperties()) {
+ unsupported = true;
+ break;
+ }
+ } else {
+ if (piClassSpec == prUnsuportedClassSpec) {
+ unsupported = true;
+ break;
+ }
+ }
+ }
+ if (!unsupported) {
+ classSpecs.append(piClassSpec);
+ }
+ }
+ mPriv->customCaps = ConnectionCapabilities(classSpecs);
+ return mPriv->customCaps;
+}
+
+/**
+ * Return whether this account should be put online automatically whenever
+ * possible.
+ *
+ * Change notification is via the connectsAutomaticallyPropertyChanged() signal.
+ *
+ * This method requires Account::FeatureCore to be ready.
+ *
+ * \return \c true if it should try to connect automatically, \c false
+ * otherwise.
+ * \sa connectsAutomaticallyPropertyChanged(), setConnectsAutomatically()
+ */
+bool Account::connectsAutomatically() const
+{
+ return mPriv->connectsAutomatically;
+}
+
+/**
+ * Set whether this account should be put online automatically whenever
+ * possible.
+ *
+ * \param value Value indicating if this account should be put online whenever
+ * possible.
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the request has been made.
+ * \sa connectsAutomaticallyPropertyChanged(), connectsAutomatically()
+ */
+PendingOperation *Account::setConnectsAutomatically(bool value)
+{
+ return new PendingVoid(
+ mPriv->properties->Set(
+ QLatin1String(TELEPATHY_INTERFACE_ACCOUNT),
+ QLatin1String("ConnectAutomatically"),
+ QDBusVariant(value)),
+ AccountPtr(this));
+}
+
+/**
+ * Return whether this account has ever been put online successfully.
+ *
+ * This property cannot change from \c true to \c false, only from \c false to \c true.
+ * When the account successfully goes online for the first time, or when it
+ * is detected that this has already happened, the firstOnline() signal is
+ * emitted.
+ *
+ * This method requires Account::FeatureCore to be ready.
+ *
+ * \return \c true if ever been online, \c false otherwise.
+ */
+bool Account::hasBeenOnline() const
+{
+ return mPriv->hasBeenOnline;
+}
+
+/**
+ * Return the status of this account connection.
+ *
+ * Note that this method may return a different value from the one returned by Connection::status()
+ * on this account connection. This happens because this value will change whenever the connection
+ * status of this account connection changes and won't consider the Connection introspection
+ * process. The same rationale also applies to connectionStatusReason() and
+ * connectionErrorDetails().
+ *
+ * A valid use case for this is for account creation UIs that wish to display the accounts
+ * connection status and nothing else on the connections (e.g. retrieve the contact list).
+ *
+ * Change notification is via the connectionStatusChanged() signal.
+ *
+ * This method requires Account::FeatureCore to be ready.
+ *
+ * \return The connection status as #ConnectionStatus.
+ * \sa connectionStatusChanged(), connectionStatusReason(), connectionError(),
+ * connectionErrorDetails()
+ */
+ConnectionStatus Account::connectionStatus() const
+{
+ return mPriv->connectionStatus;
+}
+
+/**
+ * Return the reason for this account connection status.
+ *
+ * This represents the reason for the last change to connectionStatus().
+ *
+ * Note that this method may return a different value from the one returned by
+ * Connection::statusReason() on this account connection.
+ * See connectionStatus() for more details.
+ *
+ * This method requires Account::FeatureCore to be ready.
+ *
+ * \return The connection status reason as #ConnectionStatusReason.
+ * \sa connectionStatusChanged(), connectionStatus(), connectionError(), connectionErrorDetails()
+ */
+ConnectionStatusReason Account::connectionStatusReason() const
+{
+ return mPriv->connectionStatusReason;
+}
+
+/**
+ * Return the D-Bus error name for the last disconnection or connection failure,
+ * (in particular, #TP_QT_ERROR_CANCELLED if it was disconnected by user
+ * request), or an empty string if the account is connected.
+ *
+ * This method requires Account::FeatureCore to be ready.
+ *
+ * \return The D-Bus error name for the last disconnection or connection failure.
+ * \sa connectionErrorDetails(), connectionStatus(), connectionStatusReason(),
+ * connectionStatusChanged()
+ */
+QString Account::connectionError() const
+{
+ return mPriv->connectionError;
+}
+
+/**
+ * Return detailed information related to connectionError().
+ *
+ * Note that this method may return a different value from the one returned by
+ * Connection::errorDetails() on this account connection.
+ * See connectionStatus() for more details.
+ *
+ * This method requires Account::FeatureCore to be ready.
+ *
+ * \return The connection error details as a Connection::ErrorDetails object.
+ * \sa connectionError(), connectionStatus(), connectionStatusReason(), connectionStatusChanged(),
+ * Connection::ErrorDetails.
+ */
+Connection::ErrorDetails Account::connectionErrorDetails() const
+{
+ return mPriv->connectionErrorDetails;
+}
+
+/**
+ * Return the object representing the connection of this account.
+ *
+ * Note that the Connection object returned by this method will have the
+ * features set in the connectionFactory() used by this account ready.
+ *
+ * Change notification is via the connectionChanged() signal.
+ *
+ * This method requires Account::FeatureCore to be ready.
+ *
+ * \return A pointer to the Connection object, or a null ConnectionPtr if
+ * there is no connection currently or if an error occurred.
+ * \sa connectionChanged()
+ */
+ConnectionPtr Account::connection() const
+{
+ return mPriv->connection;
+}
+
+/**
+ * Return whether this account connection is changing presence.
+ *
+ * Change notification is via the changingPresence() signal.
+ *
+ * This method requires Account::FeatureCore to be ready.
+ *
+ * \return \c true if changing presence, \c false otherwise.
+ * \sa changingPresence(), currentPresenceChanged(), setRequestedPresence()
+ */
+bool Account::isChangingPresence() const
+{
+ return mPriv->changingPresence;
+}
+
+/**
+ * Return a list of presences allowed by a connection to this account.
+ *
+ * In particular, for the statuses reported here it can be assumed that setting them as the
+ * requested presence via setRequestedPresence() will eventually result in currentPresence()
+ * changing to exactly said presence. Other statuses are only guaranteed to be matched as closely as
+ * possible.
+ *
+ * The statuses can be also used for the automatic presence, as set by setAutomaticPresence(), with
+ * the exception of any status specifications for which Presence::type() is
+ * Tp::ConnectionPresenceTypeOffline for the Presence returned by PresenceSpec::presence().
+ *
+ * However, the optional parameter can be used to allow reporting also other possible presence
+ * statuses on this protocol besides the others that can be set on yourself. These are purely
+ * informatory, for e.g. adjusting an UI to allow all possible remote contact statuses to be
+ * displayed.
+ *
+ * An offline presence status is always included, because it's always valid to make an account
+ * offline by setting the requested presence to an offline status.
+ *
+ * Full functionality requires Account::FeatureProtocolInfo and Account::FeatureProfile to be ready
+ * as well as connection with Connection::FeatureSimplePresence enabled. If the connection is online
+ * and Connection::FeatureSimplePresence is enabled, it will return the connection allowed statuses,
+ * otherwise it will return a list os statuses based on profile() and protocolInfo() information
+ * if the corresponding features are enabled.
+ *
+ * If there's a mismatch between the presence status info provided in the .profile file and/or the
+ * .manager file and what an online Connection actually reports (for example, the said data files
+ * are missing or too old to include presence information), the returned value can change, in
+ * particular when connectionChanged() is emitted with a connection for which Connection::status()
+ * is Tp::ConnectionStatusConnected.
+ *
+ * This method requires Account::FeatureCore to be ready.
+ *
+ * \param includeAllStatuses Whether the returned list will include all statuses or just the ones
+ * that can be settable using setRequestedPresence().
+ * \return The allowed statuses as a list of PresenceSpec objects.
+ */
+PresenceSpecList Account::allowedPresenceStatuses(bool includeAllStatuses) const
+{
+ QMap<QString, PresenceSpec> specMap;
+
+ // if the connection is online and ready use it
+ if (mPriv->connection &&
+ mPriv->connection->status() == ConnectionStatusConnected &&
+ mPriv->connection->actualFeatures().contains(Connection::FeatureSimplePresence)) {
+ SimpleStatusSpecMap connectionAllowedPresences =
+ mPriv->connection->lowlevel()->allowedPresenceStatuses();
+ SimpleStatusSpecMap::const_iterator i = connectionAllowedPresences.constBegin();
+ SimpleStatusSpecMap::const_iterator end = connectionAllowedPresences.constEnd();
+ for (; i != end; ++i) {
+ PresenceSpec presence = PresenceSpec(i.key(), i.value());
+ specMap.insert(i.key(), presence);
+ }
+ } else {
+ ProtocolInfo pi = protocolInfo();
+ if (pi.isValid()) {
+ // add all ProtocolInfo presences to the returned map
+ foreach (const PresenceSpec &piPresence, pi.allowedPresenceStatuses()) {
+ QString piStatus = piPresence.presence().status();
+ specMap.insert(piStatus, piPresence);
+ }
+ }
+
+ ProfilePtr pr;
+ if (isReady(FeatureProfile)) {
+ pr = profile();
+ }
+ if (pr && pr->isValid()) {
+ // add all Profile presences to the returned map
+ foreach (const Profile::Presence &prPresence, pr->presences()) {
+ QString prStatus = prPresence.id();
+ if (specMap.contains(prStatus)) {
+ // we already got the presence from ProtocolInfo, just update
+ // canHaveStatusMessage if needed
+ PresenceSpec presence = specMap.value(prStatus);
+ if (presence.canHaveStatusMessage() != prPresence.canHaveStatusMessage()) {
+ SimpleStatusSpec spec;
+ spec.type = presence.presence().type();
+ spec.maySetOnSelf = presence.maySetOnSelf();
+ spec.canHaveMessage = prPresence.canHaveStatusMessage();
+ specMap.insert(prStatus, PresenceSpec(prStatus, spec));
+ }
+ } else {
+ // presence not found in ProtocolInfo, adding it
+ specMap.insert(prStatus, presenceSpecForStatus(prStatus,
+ prPresence.canHaveStatusMessage()));
+ }
+ }
+
+ // now remove all presences that are not in the Profile, if it does
+ // not allow other presences, and the ones that are disabled
+ QMap<QString, PresenceSpec>::iterator i = specMap.begin();
+ QMap<QString, PresenceSpec>::iterator end = specMap.end();
+ while (i != end) {
+ PresenceSpec presence = i.value();
+ QString status = presence.presence().status();
+ bool hasPresence = pr->hasPresence(status);
+ Profile::Presence prPresence = pr->presence(status);
+ if ((!hasPresence && !pr->allowOtherPresences()) || (hasPresence && prPresence.isDisabled())) {
+ i = specMap.erase(i);
+ } else {
+ ++i;
+ }
+ }
+ }
+ }
+
+ // filter out presences that may not be set on self if includeAllStatuses is false
+ if (!includeAllStatuses) {
+ QMap<QString, PresenceSpec>::iterator i = specMap.begin();
+ QMap<QString, PresenceSpec>::iterator end = specMap.end();
+ while (i != end) {
+ PresenceSpec presence = i.value();
+ if (!presence.maySetOnSelf()) {
+ i = specMap.erase(i);
+ } else {
+ ++i;
+ }
+ }
+ }
+
+ if (!specMap.size()) {
+ // If we didn't discover any statuses, either the protocol doesn't really support presence,
+ // or we lack information (e.g. features not enabled or info not provided in the .manager or
+ // .profile files). "available" - just the fact that you're online in the first place, is at
+ // least a valid option for any protocol, so we'll include it as a fallback.
+
+ specMap.insert(QLatin1String("available"),
+ presenceSpecForStatus(QLatin1String("available"), false));
+ }
+
+ // We'll always include "offline". It is always valid to make an account offline via
+ // setRequestedPresence().
+ if (!specMap.contains(QLatin1String("offline"))) {
+ specMap.insert(QLatin1String("offline"),
+ presenceSpecForStatus(QLatin1String("offline"), false));
+ }
+
+ return specMap.values();
+}
+
+/**
+ * Return the maximum length for a presence status message.
+ *
+ * If a status message set using setRequestedPresence() (or setAutomaticPresence()) is longer than
+ * the maximum length allowed, the message will be truncated and
+ * currentPresenceChanged() will be emitted (if setting the presence worked)
+ * with the truncated message.
+ *
+ * Full functionality requires Connection with Connection::FeatureSimplePresence
+ * enabled. If the connection is online and Connection::FeatureSimplePresence is
+ * enabled, it will return the connection maximum status message length,
+ * otherwise it will return 0.
+ *
+ * This method requires Account::FeatureCore to be ready.
+ *
+ * \return The maximum length, or 0 if there is no limit.
+ */
+uint Account::maxPresenceStatusMessageLength() const
+{
+ // if the connection is online and ready use it
+ if (mPriv->connection &&
+ mPriv->connection->status() == ConnectionStatusConnected &&
+ mPriv->connection->actualFeatures().contains(Connection::FeatureSimplePresence)) {
+ return mPriv->connection->lowlevel()->maxPresenceStatusMessageLength();
+ }
+
+ return 0;
+}
+
+/**
+ * Return the presence status that this account will have set on it by the
+ * account manager if it brings it online automatically.
+ *
+ * Change notification is via the automaticPresenceChanged() signal.
+ *
+ * This method requires Account::FeatureCore to be ready.
+ *
+ * \return The automatic presence as a Presence object.
+ * \sa automaticPresenceChanged(), setAutomaticPresence()
+ */
+Presence Account::automaticPresence() const
+{
+ return mPriv->automaticPresence;
+}
+
+/**
+ * Set the presence status that this account should have if it is brought
+ * online automatically by the account manager.
+ *
+ * Note that changing this property won't actually change the account's status
+ * until the next time it is (re)connected for some reason.
+ *
+ * The value of this property must be one that would be acceptable for setRequestedPresence(),
+ * as returned by allowedPresenceStatuses(), with the additional restriction that the offline
+ * presence cannot be used.
+ *
+ * \param presence The presence to set when this account is brought
+ * online automatically by the account manager.
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the request has been made.
+ * \sa automaticPresenceChanged(), automaticPresence(), setRequestedPresence()
+ */
+PendingOperation *Account::setAutomaticPresence(const Presence &presence)
+{
+ return new PendingVoid(
+ mPriv->properties->Set(
+ QLatin1String(TELEPATHY_INTERFACE_ACCOUNT),
+ QLatin1String("AutomaticPresence"),
+ QDBusVariant(QVariant::fromValue(presence.barePresence()))),
+ AccountPtr(this));
+}
+
+/**
+ * Return the actual presence of this account.
+ *
+ * Change notification is via the currentPresenceChanged() signal.
+ *
+ * This method requires Account::FeatureCore to be ready.
+ *
+ * \return The current presence as a Presence object.
+ * \sa currentPresenceChanged(), setRequestedPresence(), requestedPresence(), automaticPresence()
+ */
+Presence Account::currentPresence() const
+{
+ return mPriv->currentPresence;
+}
+
+/**
+ * Return the requested presence of this account.
+ *
+ * Change notification is via the requestedPresenceChanged() signal.
+ *
+ * This method requires Account::FeatureCore to be ready.
+ *
+ * \return The requested presence as a Presence object.
+ * \sa requestedPresenceChanged(), setRequestedPresence(), currentPresence(), automaticPresence()
+ */
+Presence Account::requestedPresence() const
+{
+ return mPriv->requestedPresence;
+}
+
+/**
+ * Set the requested presence of this account.
+ *
+ * When the requested presence is changed, the account manager will attempt to
+ * manipulate the connection to make currentPresence() match requestedPresence()
+ * as closely as possible.
+ *
+ * \param presence The requested presence.
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the request has been made.
+ * \sa requestedPresenceChanged(), currentPresence(), automaticPresence(), setAutomaticPresence()
+ */
+PendingOperation *Account::setRequestedPresence(const Presence &presence)
+{
+ return new PendingVoid(
+ mPriv->properties->Set(
+ QLatin1String(TELEPATHY_INTERFACE_ACCOUNT),
+ QLatin1String("RequestedPresence"),
+ QDBusVariant(QVariant::fromValue(presence.barePresence()))),
+ AccountPtr(this));
+}
+
+/**
+ * Return whether this account is online.
+ *
+ * Change notification is via the onlinenessChanged() signal.
+ *
+ * This method requires Account::FeatureCore to be ready.
+ *
+ * \return \c true if online, otherwise \c false.
+ * \sa onlinenessChanged()
+ */
+bool Account::isOnline() const
+{
+ return mPriv->currentPresence.type() != ConnectionPresenceTypeOffline;
+}
+
+/**
+ * Return the unique identifier of this account.
+ *
+ * \return The unique identifier.
+ */
+QString Account::uniqueIdentifier() const
+{
+ QString path = objectPath();
+ return path.right(path.length() -
+ strlen("/org/freedesktop/Telepathy/Account/"));
+}
+
+/**
+ * Return the normalized user ID of the local user of this account.
+ *
+ * It is unspecified whether this user ID is globally unique.
+ *
+ * As currently implemented, IRC user IDs are only unique within the same
+ * IRCnet. On some saner protocols, the user ID includes a DNS name which
+ * provides global uniqueness.
+ *
+ * If this value is not known yet (which will always be the case for accounts
+ * that have never been online), it will be an empty string.
+ *
+ * It is possible that this value will change if the connection manager's
+ * normalization algorithm changes.
+ *
+ * This method requires Account::FeatureCore to be ready.
+ *
+ * \return The normalized user ID of the local user.
+ * \sa normalizedNameChanged()
+ */
+QString Account::normalizedName() const
+{
+ return mPriv->normalizedName;
+}
+
+/**
+ * If this account is currently connected, disconnect and reconnect it. If it
+ * is currently trying to connect, cancel the attempt to connect and start
+ * another. If it is currently disconnected, do nothing.
+ *
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the request has been made.
+ */
+PendingOperation *Account::reconnect()
+{
+ return new PendingVoid(baseInterface()->Reconnect(), AccountPtr(this));
+}
+
+/**
+ * Delete this account.
+ *
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the request has been made.
+ * \sa removed()
+ */
+PendingOperation *Account::remove()
+{
+ return new PendingVoid(baseInterface()->Remove(), AccountPtr(this));
+}
+
+/**
+ * Return whether passing hints on channel requests on this account is known to be supported.
+ *
+ * This method requires Account::FeatureCore to be ready.
+ *
+ * \return \c true if supported, \c false otherwise.
+ */
+bool Account::supportsRequestHints() const
+{
+ return mPriv->dispatcherContext->supportsHints;
+}
+
+/**
+ * Return whether the ChannelRequest::succeeded(const Tp::ChannelPtr &channel) signal is expected to
+ * be emitted with a non-NULL channel parameter for requests made using this account.
+ *
+ * This can be used as a run-time check for the Channel Dispatcher implementation being new enough.
+ * In particular, similarly old Channel Dispatchers don't support request hints either, so the
+ * return value for this function and Account::supportsRequestHints() will bet he same.
+ *
+ * This method requires Account::FeatureCore to be ready.
+ *
+ * \return \c true if supported, \c false otherwise.
+ */
+bool Account::requestsSucceedWithChannel() const
+{
+ return supportsRequestHints();
+}
+
+/**
+ * Same as \c ensureTextChat(contactIdentifier, userActionTime, preferredHandler, ChannelRequestHints())
+ */
+PendingChannelRequest *Account::ensureTextChat(
+ const QString &contactIdentifier,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler)
+{
+ return ensureTextChat(contactIdentifier, userActionTime, preferredHandler, ChannelRequestHints());
+}
+
+/**
+ * Start a request to ensure that a text channel with the given
+ * contact \a contactIdentifier exists, creating it if necessary.
+ *
+ * See ensureChannel() for more details.
+ *
+ * \param contactIdentifier The identifier of the contact to chat with.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \param preferredHandler Either the well-known bus name (starting with
+ * org.freedesktop.Telepathy.Client.) of the preferred
+ * handler for this channel, or an empty string to
+ * indicate that any handler would be acceptable.
+ * \param hints Arbitrary metadata which will be relayed to the handler if supported,
+ * as indicated by supportsRequestHints().
+ * \return A PendingChannelRequest which will emit PendingChannelRequest::finished
+ * when the request has been made.
+ * \sa ensureChannel(), createChannel()
+ */
+PendingChannelRequest *Account::ensureTextChat(
+ const QString &contactIdentifier,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints)
+{
+ QVariantMap request = textChatRequest(contactIdentifier);
+
+ return new PendingChannelRequest(AccountPtr(this), request, userActionTime,
+ preferredHandler, false, hints);
+}
+
+/**
+ * Same as \c ensureTextChat(contact, userActionTime, preferredHandler, ChannelRequestHints())
+ */
+PendingChannelRequest *Account::ensureTextChat(
+ const ContactPtr &contact,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler)
+{
+ return ensureTextChat(contact, userActionTime, preferredHandler, ChannelRequestHints());
+}
+
+/**
+ * Start a request to ensure that a text channel with the given
+ * contact \a contact exists, creating it if necessary.
+ *
+ * See ensureChannel() for more details.
+ *
+ * \param contact The contact to chat with.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \param preferredHandler Either the well-known bus name (starting with
+ * org.freedesktop.Telepathy.Client.) of the preferred
+ * handler for this channel, or an empty string to
+ * indicate that any handler would be acceptable.
+ * \param hints Arbitrary metadata which will be relayed to the handler if supported,
+ * as indicated by supportsRequestHints().
+ * \return A PendingChannelRequest which will emit PendingChannelRequest::finished
+ * when the request has been made.
+ * \sa ensureChannel(), createChannel()
+ */
+PendingChannelRequest *Account::ensureTextChat(
+ const ContactPtr &contact,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints)
+{
+ QVariantMap request = textChatRequest(contact);
+
+ return new PendingChannelRequest(AccountPtr(this), request, userActionTime,
+ preferredHandler, false, hints);
+}
+
+/**
+ * Same as \c ensureTextChatroom(roomName, userActionTime, preferredHandler, ChannelRequestHints())
+ */
+PendingChannelRequest *Account::ensureTextChatroom(
+ const QString &roomName,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler)
+{
+ return ensureTextChatroom(roomName, userActionTime, preferredHandler, ChannelRequestHints());
+}
+
+/**
+ * Start a request to ensure that a text chat room with the given
+ * room name \a roomName exists, creating it if necessary.
+ *
+ * See ensureChannel() for more details.
+ *
+ * \param roomName The name of the chat room.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \param preferredHandler Either the well-known bus name (starting with
+ * org.freedesktop.Telepathy.Client.) of the preferred
+ * handler for this channel, or an empty string to
+ * indicate that any handler would be acceptable.
+ * \param hints Arbitrary metadata which will be relayed to the handler if supported,
+ * as indicated by supportsRequestHints().
+ * \return A PendingChannelRequest which will emit PendingChannelRequest::finished
+ * when the request has been made.
+ * \sa ensureChannel(), createChannel()
+ */
+PendingChannelRequest *Account::ensureTextChatroom(
+ const QString &roomName,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints)
+{
+ QVariantMap request = textChatroomRequest(roomName);
+
+ return new PendingChannelRequest(AccountPtr(this), request, userActionTime,
+ preferredHandler, false, hints);
+}
+
+/**
+ * Same as \c ensureStreamedMediaCall(contactIdentifier, userActionTime, preferredHandler, ChannelRequestHints())
+ */
+PendingChannelRequest *Account::ensureStreamedMediaCall(
+ const QString &contactIdentifier,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler)
+{
+ return ensureStreamedMediaCall(contactIdentifier, userActionTime, preferredHandler, ChannelRequestHints());
+}
+
+/**
+ * Start a request to ensure that a media channel with the given
+ * contact \a contactIdentifier exists, creating it if necessary.
+ *
+ * See ensureChannel() for more details.
+ *
+ * \param contactIdentifier The identifier of the contact to call.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \param preferredHandler Either the well-known bus name (starting with
+ * org.freedesktop.Telepathy.Client.) of the preferred
+ * handler for this channel, or an empty string to
+ * indicate that any handler would be acceptable.
+ * \param hints Arbitrary metadata which will be relayed to the handler if supported,
+ * as indicated by supportsRequestHints().
+ * \return A PendingChannelRequest which will emit PendingChannelRequest::finished
+ * when the request has been made.
+ * \sa ensureChannel(), createChannel()
+ */
+PendingChannelRequest *Account::ensureStreamedMediaCall(
+ const QString &contactIdentifier,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints)
+{
+ QVariantMap request = streamedMediaCallRequest(contactIdentifier);
+
+ return new PendingChannelRequest(AccountPtr(this), request, userActionTime,
+ preferredHandler, false, hints);
+}
+
+/**
+ * Same as \c ensureStreamedMediaCall(contact, userActionTime, preferredHandler, ChannelRequestHints())
+ */
+PendingChannelRequest *Account::ensureStreamedMediaCall(
+ const ContactPtr &contact,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler)
+{
+ return ensureStreamedMediaCall(contact, userActionTime, preferredHandler, ChannelRequestHints());
+}
+
+/**
+ * Start a request to ensure that a media channel with the given
+ * contact \a contact exists, creating it if necessary.
+ *
+ * See ensureChannel() for more details.
+ *
+ * \param contact The contact to call.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \param preferredHandler Either the well-known bus name (starting with
+ * org.freedesktop.Telepathy.Client.) of the preferred
+ * handler for this channel, or an empty string to
+ * indicate that any handler would be acceptable.
+ * \param hints Arbitrary metadata which will be relayed to the handler if supported,
+ * as indicated by supportsRequestHints().
+ * \return A PendingChannelRequest which will emit PendingChannelRequest::finished
+ * when the request has been made.
+ * \sa ensureChannel(), createChannel()
+ */
+PendingChannelRequest *Account::ensureStreamedMediaCall(
+ const ContactPtr &contact,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints)
+{
+ QVariantMap request = streamedMediaCallRequest(contact);
+
+ return new PendingChannelRequest(AccountPtr(this), request, userActionTime,
+ preferredHandler, false, hints);
+}
+
+/**
+ * Same as \c ensureStreamedMediaAudioCall(contactIdentifier, userActionTime, preferredHandler, ChannelRequestHints())
+ */
+PendingChannelRequest *Account::ensureStreamedMediaAudioCall(
+ const QString &contactIdentifier,
+ QDateTime userActionTime,
+ const QString &preferredHandler)
+{
+ return ensureStreamedMediaAudioCall(contactIdentifier, userActionTime, preferredHandler, ChannelRequestHints());
+}
+
+/**
+ * Start a request to ensure that an audio call with the given
+ * contact \a contactIdentifier exists, creating it if necessary.
+ *
+ * See ensureChannel() for more details.
+ *
+ * This will only work on relatively modern connection managers,
+ * like telepathy-gabble 0.9.0 or later.
+ *
+ * \param contactIdentifier The identifier of the contact to call.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \param preferredHandler Either the well-known bus name (starting with
+ * org.freedesktop.Telepathy.Client.) of the preferred
+ * handler for this channel, or an empty string to
+ * indicate that any handler would be acceptable.
+ * \param hints Arbitrary metadata which will be relayed to the handler if supported,
+ * as indicated by supportsRequestHints().
+ * \return A PendingChannelRequest which will emit PendingChannelRequest::finished
+ * when the request has been made.
+ * \sa ensureChannel(), createChannel()
+ */
+PendingChannelRequest *Account::ensureStreamedMediaAudioCall(
+ const QString &contactIdentifier,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints)
+{
+ QVariantMap request = streamedMediaAudioCallRequest(contactIdentifier);
+
+ return new PendingChannelRequest(AccountPtr(this), request, userActionTime,
+ preferredHandler, false, hints);
+}
+
+/**
+ * Same as \c ensureStreamedMediaAudioCall(contact, userActionTime, preferredHandler, ChannelRequestHints())
+ */
+PendingChannelRequest *Account::ensureStreamedMediaAudioCall(
+ const ContactPtr &contact,
+ QDateTime userActionTime,
+ const QString &preferredHandler)
+{
+ return ensureStreamedMediaAudioCall(contact, userActionTime, preferredHandler, ChannelRequestHints());
+}
+
+/**
+ * Start a request to ensure that an audio call with the given
+ * contact \a contact exists, creating it if necessary.
+ *
+ * See ensureChannel() for more details.
+ *
+ * This will only work on relatively modern connection managers,
+ * like telepathy-gabble 0.9.0 or later.
+ *
+ * \param contact The contact to call.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \param preferredHandler Either the well-known bus name (starting with
+ * org.freedesktop.Telepathy.Client.) of the preferred
+ * handler for this channel, or an empty string to
+ * indicate that any handler would be acceptable.
+ * \param hints Arbitrary metadata which will be relayed to the handler if supported,
+ * as indicated by supportsRequestHints().
+ * \return A PendingChannelRequest which will emit PendingChannelRequest::finished
+ * when the request has been made.
+ * \sa ensureChannel(), createChannel()
+ */
+PendingChannelRequest *Account::ensureStreamedMediaAudioCall(
+ const ContactPtr &contact,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints)
+{
+ QVariantMap request = streamedMediaAudioCallRequest(contact);
+
+ return new PendingChannelRequest(AccountPtr(this), request, userActionTime,
+ preferredHandler, false, hints);
+}
+
+/**
+ * Same as \c ensureStreamedMediaVideoCall(contactIdentifier, withAudio, userActionTime, preferredHandler, ChannelRequestHints())
+ */
+PendingChannelRequest *Account::ensureStreamedMediaVideoCall(
+ const QString &contactIdentifier,
+ bool withAudio,
+ QDateTime userActionTime,
+ const QString &preferredHandler)
+{
+ return ensureStreamedMediaVideoCall(contactIdentifier, withAudio, userActionTime, preferredHandler, ChannelRequestHints());
+}
+
+/**
+ * Start a request to ensure that a video call with the given
+ * contact \a contactIdentifier exists, creating it if necessary.
+ *
+ * See ensureChannel() for more details.
+ *
+ * This will only work on relatively modern connection managers,
+ * like telepathy-gabble 0.9.0 or later.
+ *
+ * \param contactIdentifier The identifier of the contact to call.
+ * \param withAudio true if both audio and video are required, false for a
+ * video-only call.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \param preferredHandler Either the well-known bus name (starting with
+ * org.freedesktop.Telepathy.Client.) of the preferred
+ * handler for this channel, or an empty string to
+ * indicate that any handler would be acceptable.
+ * \param hints Arbitrary metadata which will be relayed to the handler if supported,
+ * as indicated by supportsRequestHints().
+ * \return A PendingChannelRequest which will emit PendingChannelRequest::finished
+ * when the request has been made.
+ * \sa ensureChannel(), createChannel()
+ */
+PendingChannelRequest *Account::ensureStreamedMediaVideoCall(
+ const QString &contactIdentifier,
+ bool withAudio,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints)
+{
+ QVariantMap request = streamedMediaVideoCallRequest(contactIdentifier, withAudio);
+
+ return new PendingChannelRequest(AccountPtr(this), request, userActionTime,
+ preferredHandler, false, hints);
+}
+
+/**
+ * Same as \c ensureStreamedMediaVideoCall(contact, withAudio, userActionTime, preferredHandler, ChannelRequestHints())
+ */
+PendingChannelRequest *Account::ensureStreamedMediaVideoCall(
+ const ContactPtr &contact,
+ bool withAudio,
+ QDateTime userActionTime,
+ const QString &preferredHandler)
+{
+ return ensureStreamedMediaVideoCall(contact, withAudio, userActionTime, preferredHandler, ChannelRequestHints());
+}
+
+/**
+ * Start a request to ensure that a video call with the given
+ * contact \a contact exists, creating it if necessary.
+ *
+ * See ensureChannel() for more details.
+ *
+ * This will only work on relatively modern connection managers,
+ * like telepathy-gabble 0.9.0 or later.
+ *
+ * \param contact The contact to call.
+ * \param withAudio true if both audio and video are required, false for a
+ * video-only call.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \param preferredHandler Either the well-known bus name (starting with
+ * org.freedesktop.Telepathy.Client.) of the preferred
+ * handler for this channel, or an empty string to
+ * indicate that any handler would be acceptable.
+ * \param hints Arbitrary metadata which will be relayed to the handler if supported,
+ * as indicated by supportsRequestHints().
+ * \return A PendingChannelRequest which will emit PendingChannelRequest::finished
+ * when the request has been made.
+ * \sa ensureChannel(), createChannel()
+ */
+PendingChannelRequest *Account::ensureStreamedMediaVideoCall(
+ const ContactPtr &contact,
+ bool withAudio,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints)
+{
+ QVariantMap request = streamedMediaVideoCallRequest(contact, withAudio);
+
+ return new PendingChannelRequest(AccountPtr(this), request, userActionTime,
+ preferredHandler, false, hints);
+}
+
+/**
+ * Same as \c createFileTransfer(contactIdentifier, properties, userActionTime, preferredHandler, ChannelRequestHints())
+ */
+PendingChannelRequest *Account::createFileTransfer(
+ const QString &contactIdentifier,
+ const FileTransferChannelCreationProperties &properties,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler)
+{
+ return createFileTransfer(contactIdentifier, properties, userActionTime, preferredHandler, ChannelRequestHints());
+}
+
+/**
+ * Start a request to create a file transfer channel with the given
+ * contact \a contact.
+ *
+ * \param contactIdentifier The identifier of the contact to send a file.
+ * \param properties The desired properties.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \param preferredHandler Either the well-known bus name (starting with
+ * org.freedesktop.Telepathy.Client.) of the preferred
+ * handler for this channel, or an empty string to
+ * indicate that any handler would be acceptable.
+ * \param hints Arbitrary metadata which will be relayed to the handler if supported,
+ * as indicated by supportsRequestHints().
+ * \return A PendingChannelRequest which will emit PendingChannelRequest::finished
+ * when the request has been made.
+ * \sa ensureChannel(), createChannel()
+ */
+PendingChannelRequest *Account::createFileTransfer(
+ const QString &contactIdentifier,
+ const FileTransferChannelCreationProperties &properties,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints)
+{
+ QVariantMap request = fileTransferRequest(contactIdentifier, properties);
+
+ if (request.isEmpty()) {
+ return new PendingChannelRequest(AccountPtr(this), TP_QT_ERROR_INVALID_ARGUMENT,
+ QLatin1String("Cannot create a file transfer with invalid parameters"));
+ }
+
+ return new PendingChannelRequest(AccountPtr(this), request, userActionTime,
+ preferredHandler, true, hints);
+}
+
+/**
+ * Same as \c createFileTransfer(contact, properties, userActionTime, preferredHandler, ChannelRequestHints())
+ */
+PendingChannelRequest *Account::createFileTransfer(
+ const ContactPtr &contact,
+ const FileTransferChannelCreationProperties &properties,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler)
+{
+ return createFileTransfer(contact, properties, userActionTime, preferredHandler, ChannelRequestHints());
+}
+
+/**
+ * Start a request to create a file transfer channel with the given
+ * contact \a contact.
+ *
+ * \param contact The contact to send a file.
+ * \param properties The desired properties.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \param preferredHandler Either the well-known bus name (starting with
+ * org.freedesktop.Telepathy.Client.) of the preferred
+ * handler for this channel, or an empty string to
+ * indicate that any handler would be acceptable.
+ * \param hints Arbitrary metadata which will be relayed to the handler if supported,
+ * as indicated by supportsRequestHints().
+ * \return A PendingChannelRequest which will emit PendingChannelRequest::finished
+ * when the request has been made.
+ * \sa ensureChannel(), createChannel()
+ */
+PendingChannelRequest *Account::createFileTransfer(
+ const ContactPtr &contact,
+ const FileTransferChannelCreationProperties &properties,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints)
+{
+ QVariantMap request = fileTransferRequest(contact, properties);
+
+ if (request.isEmpty()) {
+ return new PendingChannelRequest(AccountPtr(this), TP_QT_ERROR_INVALID_ARGUMENT,
+ QLatin1String("Cannot create a file transfer with invalid parameters"));
+ }
+
+ return new PendingChannelRequest(AccountPtr(this), request, userActionTime,
+ preferredHandler, true, hints);
+}
+
+/**
+ * Start a request to create a stream tube channel with the given
+ * contact identifier \a contactIdentifier.
+ *
+ * \param contactIdentifier The identifier of the contact to open a stream tube with.
+ * \param service The stream tube service.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \param preferredHandler Either the well-known bus name (starting with
+ * org.freedesktop.Telepathy.Client.) of the preferred
+ * handler for this channel, or an empty string to
+ * indicate that any handler would be acceptable.
+ * \param hints Arbitrary metadata which will be relayed to the handler if supported,
+ * as indicated by supportsRequestHints().
+ * \return A PendingChannelRequest which will emit PendingChannelRequest::finished
+ * when the request has been made.
+ * \sa ensureChannel(), createChannel()
+ */
+PendingChannelRequest *Account::createStreamTube(
+ const QString &contactIdentifier,
+ const QString &service,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints)
+{
+ QVariantMap request = streamTubeRequest(contactIdentifier, service);
+
+ return new PendingChannelRequest(AccountPtr(this), request, userActionTime,
+ preferredHandler, true, hints);
+}
+
+/**
+ * Start a request to create a stream tube channel with the given
+ * contact \a contact.
+ *
+ * \param contact The contact to open a stream tube with.
+ * \param service The stream tube service.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \param preferredHandler Either the well-known bus name (starting with
+ * org.freedesktop.Telepathy.Client.) of the preferred
+ * handler for this channel, or an empty string to
+ * indicate that any handler would be acceptable.
+ * \param hints Arbitrary metadata which will be relayed to the handler if supported,
+ * as indicated by supportsRequestHints().
+ * \return A PendingChannelRequest which will emit PendingChannelRequest::finished
+ * when the request has been made.
+ * \sa ensureChannel(), createChannel()
+ */
+PendingChannelRequest *Account::createStreamTube(
+ const ContactPtr &contact,
+ const QString &service,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints)
+{
+ QVariantMap request = streamTubeRequest(contact, service);
+
+ return new PendingChannelRequest(AccountPtr(this), request, userActionTime,
+ preferredHandler, true, hints);
+}
+
+/**
+ * Same as \c createConferenceStreamedMediaCall(channels, initialInviteeContactsIdentifiers, userActionTime, preferredHandler, ChannelRequestHints())
+ */
+PendingChannelRequest *Account::createConferenceStreamedMediaCall(
+ const QList<ChannelPtr> &channels,
+ const QStringList &initialInviteeContactsIdentifiers,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler)
+{
+ return createConferenceStreamedMediaCall(channels, initialInviteeContactsIdentifiers, userActionTime, preferredHandler, ChannelRequestHints());
+}
+
+/**
+ * Start a request to create a conference media call with the given
+ * channels \a channels.
+ *
+ * \param channels The conference channels.
+ * \param initialInviteeContactsIdentifiers A list of additional contacts
+ * identifiers to be invited to this
+ * conference when it is created.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \param preferredHandler Either the well-known bus name (starting with
+ * org.freedesktop.Telepathy.Client.) of the preferred
+ * handler for this channel, or an empty string to
+ * indicate that any handler would be acceptable.
+ * \param hints Arbitrary metadata which will be relayed to the handler if supported,
+ * as indicated by supportsRequestHints().
+ * \return A PendingChannelRequest which will emit PendingChannelRequest::finished
+ * when the request has been made.
+ * \sa ensureChannel(), createChannel()
+ */
+PendingChannelRequest *Account::createConferenceStreamedMediaCall(
+ const QList<ChannelPtr> &channels,
+ const QStringList &initialInviteeContactsIdentifiers,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints)
+{
+ QVariantMap request = conferenceStreamedMediaCallRequest(channels,
+ initialInviteeContactsIdentifiers);
+
+ return new PendingChannelRequest(AccountPtr(this), request, userActionTime,
+ preferredHandler, true, hints);
+}
+
+/**
+ * Same as \c createConferenceStreamedMediaCall(channels, initialInviteeContacts, userActionTime, preferredHandler, ChannelRequestHints())
+ */
+PendingChannelRequest *Account::createConferenceStreamedMediaCall(
+ const QList<ChannelPtr> &channels,
+ const QList<ContactPtr> &initialInviteeContacts,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler)
+{
+ return createConferenceStreamedMediaCall(channels, initialInviteeContacts, userActionTime, preferredHandler, ChannelRequestHints());
+}
+
+/**
+ * Start a request to create a conference media call with the given
+ * channels \a channels.
+ *
+ * \param channels The conference channels.
+ * \param initialInviteeContacts A list of additional contacts
+ * to be invited to this
+ * conference when it is created.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \param preferredHandler Either the well-known bus name (starting with
+ * org.freedesktop.Telepathy.Client.) of the preferred
+ * handler for this channel, or an empty string to
+ * indicate that any handler would be acceptable.
+ * \param hints Arbitrary metadata which will be relayed to the handler if supported,
+ * as indicated by supportsRequestHints().
+ * \return A PendingChannelRequest which will emit PendingChannelRequest::finished
+ * when the request has been made.
+ * \sa ensureChannel(), createChannel()
+ */
+PendingChannelRequest *Account::createConferenceStreamedMediaCall(
+ const QList<ChannelPtr> &channels,
+ const QList<ContactPtr> &initialInviteeContacts,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints)
+{
+ QVariantMap request = conferenceStreamedMediaCallRequest(channels, initialInviteeContacts);
+
+ return new PendingChannelRequest(AccountPtr(this), request, userActionTime,
+ preferredHandler, true, hints);
+}
+
+/**
+ * Same as \c createConferenceTextChat(channels, initialInviteeContactsIdentifiers, userActionTime, preferredHandler, ChannelRequestHints())
+ */
+PendingChannelRequest *Account::createConferenceTextChat(
+ const QList<ChannelPtr> &channels,
+ const QStringList &initialInviteeContactsIdentifiers,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler)
+{
+ return createConferenceTextChat(channels, initialInviteeContactsIdentifiers, userActionTime, preferredHandler, ChannelRequestHints());
+}
+
+/**
+ * Start a request to create a conference text chat with the given
+ * channels \a channels.
+ *
+ * \param channels The conference channels.
+ * \param initialInviteeContactsIdentifiers A list of additional contacts
+ * identifiers to be invited to this
+ * conference when it is created.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \param preferredHandler Either the well-known bus name (starting with
+ * org.freedesktop.Telepathy.Client.) of the preferred
+ * handler for this channel, or an empty string to
+ * indicate that any handler would be acceptable.
+ * \param hints Arbitrary metadata which will be relayed to the handler if supported,
+ * as indicated by supportsRequestHints().
+ * \return A PendingChannelRequest which will emit PendingChannelRequest::finished
+ * when the request has been made.
+ * \sa ensureChannel(), createChannel()
+ */
+PendingChannelRequest *Account::createConferenceTextChat(
+ const QList<ChannelPtr> &channels,
+ const QStringList &initialInviteeContactsIdentifiers,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints)
+{
+ QVariantMap request = conferenceTextChatRequest(channels, initialInviteeContactsIdentifiers);
+
+ return new PendingChannelRequest(AccountPtr(this), request, userActionTime,
+ preferredHandler, true, hints);
+}
+
+/**
+ * Same as \c createConferenceTextChat(channels, initialInviteeContacts, userActionTime, preferredHandler, ChannelRequestHints())
+ */
+PendingChannelRequest *Account::createConferenceTextChat(
+ const QList<ChannelPtr> &channels,
+ const QList<ContactPtr> &initialInviteeContacts,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler)
+{
+ return createConferenceTextChat(channels, initialInviteeContacts, userActionTime, preferredHandler, ChannelRequestHints());
+}
+
+/**
+ * Start a request to create a conference text chat with the given
+ * channels \a channels.
+ *
+ * \param channels The conference channels.
+ * \param initialInviteeContacts A list of additional contacts
+ * to be invited to this
+ * conference when it is created.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \param preferredHandler Either the well-known bus name (starting with
+ * org.freedesktop.Telepathy.Client.) of the preferred
+ * handler for this channel, or an empty string to
+ * indicate that any handler would be acceptable.
+ * \param hints Arbitrary metadata which will be relayed to the handler if supported,
+ * as indicated by supportsRequestHints().
+ * \return A PendingChannelRequest which will emit PendingChannelRequest::finished
+ * when the request has been made.
+ * \sa ensureChannel(), createChannel()
+ */
+PendingChannelRequest *Account::createConferenceTextChat(
+ const QList<ChannelPtr> &channels,
+ const QList<ContactPtr> &initialInviteeContacts,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints)
+{
+ QVariantMap request = conferenceTextChatRequest(channels, initialInviteeContacts);
+
+ return new PendingChannelRequest(AccountPtr(this), request, userActionTime,
+ preferredHandler, true, hints);
+}
+
+/**
+ * Same as \c createConferenceTextChatroom(roomName, channels, initialInviteeContactsIdentifiers, userActionTime, preferredHandler, ChannelRequestHints())
+ */
+PendingChannelRequest *Account::createConferenceTextChatRoom(
+ const QString &roomName,
+ const QList<ChannelPtr> &channels,
+ const QStringList &initialInviteeContactsIdentifiers,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler)
+{
+ return createConferenceTextChatroom(roomName, channels, initialInviteeContactsIdentifiers, userActionTime, preferredHandler, ChannelRequestHints());
+}
+
+/**
+ * Start a request to create a conference text chat room with the given
+ * channels \a channels and room name \a roomName.
+ *
+ * \param roomName The room name.
+ * \param channels The conference channels.
+ * \param initialInviteeContactsIdentifiers A list of additional contacts
+ * identifiers to be invited to this
+ * conference when it is created.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \param preferredHandler Either the well-known bus name (starting with
+ * org.freedesktop.Telepathy.Client.) of the preferred
+ * handler for this channel, or an empty string to
+ * indicate that any handler would be acceptable.
+ * \param hints Arbitrary metadata which will be relayed to the handler if supported,
+ * as indicated by supportsRequestHints().
+ * \return A PendingChannelRequest which will emit PendingChannelRequest::finished
+ * when the request has been made.
+ * \sa ensureChannel(), createChannel()
+ */
+PendingChannelRequest *Account::createConferenceTextChatroom(
+ const QString &roomName,
+ const QList<ChannelPtr> &channels,
+ const QStringList &initialInviteeContactsIdentifiers,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints)
+{
+ QVariantMap request = conferenceTextChatroomRequest(roomName, channels,
+ initialInviteeContactsIdentifiers);
+
+ return new PendingChannelRequest(AccountPtr(this), request, userActionTime,
+ preferredHandler, true, hints);
+}
+
+/**
+ * Same as \c createConferenceTextChatroom(roomName, channels, initialInviteeContacts, userActionTime, preferredHandler, ChannelRequestHints())
+ */
+PendingChannelRequest *Account::createConferenceTextChatRoom(
+ const QString &roomName,
+ const QList<ChannelPtr> &channels,
+ const QList<ContactPtr> &initialInviteeContacts,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler)
+{
+ return createConferenceTextChatroom(roomName, channels, initialInviteeContacts, userActionTime, preferredHandler, ChannelRequestHints());
+}
+
+/**
+ * Start a request to create a conference text chat room with the given
+ * channels \a channels and room name \a roomName.
+ *
+ * \param roomName The room name.
+ * \param channels The conference channels.
+ * \param initialInviteeContacts A list of additional contacts
+ * to be invited to this
+ * conference when it is created.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \param preferredHandler Either the well-known bus name (starting with
+ * org.freedesktop.Telepathy.Client.) of the preferred
+ * handler for this channel, or an empty string to
+ * indicate that any handler would be acceptable.
+ * \param hints Arbitrary metadata which will be relayed to the handler if supported,
+ * as indicated by supportsRequestHints().
+ * \return A PendingChannelRequest which will emit PendingChannelRequest::finished
+ * when the request has been made.
+ * \sa ensureChannel(), createChannel()
+ */
+PendingChannelRequest *Account::createConferenceTextChatroom(
+ const QString &roomName,
+ const QList<ChannelPtr> &channels,
+ const QList<ContactPtr> &initialInviteeContacts,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints)
+{
+ QVariantMap request = conferenceTextChatroomRequest(roomName, channels,
+ initialInviteeContacts);
+
+ return new PendingChannelRequest(AccountPtr(this), request, userActionTime,
+ preferredHandler, true, hints);
+}
+
+/**
+ * Same as \c createContactSearch(server, limit, userActionTime, preferredHandler, ChannelRequestHints())
+ */
+PendingChannelRequest *Account::createContactSearch(
+ const QString &server,
+ uint limit,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler)
+{
+ return createContactSearch(server, limit, userActionTime, preferredHandler, ChannelRequestHints());
+}
+
+/**
+ * Start a request to create a contact search channel with the given
+ * server \a server and limit \a limit.
+ *
+ * \param server For protocols which support searching for contacts on multiple servers with
+ * different DNS names (like XMPP), the DNS name of the server to be searched,
+ * e.g. "characters.shakespeare.lit". Otherwise, an empty string.
+ * \param limit The desired maximum number of results that should be returned by a doing a search.
+ * If the protocol does not support specifying a limit for the number of results
+ * returned at a time, this will be ignored.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \param preferredHandler Either the well-known bus name (starting with
+ * org.freedesktop.Telepathy.Client.) of the preferred
+ * handler for this channel, or an empty string to
+ * indicate that any handler would be acceptable.
+ * \param hints Arbitrary metadata which will be relayed to the handler if supported,
+ * as indicated by supportsRequestHints().
+ * \return A PendingChannelRequest which will emit PendingChannelRequest::finished
+ * when the request has been made.
+ * \sa createChannel()
+ */
+PendingChannelRequest *Account::createContactSearch(
+ const QString &server,
+ uint limit,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints)
+{
+ QVariantMap request = contactSearchRequest(capabilities(), server, limit);
+
+ return new PendingChannelRequest(AccountPtr(this), request, userActionTime,
+ preferredHandler, true, hints);
+}
+
+/**
+ * Start a request to ensure that a text channel with the given
+ * contact \a contactIdentifier exists, creating it if necessary.
+ * This initially just creates a PendingChannel object,
+ * which can be used to track the success or failure of the request.
+ *
+ * \param contactIdentifier The identifier of the contact to chat with.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \return A PendingChannel which will emit PendingChannel::finished
+ * successfully, when the Channel is available for handling using
+ * PendingChannel::channel(), or with an error if one has been encountered.
+ * \sa ensureAndHandleChannel(), createAndHandleChannel()
+ */
+PendingChannel *Account::ensureAndHandleTextChat(
+ const QString &contactIdentifier,
+ const QDateTime &userActionTime)
+{
+ QVariantMap request = textChatRequest(contactIdentifier);
+
+ return ensureAndHandleChannel(request, userActionTime);
+}
+
+/**
+ * Start a request to ensure that a text channel with the given
+ * contact \a contact exists, creating it if necessary.
+ * This initially just creates a PendingChannel object,
+ * which can be used to track the success or failure of the request.
+ *
+ * \param contact The contact to chat with.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \return A PendingChannel which will emit PendingChannel::finished
+ * successfully, when the Channel is available for handling using
+ * PendingChannel::channel(), or with an error if one has been encountered.
+ * \sa ensureAndHandleChannel(), createAndHandleChannel()
+ */
+PendingChannel *Account::ensureAndHandleTextChat(
+ const ContactPtr &contact,
+ const QDateTime &userActionTime)
+{
+ QVariantMap request = textChatRequest(contact);
+
+ return ensureAndHandleChannel(request, userActionTime);
+}
+
+/**
+ * Start a request to ensure that a text chat room with the given
+ * room name \a roomName exists, creating it if necessary.
+ * This initially just creates a PendingChannel object,
+ * which can be used to track the success or failure of the request.
+ *
+ * \param roomName The name of the chat room.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \return A PendingChannel which will emit PendingChannel::finished
+ * successfully, when the Channel is available for handling using
+ * PendingChannel::channel(), or with an error if one has been encountered.
+ * \sa ensureAndHandleChannel(), createAndHandleChannel()
+ */
+PendingChannel *Account::ensureAndHandleTextChatroom(
+ const QString &roomName,
+ const QDateTime &userActionTime)
+{
+ QVariantMap request = textChatroomRequest(roomName);
+
+ return ensureAndHandleChannel(request, userActionTime);
+}
+
+/**
+ * Start a request to ensure that a media channel with the given
+ * contact \a contactIdentifier exists, creating it if necessary.
+ * This initially just creates a PendingChannel object,
+ * which can be used to track the success or failure of the request.
+ *
+ * \param contactIdentifier The identifier of the contact to call.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \return A PendingChannel which will emit PendingChannel::finished
+ * successfully, when the Channel is available for handling using
+ * PendingChannel::channel(), or with an error if one has been encountered.
+ * \sa ensureAndHandleChannel(), createAndHandleChannel()
+ */
+PendingChannel *Account::ensureAndHandleStreamedMediaCall(
+ const QString &contactIdentifier,
+ const QDateTime &userActionTime)
+{
+ QVariantMap request = streamedMediaCallRequest(contactIdentifier);
+
+ return ensureAndHandleChannel(request, userActionTime);
+}
+
+/**
+ * Start a request to ensure that a media channel with the given
+ * contact \a contact exists, creating it if necessary.
+ * This initially just creates a PendingChannel object,
+ * which can be used to track the success or failure of the request.
+ *
+ * \param contact The contact to call.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \return A PendingChannel which will emit PendingChannel::finished
+ * successfully, when the Channel is available for handling using
+ * PendingChannel::channel(), or with an error if one has been encountered.
+ * \sa ensureAndHandleChannel(), createAndHandleChannel()
+ */
+PendingChannel *Account::ensureAndHandleStreamedMediaCall(
+ const ContactPtr &contact,
+ const QDateTime &userActionTime)
+{
+ QVariantMap request = streamedMediaCallRequest(contact);
+
+ return ensureAndHandleChannel(request, userActionTime);
+}
+
+/**
+ * Start a request to ensure that an audio call with the given
+ * contact \a contactIdentifier exists, creating it if necessary.
+ * This initially just creates a PendingChannel object,
+ * which can be used to track the success or failure of the request.
+ *
+ * This will only work on relatively modern connection managers,
+ * like telepathy-gabble 0.9.0 or later.
+ *
+ * \param contactIdentifier The identifier of the contact to call.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \return A PendingChannel which will emit PendingChannel::finished
+ * successfully, when the Channel is available for handling using
+ * PendingChannel::channel(), or with an error if one has been encountered.
+ * \sa ensureAndHandleChannel(), createAndHandleChannel()
+ */
+PendingChannel *Account::ensureAndHandleStreamedMediaAudioCall(
+ const QString &contactIdentifier,
+ const QDateTime &userActionTime)
+{
+ QVariantMap request = streamedMediaAudioCallRequest(contactIdentifier);
+
+ return ensureAndHandleChannel(request, userActionTime);
+}
+
+/**
+ * Start a request to ensure that an audio call with the given
+ * contact \a contact exists, creating it if necessary.
+ * This initially just creates a PendingChannel object,
+ * which can be used to track the success or failure of the request.
+ *
+ * This will only work on relatively modern connection managers,
+ * like telepathy-gabble 0.9.0 or later.
+ *
+ * \param contact The contact to call.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \return A PendingChannel which will emit PendingChannel::finished
+ * successfully, when the Channel is available for handling using
+ * PendingChannel::channel(), or with an error if one has been encountered.
+ * \sa ensureAndHandleChannel(), createAndHandleChannel()
+ */
+PendingChannel *Account::ensureAndHandleStreamedMediaAudioCall(
+ const ContactPtr &contact,
+ const QDateTime &userActionTime)
+{
+ QVariantMap request = streamedMediaAudioCallRequest(contact);
+
+ return ensureAndHandleChannel(request, userActionTime);
+}
+
+/**
+ * Start a request to ensure that a video call with the given
+ * contact \a contactIdentifier exists, creating it if necessary.
+ * This initially just creates a PendingChannel object,
+ * which can be used to track the success or failure of the request.
+ *
+ * This will only work on relatively modern connection managers,
+ * like telepathy-gabble 0.9.0 or later.
+ *
+ * \param contactIdentifier The identifier of the contact to call.
+ * \param withAudio true if both audio and video are required, false for a
+ * video-only call.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \return A PendingChannel which will emit PendingChannel::finished
+ * successfully, when the Channel is available for handling using
+ * PendingChannel::channel(), or with an error if one has been encountered.
+ * \sa ensureAndHandleChannel(), createAndHandleChannel()
+ */
+PendingChannel *Account::ensureAndHandleStreamedMediaVideoCall(
+ const QString &contactIdentifier,
+ bool withAudio,
+ const QDateTime &userActionTime)
+{
+ QVariantMap request = streamedMediaVideoCallRequest(contactIdentifier, withAudio);
+
+ return ensureAndHandleChannel(request, userActionTime);
+}
+
+/**
+ * Start a request to ensure that a video call with the given
+ * contact \a contact exists, creating it if necessary.
+ * This initially just creates a PendingChannel object,
+ * which can be used to track the success or failure of the request.
+ *
+ * This will only work on relatively modern connection managers,
+ * like telepathy-gabble 0.9.0 or later.
+ *
+ * \param contact The contact to call.
+ * \param withAudio true if both audio and video are required, false for a
+ * video-only call.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \return A PendingChannel which will emit PendingChannel::finished
+ * successfully, when the Channel is available for handling using
+ * PendingChannel::channel(), or with an error if one has been encountered.
+ * \sa ensureAndHandleChannel(), createAndHandleChannel()
+ */
+PendingChannel *Account::ensureAndHandleStreamedMediaVideoCall(
+ const ContactPtr &contact,
+ bool withAudio,
+ const QDateTime &userActionTime)
+{
+ QVariantMap request = streamedMediaVideoCallRequest(contact, withAudio);
+
+ return ensureAndHandleChannel(request, userActionTime);
+}
+
+/**
+ * Start a request to create a file transfer channel with the given
+ * contact \a contactIdentifier.
+ * This initially just creates a PendingChannel object,
+ * which can be used to track the success or failure of the request.
+ *
+ * \param contactIdentifier The identifier of the contact to send a file.
+ * \param properties The desired properties.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \return A PendingChannel which will emit PendingChannel::finished
+ * successfully, when the Channel is available for handling using
+ * PendingChannel::channel(), or with an error if one has been encountered.
+ * \sa ensureAndHandleChannel(), createAndHandleChannel()
+ */
+PendingChannel *Account::createAndHandleFileTransfer(
+ const QString &contactIdentifier,
+ const FileTransferChannelCreationProperties &properties,
+ const QDateTime &userActionTime)
+{
+ QVariantMap request = fileTransferRequest(contactIdentifier, properties);
+
+ if (request.isEmpty()) {
+ return new PendingChannel(TP_QT_ERROR_INVALID_ARGUMENT,
+ QLatin1String("Cannot create a file transfer with invalid parameters"));
+ }
+
+ return createAndHandleChannel(request, userActionTime);
+}
+
+/**
+ * Start a request to create a file transfer channel with the given
+ * contact \a contact.
+ * This initially just creates a PendingChannel object,
+ * which can be used to track the success or failure of the request.
+ *
+ * \param contact The contact to send a file.
+ * \param properties The desired properties.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \return A PendingChannel which will emit PendingChannel::finished
+ * successfully, when the Channel is available for handling using
+ * PendingChannel::channel(), or with an error if one has been encountered.
+ * \sa ensureAndHandleChannel(), createAndHandleChannel()
+ */
+PendingChannel *Account::createAndHandleFileTransfer(
+ const ContactPtr &contact,
+ const FileTransferChannelCreationProperties &properties,
+ const QDateTime &userActionTime)
+{
+ QVariantMap request = fileTransferRequest(contact, properties);
+
+ if (request.isEmpty()) {
+ return new PendingChannel(TP_QT_ERROR_INVALID_ARGUMENT,
+ QLatin1String("Cannot create a file transfer with invalid parameters"));
+ }
+
+ return createAndHandleChannel(request, userActionTime);
+}
+
+/**
+ * Start a request to create a stream tube channel with the given
+ * contact identifier \a contactIdentifier.
+ * This initially just creates a PendingChannel object,
+ * which can be used to track the success or failure of the request.
+ *
+ * \param contactIdentifier The identifier of the contact to open a stream tube with.
+ * \param service The stream tube service.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \return A PendingChannel which will emit PendingChannel::finished
+ * successfully, when the Channel is available for handling using
+ * PendingChannel::channel(), or with an error if one has been encountered.
+ * \sa ensureAndHandleChannel(), createAndHandleChannel()
+ */
+PendingChannel *Account::createAndHandleStreamTube(
+ const QString &contactIdentifier,
+ const QString &service,
+ const QDateTime &userActionTime)
+{
+ QVariantMap request = streamTubeRequest(contactIdentifier, service);
+
+ return createAndHandleChannel(request, userActionTime);
+}
+
+/**
+ * Start a request to create a stream tube channel with the given
+ * contact \a contact.
+ * This initially just creates a PendingChannel object,
+ * which can be used to track the success or failure of the request.
+ *
+ * \param contact The contact to open a stream tube with.
+ * \param service The stream tube service.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \return A PendingChannel which will emit PendingChannel::finished
+ * successfully, when the Channel is available for handling using
+ * PendingChannel::channel(), or with an error if one has been encountered.
+ * \sa ensureAndHandleChannel(), createAndHandleChannel()
+ */
+PendingChannel *Account::createAndHandleStreamTube(
+ const ContactPtr &contact,
+ const QString &service,
+ const QDateTime &userActionTime)
+{
+ QVariantMap request = streamTubeRequest(contact, service);
+
+ return createAndHandleChannel(request, userActionTime);
+}
+
+/**
+ * Start a request to create a conference text chat with the given
+ * channels \a channels.
+ * This initially just creates a PendingChannel object,
+ * which can be used to track the success or failure of the request.
+ *
+ * \param channels The conference channels.
+ * \param initialInviteeContactsIdentifiers A list of additional contacts
+ * identifiers to be invited to this
+ * conference when it is created.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \return A PendingChannel which will emit PendingChannel::finished
+ * successfully, when the Channel is available for handling using
+ * PendingChannel::channel(), or with an error if one has been encountered.
+ * \sa ensureAndHandleChannel(), createAndHandleChannel()
+ */
+PendingChannel *Account::createAndHandleConferenceTextChat(
+ const QList<ChannelPtr> &channels,
+ const QStringList &initialInviteeContactsIdentifiers,
+ const QDateTime &userActionTime)
+{
+ QVariantMap request = conferenceTextChatRequest(channels, initialInviteeContactsIdentifiers);
+
+ return createAndHandleChannel(request, userActionTime);
+}
+
+/**
+ * Start a request to create a conference text chat with the given
+ * channels \a channels.
+ * This initially just creates a PendingChannel object,
+ * which can be used to track the success or failure of the request.
+ *
+ * \param channels The conference channels.
+ * \param initialInviteeContacts A list of additional contacts
+ * to be invited to this
+ * conference when it is created.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \return A PendingChannel which will emit PendingChannel::finished
+ * successfully, when the Channel is available for handling using
+ * PendingChannel::channel(), or with an error if one has been encountered.
+ * \sa ensureAndHandleChannel(), createAndHandleChannel()
+ */
+PendingChannel *Account::createAndHandleConferenceTextChat(
+ const QList<ChannelPtr> &channels,
+ const QList<ContactPtr> &initialInviteeContacts,
+ const QDateTime &userActionTime)
+{
+ QVariantMap request = conferenceTextChatRequest(channels, initialInviteeContacts);
+
+ return createAndHandleChannel(request, userActionTime);
+}
+
+/**
+ * Start a request to create a conference text chat room with the given
+ * channels \a channels and room name \a roomName.
+ * This initially just creates a PendingChannel object,
+ * which can be used to track the success or failure of the request.
+ *
+ * \param roomName The room name.
+ * \param channels The conference channels.
+ * \param initialInviteeContactsIdentifiers A list of additional contacts
+ * identifiers to be invited to this
+ * conference when it is created.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \return A PendingChannel which will emit PendingChannel::finished
+ * successfully, when the Channel is available for handling using
+ * PendingChannel::channel(), or with an error if one has been encountered.
+ * \sa ensureAndHandleChannel(), createAndHandleChannel()
+ */
+PendingChannel *Account::createAndHandleConferenceTextChatroom(
+ const QString &roomName,
+ const QList<ChannelPtr> &channels,
+ const QStringList &initialInviteeContactsIdentifiers,
+ const QDateTime &userActionTime)
+{
+ QVariantMap request = conferenceTextChatroomRequest(roomName, channels,
+ initialInviteeContactsIdentifiers);
+
+ return createAndHandleChannel(request, userActionTime);
+}
+
+/**
+ * Start a request to create a conference text chat room with the given
+ * channels \a channels and room name \a roomName.
+ * This initially just creates a PendingChannel object,
+ * which can be used to track the success or failure of the request.
+ *
+ * \param roomName The room name.
+ * \param channels The conference channels.
+ * \param initialInviteeContacts A list of additional contacts
+ * to be invited to this
+ * conference when it is created.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \return A PendingChannel which will emit PendingChannel::finished
+ * successfully, when the Channel is available for handling using
+ * PendingChannel::channel(), or with an error if one has been encountered.
+ * \sa ensureAndHandleChannel(), createAndHandleChannel()
+ */
+PendingChannel *Account::createAndHandleConferenceTextChatroom(
+ const QString &roomName,
+ const QList<ChannelPtr> &channels,
+ const QList<ContactPtr> &initialInviteeContacts,
+ const QDateTime &userActionTime)
+{
+ QVariantMap request = conferenceTextChatroomRequest(roomName, channels,
+ initialInviteeContacts);
+
+ return createAndHandleChannel(request, userActionTime);
+}
+
+/**
+ * Start a request to create a conference media call with the given
+ * channels \a channels.
+ * This initially just creates a PendingChannel object,
+ * which can be used to track the success or failure of the request.
+ *
+ * \param channels The conference channels.
+ * \param initialInviteeContactsIdentifiers A list of additional contacts
+ * identifiers to be invited to this
+ * conference when it is created.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \return A PendingChannel which will emit PendingChannel::finished
+ * successfully, when the Channel is available for handling using
+ * PendingChannel::channel(), or with an error if one has been encountered.
+ * \sa ensureAndHandleChannel(), createAndHandleChannel()
+ */
+PendingChannel *Account::createAndHandleConferenceStreamedMediaCall(
+ const QList<ChannelPtr> &channels,
+ const QStringList &initialInviteeContactsIdentifiers,
+ const QDateTime &userActionTime)
+{
+ QVariantMap request = conferenceStreamedMediaCallRequest(channels,
+ initialInviteeContactsIdentifiers);
+
+ return createAndHandleChannel(request, userActionTime);
+}
+
+/**
+ * Start a request to create a conference media call with the given
+ * channels \a channels.
+ * This initially just creates a PendingChannel object,
+ * which can be used to track the success or failure of the request.
+ *
+ * \param channels The conference channels.
+ * \param initialInviteeContacts A list of additional contacts
+ * to be invited to this
+ * conference when it is created.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \return A PendingChannel which will emit PendingChannel::finished
+ * successfully, when the Channel is available for handling using
+ * PendingChannel::channel(), or with an error if one has been encountered.
+ * \sa ensureAndHandleChannel(), createAndHandleChannel()
+ */
+PendingChannel *Account::createAndHandleConferenceStreamedMediaCall(
+ const QList<ChannelPtr> &channels,
+ const QList<ContactPtr> &initialInviteeContacts,
+ const QDateTime &userActionTime)
+{
+ QVariantMap request = conferenceStreamedMediaCallRequest(channels, initialInviteeContacts);
+
+ return createAndHandleChannel(request, userActionTime);
+}
+
+/**
+ * Start a request to create a contact search channel with the given
+ * server \a server and limit \a limit.
+ * This initially just creates a PendingChannel object,
+ * which can be used to track the success or failure of the request.
+ *
+ * \param server For protocols which support searching for contacts on multiple servers with
+ * different DNS names (like XMPP), the DNS name of the server to be searched,
+ * e.g. "characters.shakespeare.lit". Otherwise, an empty string.
+ * If the protocol does not support specifying a search server, this will be ignored.
+ * \param limit The desired maximum number of results that should be returned by a doing a search.
+ * If the protocol does not support specifying a limit for the number of results
+ * returned at a time, this will be ignored.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \return A PendingChannel which will emit PendingChannel::finished
+ * successfully, when the Channel is available for handling using
+ * PendingChannel::channel(), or with an error if one has been encountered.
+ * \sa ensureAndHandleChannel(), createAndHandleChannel()
+ */
+PendingChannel *Account::createAndHandleContactSearch(
+ const QString &server,
+ uint limit,
+ const QDateTime &userActionTime)
+{
+ QVariantMap request = contactSearchRequest(capabilities(), server, limit);
+
+ return createAndHandleChannel(request, userActionTime);
+}
+
+/**
+ * Same as \c createChannel(request, userActionTime, preferredHandler, ChannelRequestHints())
+ */
+PendingChannelRequest *Account::createChannel(
+ const QVariantMap &request,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler)
+{
+ return createChannel(request, userActionTime, preferredHandler, ChannelRequestHints());
+}
+
+/**
+ * Start a request to create a channel.
+ * This initially just creates a PendingChannelRequest object,
+ * which can be used to track the success or failure of the request,
+ * or to cancel it.
+ *
+ * Helper methods for text chat, text chat room, media call and conference are
+ * provided and should be used if appropriate.
+ *
+ * \param request A dictionary containing desirable properties.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \param preferredHandler Either the well-known bus name (starting with
+ * org.freedesktop.Telepathy.Client.) of the preferred
+ * handler for this channel, or an empty string to
+ * indicate that any handler would be acceptable.
+ * \param hints Arbitrary metadata which will be relayed to the handler if supported,
+ * as indicated by supportsRequestHints().
+ * \return A PendingChannelRequest which will emit PendingChannelRequest::finished
+ * when the request has been made.
+ * \sa createChannel()
+ */
+PendingChannelRequest *Account::createChannel(
+ const QVariantMap &request,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints)
+{
+ return new PendingChannelRequest(AccountPtr(this), request, userActionTime,
+ preferredHandler, true, hints);
+}
+
+/**
+ * Same as \c ensureChannel(request, userActionTime, preferredHandler, ChannelRequestHints())
+ */
+PendingChannelRequest *Account::ensureChannel(
+ const QVariantMap &request,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler)
+{
+ return ensureChannel(request, userActionTime, preferredHandler, ChannelRequestHints());
+}
+
+/**
+ * Start a request to ensure that a channel exists, creating it if necessary.
+ * This initially just creates a PendingChannelRequest object,
+ * which can be used to track the success or failure of the request,
+ * or to cancel it.
+ *
+ * Helper methods for text chat, text chat room, media call and conference are
+ * provided and should be used if appropriate.
+ *
+ * \param request A dictionary containing desirable properties.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \param preferredHandler Either the well-known bus name (starting with
+ * org.freedesktop.Telepathy.Client.) of the preferred
+ * handler for this channel, or an empty string to
+ * indicate that any handler would be acceptable.
+ * \param hints Arbitrary metadata which will be relayed to the handler if supported,
+ * as indicated by supportsRequestHints().
+ * \return A PendingChannelRequest which will emit PendingChannelRequest::finished
+ * when the request has been made.
+ * \sa createChannel()
+ */
+PendingChannelRequest *Account::ensureChannel(
+ const QVariantMap &request,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints)
+{
+ return new PendingChannelRequest(AccountPtr(this), request, userActionTime,
+ preferredHandler, false, hints);
+}
+
+/**
+ * Start a request to create channel.
+ * This initially just creates a PendingChannel object,
+ * which can be used to track the success or failure of the request.
+ *
+ * Helper methods for text chat, text chat room, media call and conference are
+ * provided and should be used if appropriate.
+ *
+ * The caller is responsible for closing the channel with
+ * Channel::requestClose() or Channel::requestLeave() when it has finished handling it.
+ *
+ * A possible error returned by this method is #TP_QT_ERROR_NOT_AVAILABLE, in case a conflicting
+ * channel that matches \a request already exists.
+ *
+ * \param request A dictionary containing desirable properties.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \return A PendingChannel which will emit PendingChannel::finished
+ * successfully, when the Channel is available for handling using
+ * PendingChannel::channel(), or with an error if one has been encountered.
+ * \sa ensureAndHandleChannel()
+ */
+PendingChannel *Account::createAndHandleChannel(
+ const QVariantMap &request,
+ const QDateTime &userActionTime)
+{
+ return new PendingChannel(AccountPtr(this), request, userActionTime, true);
+}
+
+/**
+ * Start a request to ensure that a channel exists, creating it if necessary.
+ * This initially just creates a PendingChannel object,
+ * which can be used to track the success or failure of the request.
+ *
+ * Helper methods for text chat, text chat room, media call and conference are
+ * provided and should be used if appropriate.
+ *
+ * The caller is responsible for closing the channel with
+ * Channel::requestClose() or Channel::requestLeave() when it has finished handling it.
+ *
+ * A possible error returned by this method is #TP_QT_ERROR_NOT_YOURS, in case somebody else is
+ * already handling a channel that matches \a request.
+ *
+ * \param request A dictionary containing desirable properties.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \return A PendingChannel which will emit PendingChannel::finished
+ * successfully, when the Channel is available for handling using
+ * PendingChannel::channel(), or with an error if one has been encountered.
+ * \sa createAndHandleChannel()
+ */
+PendingChannel *Account::ensureAndHandleChannel(
+ const QVariantMap &request,
+ const QDateTime &userActionTime)
+{
+ return new PendingChannel(AccountPtr(this), request, userActionTime, false);
+}
+
+/**
+ * Return the Client::AccountInterface interface proxy object for this account.
+ * This method is protected since the convenience methods provided by this
+ * class should generally be used instead of calling D-Bus methods
+ * directly.
+ *
+ * \return A pointer to the existing Client::AccountInterface object for this
+ * Account object.
+ */
+Client::AccountInterface *Account::baseInterface() const
+{
+ return mPriv->baseInterface;
+}
+
+/**
+ * Return the Client::ChannelDispatcherInterface interface proxy object to use for requesting
+ * channels on this account.
+ *
+ * This method is protected since the convenience methods provided by this
+ * class should generally be used instead of calling D-Bus methods
+ * directly.
+ *
+ * \return A pointer to the existing Client::ChannelDispatcherInterface object for this
+ * Account object.
+ */
+Client::ChannelDispatcherInterface *Account::dispatcherInterface() const
+{
+ return mPriv->dispatcherContext->iface;
+}
+
+/**** Private ****/
+void Account::Private::init()
+{
+ if (!parent->isValid()) {
+ return;
+ }
+
+ parent->connect(baseInterface,
+ SIGNAL(Removed()),
+ SLOT(onRemoved()));
+ parent->connect(baseInterface,
+ SIGNAL(AccountPropertyChanged(QVariantMap)),
+ SLOT(onPropertyChanged(QVariantMap)));
+}
+
+void Account::Private::introspectMain(Account::Private *self)
+{
+ if (self->dispatcherContext->introspected) {
+ self->parent->onDispatcherIntrospected(0);
+ return;
+ }
+
+ if (!self->dispatcherContext->introspectOp) {
+ debug() << "Discovering if the Channel Dispatcher supports request hints";
+ self->dispatcherContext->introspectOp =
+ self->dispatcherContext->iface->requestPropertySupportsRequestHints();
+ }
+
+ connect(self->dispatcherContext->introspectOp.data(),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ self->parent,
+ SLOT(onDispatcherIntrospected(Tp::PendingOperation*)));
+}
+
+void Account::Private::introspectAvatar(Account::Private *self)
+{
+ debug() << "Calling GetAvatar(Account)";
+ // we already checked if avatar interface exists, so bypass avatar interface
+ // checking
+ Client::AccountInterfaceAvatarInterface *iface =
+ self->parent->interface<Client::AccountInterfaceAvatarInterface>();
+
+ // If we are here it means the user cares about avatar, so
+ // connect to avatar changed signal, so we update the avatar
+ // when it changes.
+ self->parent->connect(iface,
+ SIGNAL(AvatarChanged()),
+ SLOT(onAvatarChanged()));
+
+ self->retrieveAvatar();
+}
+
+void Account::Private::introspectProtocolInfo(Account::Private *self)
+{
+ Q_ASSERT(!self->cm);
+
+ self->cm = ConnectionManager::create(
+ self->parent->dbusConnection(), self->cmName,
+ self->connFactory, self->chanFactory, self->contactFactory);
+ self->parent->connect(self->cm->becomeReady(),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onConnectionManagerReady(Tp::PendingOperation*)));
+}
+
+void Account::Private::introspectCapabilities(Account::Private *self)
+{
+ if (!self->connection) {
+ // there is no connection, just make capabilities ready
+ self->readinessHelper->setIntrospectCompleted(FeatureCapabilities, true);
+ return;
+ }
+
+ self->parent->connect(self->connection->becomeReady(),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onConnectionReady(Tp::PendingOperation*)));
+}
+
+void Account::Private::updateProperties(const QVariantMap &props)
+{
+ debug() << "Account::updateProperties: changed:";
+
+ if (props.contains(QLatin1String("Interfaces"))) {
+ parent->setInterfaces(qdbus_cast<QStringList>(props[QLatin1String("Interfaces")]));
+ debug() << " Interfaces:" << parent->interfaces();
+ }
+
+ QString oldIconName = parent->iconName();
+ bool serviceNameChanged = false;
+ bool profileChanged = false;
+ if (props.contains(QLatin1String("Service")) &&
+ serviceName != qdbus_cast<QString>(props[QLatin1String("Service")])) {
+ serviceNameChanged = true;
+ serviceName = qdbus_cast<QString>(props[QLatin1String("Service")]);
+ debug() << " Service Name:" << parent->serviceName();
+ /* use parent->serviceName() here as if the service name is empty we are going to use the
+ * protocol name */
+ emit parent->serviceNameChanged(parent->serviceName());
+ parent->notify("serviceName");
+
+ /* if we had a profile and the service changed, it means the profile also changed */
+ if (parent->isReady(Account::FeatureProfile)) {
+ /* service name changed, let's recreate profile */
+ profileChanged = true;
+ profile.reset();
+ emit parent->profileChanged(parent->profile());
+ parent->notify("profile");
+ }
+ }
+
+ if (props.contains(QLatin1String("DisplayName")) &&
+ displayName != qdbus_cast<QString>(props[QLatin1String("DisplayName")])) {
+ displayName = qdbus_cast<QString>(props[QLatin1String("DisplayName")]);
+ debug() << " Display Name:" << displayName;
+ emit parent->displayNameChanged(displayName);
+ parent->notify("displayName");
+ }
+
+ if ((props.contains(QLatin1String("Icon")) &&
+ oldIconName != qdbus_cast<QString>(props[QLatin1String("Icon")])) ||
+ serviceNameChanged) {
+
+ if (props.contains(QLatin1String("Icon"))) {
+ iconName = qdbus_cast<QString>(props[QLatin1String("Icon")]);
+ }
+
+ QString newIconName = parent->iconName();
+ if (oldIconName != newIconName) {
+ debug() << " Icon:" << newIconName;
+ emit parent->iconNameChanged(newIconName);
+ parent->notify("iconName");
+ }
+ }
+
+ if (props.contains(QLatin1String("Nickname")) &&
+ nickname != qdbus_cast<QString>(props[QLatin1String("Nickname")])) {
+ nickname = qdbus_cast<QString>(props[QLatin1String("Nickname")]);
+ debug() << " Nickname:" << nickname;
+ emit parent->nicknameChanged(nickname);
+ parent->notify("nickname");
+ }
+
+ if (props.contains(QLatin1String("NormalizedName")) &&
+ normalizedName != qdbus_cast<QString>(props[QLatin1String("NormalizedName")])) {
+ normalizedName = qdbus_cast<QString>(props[QLatin1String("NormalizedName")]);
+ debug() << " Normalized Name:" << normalizedName;
+ emit parent->normalizedNameChanged(normalizedName);
+ parent->notify("normalizedName");
+ }
+
+ if (props.contains(QLatin1String("Valid")) &&
+ valid != qdbus_cast<bool>(props[QLatin1String("Valid")])) {
+ valid = qdbus_cast<bool>(props[QLatin1String("Valid")]);
+ debug() << " Valid:" << (valid ? "true" : "false");
+ emit parent->validityChanged(valid);
+ parent->notify("valid");
+ }
+
+ if (props.contains(QLatin1String("Enabled")) &&
+ enabled != qdbus_cast<bool>(props[QLatin1String("Enabled")])) {
+ enabled = qdbus_cast<bool>(props[QLatin1String("Enabled")]);
+ debug() << " Enabled:" << (enabled ? "true" : "false");
+ emit parent->stateChanged(enabled);
+ parent->notify("enabled");
+ }
+
+ if (props.contains(QLatin1String("ConnectAutomatically")) &&
+ connectsAutomatically !=
+ qdbus_cast<bool>(props[QLatin1String("ConnectAutomatically")])) {
+ connectsAutomatically =
+ qdbus_cast<bool>(props[QLatin1String("ConnectAutomatically")]);
+ debug() << " Connects Automatically:" << (connectsAutomatically ? "true" : "false");
+ emit parent->connectsAutomaticallyPropertyChanged(connectsAutomatically);
+ parent->notify("connectsAutomatically");
+ }
+
+ if (props.contains(QLatin1String("HasBeenOnline")) &&
+ !hasBeenOnline &&
+ qdbus_cast<bool>(props[QLatin1String("HasBeenOnline")])) {
+ hasBeenOnline = true;
+ debug() << " HasBeenOnline changed to true";
+ // don't emit firstOnline unless we're already ready, that would be
+ // misleading - we'd emit it just before any already-used account
+ // became ready
+ if (parent->isReady(Account::FeatureCore)) {
+ emit parent->firstOnline();
+ }
+ parent->notify("hasBeenOnline");
+ }
+
+ if (props.contains(QLatin1String("Parameters")) &&
+ parameters != qdbus_cast<QVariantMap>(props[QLatin1String("Parameters")])) {
+ parameters = qdbus_cast<QVariantMap>(props[QLatin1String("Parameters")]);
+ emit parent->parametersChanged(parameters);
+ parent->notify("parameters");
+ }
+
+ if (props.contains(QLatin1String("AutomaticPresence")) &&
+ automaticPresence.barePresence() != qdbus_cast<SimplePresence>(
+ props[QLatin1String("AutomaticPresence")])) {
+ automaticPresence = Presence(qdbus_cast<SimplePresence>(
+ props[QLatin1String("AutomaticPresence")]));
+ debug() << " Automatic Presence:" << automaticPresence.type() <<
+ "-" << automaticPresence.status();
+ emit parent->automaticPresenceChanged(automaticPresence);
+ parent->notify("automaticPresence");
+ }
+
+ if (props.contains(QLatin1String("CurrentPresence")) &&
+ currentPresence.barePresence() != qdbus_cast<SimplePresence>(
+ props[QLatin1String("CurrentPresence")])) {
+ currentPresence = Presence(qdbus_cast<SimplePresence>(
+ props[QLatin1String("CurrentPresence")]));
+ debug() << " Current Presence:" << currentPresence.type() <<
+ "-" << currentPresence.status();
+ emit parent->currentPresenceChanged(currentPresence);
+ parent->notify("currentPresence");
+ emit parent->onlinenessChanged(parent->isOnline());
+ parent->notify("online");
+ }
+
+ if (props.contains(QLatin1String("RequestedPresence")) &&
+ requestedPresence.barePresence() != qdbus_cast<SimplePresence>(
+ props[QLatin1String("RequestedPresence")])) {
+ requestedPresence = Presence(qdbus_cast<SimplePresence>(
+ props[QLatin1String("RequestedPresence")]));
+ debug() << " Requested Presence:" << requestedPresence.type() <<
+ "-" << requestedPresence.status();
+ emit parent->requestedPresenceChanged(requestedPresence);
+ parent->notify("requestedPresence");
+ }
+
+ if (props.contains(QLatin1String("ChangingPresence")) &&
+ changingPresence != qdbus_cast<bool>(
+ props[QLatin1String("ChangingPresence")])) {
+ changingPresence = qdbus_cast<bool>(
+ props[QLatin1String("ChangingPresence")]);
+ debug() << " Changing Presence:" << changingPresence;
+ emit parent->changingPresence(changingPresence);
+ parent->notify("changingPresence");
+ }
+
+ if (props.contains(QLatin1String("Connection"))) {
+ QString path = qdbus_cast<QDBusObjectPath>(props[QLatin1String("Connection")]).path();
+ if (path.isEmpty()) {
+ debug() << " The map contains \"Connection\" but it's empty as a QDBusObjectPath!";
+ debug() << " Trying QString (known bug in some MC/dbus-glib versions)";
+ path = qdbus_cast<QString>(props[QLatin1String("Connection")]);
+ }
+
+ debug() << " Connection Object Path:" << path;
+ if (path == QLatin1String("/")) {
+ path = QString();
+ }
+
+ connObjPathQueue.enqueue(path);
+
+ if (connObjPathQueue.size() == 1) {
+ processConnQueue();
+ }
+
+ // onConnectionBuilt for a previous path will make sure the path we enqueued is processed if
+ // the queue wasn't empty (so is now size() > 1)
+ }
+
+ bool connectionStatusChanged = false;
+ if (props.contains(QLatin1String("ConnectionStatus")) ||
+ props.contains(QLatin1String("ConnectionStatusReason")) ||
+ props.contains(QLatin1String("ConnectionError")) ||
+ props.contains(QLatin1String("ConnectionErrorDetails"))) {
+ ConnectionStatus oldConnectionStatus = connectionStatus;
+
+ if (props.contains(QLatin1String("ConnectionStatus")) &&
+ connectionStatus != ConnectionStatus(
+ qdbus_cast<uint>(props[QLatin1String("ConnectionStatus")]))) {
+ connectionStatus = ConnectionStatus(
+ qdbus_cast<uint>(props[QLatin1String("ConnectionStatus")]));
+ debug() << " Connection Status:" << connectionStatus;
+ connectionStatusChanged = true;
+ }
+
+ if (props.contains(QLatin1String("ConnectionStatusReason")) &&
+ connectionStatusReason != ConnectionStatusReason(
+ qdbus_cast<uint>(props[QLatin1String("ConnectionStatusReason")]))) {
+ connectionStatusReason = ConnectionStatusReason(
+ qdbus_cast<uint>(props[QLatin1String("ConnectionStatusReason")]));
+ debug() << " Connection StatusReason:" << connectionStatusReason;
+ connectionStatusChanged = true;
+ }
+
+ if (connectionStatusChanged) {
+ parent->notify("connectionStatus");
+ parent->notify("connectionStatusReason");
+ }
+
+ if (props.contains(QLatin1String("ConnectionError")) &&
+ connectionError != qdbus_cast<QString>(
+ props[QLatin1String("ConnectionError")])) {
+ connectionError = qdbus_cast<QString>(
+ props[QLatin1String("ConnectionError")]);
+ debug() << " Connection Error:" << connectionError;
+ connectionStatusChanged = true;
+ }
+
+ if (props.contains(QLatin1String("ConnectionErrorDetails")) &&
+ connectionErrorDetails.allDetails() != qdbus_cast<QVariantMap>(
+ props[QLatin1String("ConnectionErrorDetails")])) {
+ connectionErrorDetails = Connection::ErrorDetails(qdbus_cast<QVariantMap>(
+ props[QLatin1String("ConnectionErrorDetails")]));
+ debug() << " Connection Error Details:" << connectionErrorDetails.allDetails();
+ connectionStatusChanged = true;
+ }
+
+ if (connectionStatusChanged) {
+ /* Something other than status changed, let's not emit connectionStatusChanged
+ * and keep the error/errorDetails, for the next interaction.
+ * It may happen if ConnectionError changes and in another property
+ * change the status changes to Disconnected, so we use the error
+ * previously signalled. If the status changes to something other
+ * than Disconnected later, the error is cleared. */
+ if (oldConnectionStatus != connectionStatus) {
+ /* We don't signal error for status other than Disconnected */
+ if (connectionStatus != ConnectionStatusDisconnected) {
+ connectionError = QString();
+ connectionErrorDetails = Connection::ErrorDetails();
+ } else if (connectionError.isEmpty()) {
+ connectionError = ConnectionHelper::statusReasonToErrorName(
+ connectionStatusReason, oldConnectionStatus);
+ }
+
+ checkCapabilitiesChanged(profileChanged);
+
+ emit parent->connectionStatusChanged(connectionStatus);
+ parent->notify("connectionError");
+ parent->notify("connectionErrorDetails");
+ } else {
+ connectionStatusChanged = false;
+ }
+ }
+ }
+
+ if (!connectionStatusChanged && profileChanged) {
+ checkCapabilitiesChanged(profileChanged);
+ }
+}
+
+void Account::Private::retrieveAvatar()
+{
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+ parent->mPriv->properties->Get(
+ QLatin1String(TELEPATHY_INTERFACE_ACCOUNT_INTERFACE_AVATAR),
+ QLatin1String("Avatar")), parent);
+ parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotAvatar(QDBusPendingCallWatcher*)));
+}
+
+bool Account::Private::processConnQueue()
+{
+ while (!connObjPathQueue.isEmpty()) {
+ QString path = connObjPathQueue.head();
+ if (path.isEmpty()) {
+ if (!connection.isNull()) {
+ debug() << "Dropping connection for account" << parent->objectPath();
+
+ connection.reset();
+ emit parent->connectionChanged(connection);
+ parent->notify("connection");
+ parent->notify("connectionObjectPath");
+ }
+
+ connObjPathQueue.dequeue();
+ } else {
+ debug() << "Building connection" << path << "for account" << parent->objectPath();
+
+ if (connection && connection->objectPath() == path) {
+ debug() << " Connection already built";
+ connObjPathQueue.dequeue();
+ continue;
+ }
+
+ QString busName = path.mid(1).replace(QLatin1String("/"), QLatin1String("."));
+ parent->connect(connFactory->proxy(busName, path, chanFactory, contactFactory),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onConnectionBuilt(Tp::PendingOperation*)));
+
+ // No dequeue here, but only in onConnectionBuilt, so we will queue future changes
+ return false; // Only move on to the next paths when that build finishes
+ }
+ }
+
+ return true;
+}
+
+void Account::onDispatcherIntrospected(Tp::PendingOperation *op)
+{
+ if (!mPriv->dispatcherContext->introspected) {
+ Tp::PendingVariant *pv = static_cast<Tp::PendingVariant *>(op);
+ Q_ASSERT(pv != NULL);
+
+ // Only the first Account for a given dispatcher will enter this branch, and will
+ // immediately make further created accounts skip the whole waiting for CD to get
+ // introspected part entirely
+ mPriv->dispatcherContext->introspected = true;
+
+ if (pv->isValid()) {
+ mPriv->dispatcherContext->supportsHints = qdbus_cast<bool>(pv->result());
+ debug() << "Discovered channel dispatcher support for request hints: "
+ << mPriv->dispatcherContext->supportsHints;
+ } else {
+ if (pv->errorName() == TP_QT_ERROR_NOT_IMPLEMENTED) {
+ debug() << "Channel Dispatcher does not implement support for request hints";
+ } else {
+ warning() << "(Too old?) Channel Dispatcher failed to tell us whether"
+ << "it supports request hints, assuming it doesn't:"
+ << pv->errorName() << ':' << pv->errorMessage();
+ }
+ mPriv->dispatcherContext->supportsHints = false;
+ }
+ }
+
+ debug() << "Calling Properties::GetAll(Account) on " << objectPath();
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+ mPriv->properties->GetAll(
+ QLatin1String(TELEPATHY_INTERFACE_ACCOUNT)), this);
+ connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotMainProperties(QDBusPendingCallWatcher*)));
+}
+
+void Account::gotMainProperties(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QVariantMap> reply = *watcher;
+
+ if (!reply.isError()) {
+ debug() << "Got reply to Properties.GetAll(Account) for" << objectPath();
+ mPriv->updateProperties(reply.value());
+
+ mPriv->readinessHelper->setInterfaces(interfaces());
+ mPriv->mayFinishCore = true;
+
+ if (mPriv->connObjPathQueue.isEmpty()) {
+ debug() << "Account basic functionality is ready";
+ mPriv->coreFinished = true;
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureCore, true);
+ } else {
+ debug() << "Deferring finishing Account::FeatureCore until the connection is built";
+ }
+ } else {
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureCore, false, reply.error());
+
+ warning().nospace() <<
+ "GetAll(Account) failed: " <<
+ reply.error().name() << ": " << reply.error().message();
+ }
+
+ watcher->deleteLater();
+}
+
+void Account::gotAvatar(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QVariant> reply = *watcher;
+
+ if (!reply.isError()) {
+ debug() << "Got reply to GetAvatar(Account)";
+ mPriv->avatar = qdbus_cast<Avatar>(reply);
+
+ // It could be in either of actual or missing from the first time in corner cases like the
+ // object going away, so let's be prepared for both (only checking for actualFeatures here
+ // actually used to trigger a rare bug)
+ //
+ // Anyway, the idea is to not do setIntrospectCompleted twice
+ if (!mPriv->readinessHelper->actualFeatures().contains(FeatureAvatar) &&
+ !mPriv->readinessHelper->missingFeatures().contains(FeatureAvatar)) {
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureAvatar, true);
+ }
+
+ emit avatarChanged(mPriv->avatar);
+ notify("avatar");
+ } else {
+ // check if the feature is already there, and for some reason retrieveAvatar
+ // failed when called the second time
+ if (!mPriv->readinessHelper->actualFeatures().contains(FeatureAvatar) &&
+ !mPriv->readinessHelper->missingFeatures().contains(FeatureAvatar)) {
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureAvatar, false, reply.error());
+ }
+
+ warning().nospace() <<
+ "GetAvatar(Account) failed: " <<
+ reply.error().name() << ": " << reply.error().message();
+ }
+
+ watcher->deleteLater();
+}
+
+void Account::onAvatarChanged()
+{
+ debug() << "Avatar changed, retrieving it";
+ mPriv->retrieveAvatar();
+}
+
+void Account::onConnectionManagerReady(PendingOperation *operation)
+{
+ bool error = operation->isError();
+ if (!error) {
+ error = !mPriv->cm->hasProtocol(mPriv->protocolName);
+ }
+
+ if (!error) {
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureProtocolInfo, true);
+ }
+ else {
+ warning() << "Failed to find the protocol in the CM protocols for account" << objectPath();
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureProtocolInfo, false,
+ operation->errorName(), operation->errorMessage());
+ }
+}
+
+void Account::onConnectionReady(PendingOperation *op)
+{
+ mPriv->checkCapabilitiesChanged(false);
+
+ /* let's not fail if connection can't become ready, the caps will still
+ * work, but return the CM caps instead. Also no need to call
+ * setIntrospectCompleted if the feature was already set to complete once,
+ * since this method will be called whenever the account connection
+ * changes */
+ if (!isReady(FeatureCapabilities)) {
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureCapabilities, true);
+ }
+}
+
+void Account::onPropertyChanged(const QVariantMap &delta)
+{
+ mPriv->updateProperties(delta);
+}
+
+void Account::onRemoved()
+{
+ mPriv->valid = false;
+ mPriv->enabled = false;
+ invalidate(TP_QT_ERROR_OBJECT_REMOVED,
+ QLatin1String("Account removed from AccountManager"));
+ emit removed();
+}
+
+void Account::onConnectionBuilt(PendingOperation *op)
+{
+ PendingReady *readyOp = qobject_cast<PendingReady *>(op);
+ Q_ASSERT(readyOp != NULL);
+
+ if (op->isError()) {
+ warning() << "Building connection" << mPriv->connObjPathQueue.head() << "failed with" <<
+ op->errorName() << "-" << op->errorMessage();
+
+ if (!mPriv->connection.isNull()) {
+ mPriv->connection.reset();
+ emit connectionChanged(mPriv->connection);
+ notify("connection");
+ notify("connectionObjectPath");
+ }
+ } else {
+ ConnectionPtr prevConn = mPriv->connection;
+ QString prevConnPath = mPriv->connectionObjectPath();
+
+ mPriv->connection = ConnectionPtr::qObjectCast(readyOp->proxy());
+ Q_ASSERT(mPriv->connection);
+
+ debug() << "Connection" << mPriv->connectionObjectPath() << "built for" << objectPath();
+
+ if (prevConn != mPriv->connection) {
+ notify("connection");
+ emit connectionChanged(mPriv->connection);
+ }
+
+ if (prevConnPath != mPriv->connectionObjectPath()) {
+ notify("connectionObjectPath");
+ }
+ }
+
+ mPriv->connObjPathQueue.dequeue();
+
+ if (mPriv->processConnQueue() && !mPriv->coreFinished && mPriv->mayFinishCore) {
+ debug() << "Account" << objectPath() << "basic functionality is ready (connections built)";
+ mPriv->coreFinished = true;
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureCore, true);
+ }
+}
+
+/**
+ * \fn void Account::removed()
+ *
+ * Emitted when this account is removed from the account manager it belonged.
+ *
+ * \sa remove().
+ */
+
+/**
+ * \fn void Account::validityChanged(bool validity)
+ *
+ * Emitted when the value of isValidAccount() changes.
+ *
+ * \param validity The new validity of this account.
+ * \sa isValidAccount()
+ */
+
+/**
+ * \fn void Account::stateChanged(bool state)
+ *
+ * Emitted when the value of isEnabled() changes.
+ *
+ * \param state The new state of this account.
+ * \sa isEnabled()
+ */
+
+/**
+ * \fn void Account::serviceNameChanged(const QString &serviceName)
+ *
+ * Emitted when the value of serviceName() changes.
+ *
+ * \param serviceName The new service name of this account.
+ * \sa serviceName(), setServiceName()
+ */
+
+/**
+ * \fn void Account::profileChanged(const Tp::ProfilePtr &profile)
+ *
+ * Emitted when the value of profile() changes.
+ *
+ * \param profile The new profile of this account.
+ * \sa profile()
+ */
+
+/**
+ * \fn void Account::displayNameChanged(const QString &displayName)
+ *
+ * Emitted when the value of displayName() changes.
+ *
+ * \param displayName The new display name of this account.
+ * \sa displayName(), setDisplayName()
+ */
+
+/**
+ * \fn void Account::iconNameChanged(const QString &iconName)
+ *
+ * Emitted when the value of iconName() changes.
+ *
+ * \param iconName The new icon name of this account.
+ * \sa iconName(), setIconName()
+ */
+
+/**
+ * \fn void Account::nicknameChanged(const QString &nickname)
+ *
+ * Emitted when the value of nickname() changes.
+ *
+ * \param nickname The new nickname of this account.
+ * \sa nickname(), setNickname()
+ */
+
+/**
+ * \fn void Account::normalizedNameChanged(const QString &normalizedName)
+ *
+ * Emitted when the value of normalizedName() changes.
+ *
+ * \param normalizedName The new normalized name of this account.
+ * \sa normalizedName()
+ */
+
+/**
+ * \fn void Account::capabilitiesChanged(const Tp::ConnectionCapabilities &capabilities)
+ *
+ * Emitted when the value of capabilities() changes.
+ *
+ * \param capabilities The new capabilities of this account.
+ * \sa capabilities()
+ */
+
+/**
+ * \fn void Account::connectsAutomaticallyPropertyChanged(bool connectsAutomatically)
+ *
+ * Emitted when the value of connectsAutomatically() changes.
+ *
+ * \param connectsAutomatically The new value of connects automatically property
+ * of this account.
+ * \sa isEnabled()
+ */
+
+/**
+ * \fn void Account::firstOnline()
+ *
+ * Emitted when this account is first put online.
+ *
+ * \sa hasBeenOnline()
+ */
+
+/**
+ * \fn void Account::parametersChanged(const QVariantMap &parameters)
+ *
+ * Emitted when the value of parameters() changes.
+ *
+ * \param parameters The new parameters of this account.
+ * \sa parameters()
+ */
+
+/**
+ * \fn void Account::changingPresence(bool value)
+ *
+ * Emitted when the value of isChangingPresence() changes.
+ *
+ * \param value Whether this account's connection is changing presence.
+ * \sa isChangingPresence()
+ */
+
+/**
+ * \fn void Account::automaticPresenceChanged(const Tp::Presence &automaticPresence)
+ *
+ * Emitted when the value of automaticPresence() changes.
+ *
+ * \param automaticPresence The new value of automatic presence property of this
+ * account.
+ * \sa automaticPresence(), currentPresenceChanged()
+ */
+
+/**
+ * \fn void Account::currentPresenceChanged(const Tp::Presence &currentPresence)
+ *
+ * Emitted when the value of currentPresence() changes.
+ *
+ * \param currentPresence The new value of the current presence property of this
+ * account.
+ * \sa currentPresence()
+ */
+
+/**
+ * \fn void Account::requestedPresenceChanged(const Tp::Presence &requestedPresence)
+ *
+ * Emitted when the value of requestedPresence() changes.
+ *
+ * \param requestedPresence The new value of the requested presence property of this
+ * account.
+ * \sa requestedPresence(), currentPresenceChanged()
+ */
+
+/**
+ * \fn void Account::onlinenessChanged(bool online)
+ *
+ * Emitted when the value of isOnline() changes.
+ *
+ * \param online Whether this account is online.
+ * \sa isOnline(), currentPresence()
+ */
+
+/**
+ * \fn void Account::avatarChanged(const Tp::Avatar &avatar)
+ *
+ * Emitted when the value of avatar() changes.
+ *
+ * \param avatar The new avatar of this account.
+ * \sa avatar()
+ */
+
+/**
+ * \fn void Account::connectionStatusChanged(Tp::ConnectionStatus status)
+ *
+ * Emitted when the connection status changes.
+ *
+ * \param status The new status of this account connection.
+ * \sa connectionStatus(), connectionStatusReason(), connectionError(), connectionErrorDetails(),
+ * Connection::ErrorDetails
+ */
+
+/**
+ * \fn void Account::connectionChanged(const Tp::ConnectionPtr &connection)
+ *
+ * Emitted when the value of connection() changes.
+ *
+ * The \a connection will have the features set in the ConnectionFactory used by this account ready
+ * and the same channel and contact factories used by this account.
+ *
+ * \param connection A ConnectionPtr pointing to the new Connection object or a null ConnectionPtr
+ * if there is no connection.
+ * \sa connection()
+ */
+
+} // Tp
diff --git a/TelepathyQt/account.h b/TelepathyQt/account.h
new file mode 100644
index 00000000..4ca2d8fe
--- /dev/null
+++ b/TelepathyQt/account.h
@@ -0,0 +1,598 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_account_h_HEADER_GUARD_
+#define _TelepathyQt_account_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/_gen/cli-account.h>
+
+#include <TelepathyQt/ChannelRequestHints>
+#include <TelepathyQt/Connection>
+#include <TelepathyQt/ConnectionCapabilities>
+#include <TelepathyQt/ConnectionFactory>
+#include <TelepathyQt/ContactFactory>
+#include <TelepathyQt/ChannelFactory>
+#include <TelepathyQt/ChannelDispatcherInterface>
+#include <TelepathyQt/DBus>
+#include <TelepathyQt/DBusProxy>
+#include <TelepathyQt/FileTransferChannelCreationProperties>
+#include <TelepathyQt/OptionalInterfaceFactory>
+#include <TelepathyQt/Presence>
+#include <TelepathyQt/PresenceSpec>
+#include <TelepathyQt/ProtocolInfo>
+#include <TelepathyQt/ReadinessHelper>
+#include <TelepathyQt/Types>
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/SharedPtr>
+
+#include <QSet>
+#include <QString>
+#include <QStringList>
+#include <QVariantMap>
+
+namespace Tp
+{
+
+class Account;
+class Connection;
+class PendingChannel;
+class PendingChannelRequest;
+class PendingConnection;
+class PendingOperation;
+class PendingReady;
+class PendingStringList;
+
+class TP_QT_EXPORT Account : public StatelessDBusProxy,
+ public OptionalInterfaceFactory<Account>
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(Account)
+ Q_PROPERTY(bool valid READ isValidAccount NOTIFY validityChanged)
+ Q_PROPERTY(bool enabled READ isEnabled NOTIFY stateChanged)
+ Q_PROPERTY(QString cmName READ cmName)
+ Q_PROPERTY(QString protocolName READ protocolName)
+ Q_PROPERTY(QString serviceName READ serviceName NOTIFY serviceNameChanged)
+ Q_PROPERTY(ProfilePtr profile READ profile NOTIFY profileChanged)
+ Q_PROPERTY(QString displayName READ displayName NOTIFY displayNameChanged)
+ Q_PROPERTY(QString iconName READ iconName NOTIFY iconNameChanged)
+ Q_PROPERTY(QString nickname READ nickname NOTIFY nicknameChanged)
+ Q_PROPERTY(AvatarSpec avatarRequirements READ avatarRequirements)
+ Q_PROPERTY(Avatar avatar READ avatar NOTIFY avatarChanged)
+ Q_PROPERTY(QVariantMap parameters READ parameters NOTIFY parametersChanged)
+ Q_PROPERTY(ProtocolInfo protocolInfo READ protocolInfo)
+ Q_PROPERTY(ConnectionCapabilities capabilities READ capabilities NOTIFY capabilitiesChanged)
+ Q_PROPERTY(bool hasBeenOnline READ hasBeenOnline)
+ Q_PROPERTY(bool connectsAutomatically READ connectsAutomatically NOTIFY connectsAutomaticallyPropertyChanged)
+ Q_PROPERTY(ConnectionStatus connectionStatus READ connectionStatus NOTIFY connectionStatusChanged)
+ Q_PROPERTY(ConnectionStatusReason connectionStatusReason READ connectionStatusReason)
+ Q_PROPERTY(QString connectionError READ connectionError)
+ Q_PROPERTY(Tp::Connection::ErrorDetails connectionErrorDetails READ connectionErrorDetails)
+ Q_PROPERTY(ConnectionPtr connection READ connection NOTIFY connectionChanged)
+ Q_PROPERTY(bool changingPresence READ isChangingPresence NOTIFY changingPresence)
+ Q_PROPERTY(Presence automaticPresence READ automaticPresence NOTIFY automaticPresenceChanged)
+ Q_PROPERTY(Presence currentPresence READ currentPresence NOTIFY currentPresenceChanged)
+ Q_PROPERTY(Presence requestedPresence READ requestedPresence NOTIFY requestedPresenceChanged)
+ Q_PROPERTY(bool online READ isOnline NOTIFY onlinenessChanged)
+ Q_PROPERTY(QString uniqueIdentifier READ uniqueIdentifier)
+ Q_PROPERTY(QString normalizedName READ normalizedName NOTIFY normalizedNameChanged)
+
+public:
+ static const Feature FeatureCore;
+ static const Feature FeatureAvatar;
+ static const Feature FeatureProtocolInfo;
+ static const Feature FeatureCapabilities;
+ static const Feature FeatureProfile;
+
+ static AccountPtr create(const QString &busName, const QString &objectPath,
+ const ConnectionFactoryConstPtr &connectionFactory =
+ ConnectionFactory::create(QDBusConnection::sessionBus()),
+ const ChannelFactoryConstPtr &channelFactory =
+ ChannelFactory::create(QDBusConnection::sessionBus()),
+ const ContactFactoryConstPtr &contactFactory =
+ ContactFactory::create());
+ static AccountPtr create(const QDBusConnection &bus,
+ const QString &busName, const QString &objectPath,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory =
+ ContactFactory::create());
+ virtual ~Account();
+
+ ConnectionFactoryConstPtr connectionFactory() const;
+ ChannelFactoryConstPtr channelFactory() const;
+ ContactFactoryConstPtr contactFactory() const;
+
+ bool isValidAccount() const;
+
+ bool isEnabled() const;
+ PendingOperation *setEnabled(bool value);
+
+ QString cmName() const;
+
+ QString protocolName() const;
+
+ QString serviceName() const;
+ PendingOperation *setServiceName(const QString &value);
+
+ ProfilePtr profile() const;
+
+ QString displayName() const;
+ PendingOperation *setDisplayName(const QString &value);
+
+ QString iconName() const;
+ PendingOperation *setIconName(const QString &value);
+
+ QString nickname() const;
+ PendingOperation *setNickname(const QString &value);
+
+ AvatarSpec avatarRequirements() const;
+ // TODO: We probably want to expose the avatar file name once we have the avatar token and MC
+ // starts sharing the cache used by tp-qt4 and tp-glib and use Tp::AvatarData to represent
+ // it as used in Tp::Contact
+ const Avatar &avatar() const;
+ PendingOperation *setAvatar(const Avatar &avatar);
+
+ QVariantMap parameters() const;
+ PendingStringList *updateParameters(const QVariantMap &set,
+ const QStringList &unset);
+
+ ProtocolInfo protocolInfo() const;
+
+ ConnectionCapabilities capabilities() const;
+
+ bool connectsAutomatically() const;
+ PendingOperation *setConnectsAutomatically(bool value);
+
+ bool hasBeenOnline() const;
+
+ ConnectionStatus connectionStatus() const;
+ ConnectionStatusReason connectionStatusReason() const;
+ QString connectionError() const;
+ Connection::ErrorDetails connectionErrorDetails() const;
+ ConnectionPtr connection() const;
+
+ bool isChangingPresence() const;
+
+ PresenceSpecList allowedPresenceStatuses(bool includeAllStatuses = false) const;
+ uint maxPresenceStatusMessageLength() const;
+
+ // TODO: Add overload methods to set presence from a Profile::Presence
+ // TODO: Add usablePresences() that would return a list of presences that could be set on the
+ // account
+ Presence automaticPresence() const;
+ PendingOperation *setAutomaticPresence(const Presence &presence);
+
+ Presence currentPresence() const;
+
+ Presence requestedPresence() const;
+ PendingOperation *setRequestedPresence(const Presence &presence);
+
+ bool isOnline() const;
+
+ QString uniqueIdentifier() const;
+
+ QString normalizedName() const;
+
+ PendingOperation *reconnect();
+
+ PendingOperation *remove();
+
+ bool supportsRequestHints() const;
+ bool requestsSucceedWithChannel() const;
+
+ // TODO ABI break: collapse all of the overloads without a hints arg with the corresponding
+ // hints versions and add a default param for the hints args
+
+ PendingChannelRequest *ensureTextChat(
+ const QString &contactIdentifier,
+ const QDateTime &userActionTime = QDateTime::currentDateTime(),
+ const QString &preferredHandler = QString());
+ PendingChannelRequest *ensureTextChat(
+ const QString &contactIdentifier,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints);
+
+ PendingChannelRequest *ensureTextChat(
+ const ContactPtr &contact,
+ const QDateTime &userActionTime = QDateTime::currentDateTime(),
+ const QString &preferredHandler = QString());
+ PendingChannelRequest *ensureTextChat(
+ const ContactPtr &contact,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints);
+
+ PendingChannelRequest *ensureTextChatroom(
+ const QString &roomName,
+ const QDateTime &userActionTime = QDateTime::currentDateTime(),
+ const QString &preferredHandler = QString());
+ PendingChannelRequest *ensureTextChatroom(
+ const QString &roomName,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints);
+
+ PendingChannelRequest *ensureStreamedMediaCall(
+ const QString &contactIdentifier,
+ const QDateTime &userActionTime = QDateTime::currentDateTime(),
+ const QString &preferredHandler = QString());
+ PendingChannelRequest *ensureStreamedMediaCall(
+ const QString &contactIdentifier,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints);
+
+ PendingChannelRequest *ensureStreamedMediaCall(
+ const ContactPtr &contact,
+ const QDateTime &userActionTime = QDateTime::currentDateTime(),
+ const QString &preferredHandler = QString());
+ PendingChannelRequest *ensureStreamedMediaCall(
+ const ContactPtr &contact,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints);
+
+ PendingChannelRequest *ensureStreamedMediaAudioCall(
+ const QString &contactIdentifier,
+ QDateTime userActionTime = QDateTime::currentDateTime(),
+ const QString &preferredHandler = QString());
+ PendingChannelRequest *ensureStreamedMediaAudioCall(
+ const QString &contactIdentifier,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints);
+
+ PendingChannelRequest *ensureStreamedMediaAudioCall(
+ const ContactPtr &contact,
+ QDateTime userActionTime = QDateTime::currentDateTime(),
+ const QString &preferredHandler = QString());
+ PendingChannelRequest *ensureStreamedMediaAudioCall(
+ const ContactPtr &contact,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints);
+
+ PendingChannelRequest *ensureStreamedMediaVideoCall(
+ const QString &contactIdentifier,
+ bool withAudio = true,
+ QDateTime userActionTime = QDateTime::currentDateTime(),
+ const QString &preferredHandler = QString());
+ PendingChannelRequest *ensureStreamedMediaVideoCall(
+ const QString &contactIdentifier,
+ bool withAudio,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints);
+
+ PendingChannelRequest *ensureStreamedMediaVideoCall(
+ const ContactPtr &contact,
+ bool withAudio = true,
+ QDateTime userActionTime = QDateTime::currentDateTime(),
+ const QString &preferredHandler = QString());
+ PendingChannelRequest *ensureStreamedMediaVideoCall(
+ const ContactPtr &contact,
+ bool withAudio,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints);
+
+ PendingChannelRequest *createFileTransfer(
+ const QString &contactIdentifier,
+ const FileTransferChannelCreationProperties &properties,
+ const QDateTime &userActionTime = QDateTime::currentDateTime(),
+ const QString &preferredHandler = QString());
+ PendingChannelRequest *createFileTransfer(
+ const QString &contactIdentifier,
+ const FileTransferChannelCreationProperties &properties,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints);
+
+ PendingChannelRequest *createFileTransfer(
+ const ContactPtr &contact,
+ const FileTransferChannelCreationProperties &properties,
+ const QDateTime &userActionTime = QDateTime::currentDateTime(),
+ const QString &preferredHandler = QString());
+ PendingChannelRequest *createFileTransfer(
+ const ContactPtr &contact,
+ const FileTransferChannelCreationProperties &properties,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints);
+
+ PendingChannelRequest *createStreamTube(
+ const QString &contactIdentifier,
+ const QString &service,
+ const QDateTime &userActionTime = QDateTime::currentDateTime(),
+ const QString &preferredHandler = QString(),
+ const ChannelRequestHints &hints = ChannelRequestHints());
+ PendingChannelRequest *createStreamTube(
+ const ContactPtr &contact,
+ const QString &service,
+ const QDateTime &userActionTime = QDateTime::currentDateTime(),
+ const QString &preferredHandler = QString(),
+ const ChannelRequestHints &hints = ChannelRequestHints());
+
+ PendingChannelRequest *createConferenceStreamedMediaCall(
+ const QList<ChannelPtr> &channels,
+ const QStringList &initialInviteeContactsIdentifiers = QStringList(),
+ const QDateTime &userActionTime = QDateTime::currentDateTime(),
+ const QString &preferredHandler = QString());
+ PendingChannelRequest *createConferenceStreamedMediaCall(
+ const QList<ChannelPtr> &channels,
+ const QStringList &initialInviteeContactsIdentifiers,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints);
+
+ PendingChannelRequest *createConferenceStreamedMediaCall(
+ const QList<ChannelPtr> &channels,
+ const QList<ContactPtr> &initialInviteeContacts = QList<ContactPtr>(),
+ const QDateTime &userActionTime = QDateTime::currentDateTime(),
+ const QString &preferredHandler = QString());
+ PendingChannelRequest *createConferenceStreamedMediaCall(
+ const QList<ChannelPtr> &channels,
+ const QList<ContactPtr> &initialInviteeContacts,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints);
+
+ PendingChannelRequest *createConferenceTextChat(
+ const QList<ChannelPtr> &channels,
+ const QList<ContactPtr> &initialInviteeContacts = QList<ContactPtr>(),
+ const QDateTime &userActionTime = QDateTime::currentDateTime(),
+ const QString &preferredHandler = QString());
+ PendingChannelRequest *createConferenceTextChat(
+ const QList<ChannelPtr> &channels,
+ const QList<ContactPtr> &initialInviteeContacts,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints);
+
+ PendingChannelRequest *createConferenceTextChat(
+ const QList<ChannelPtr> &channels,
+ const QStringList &initialInviteeContactsIdentifiers = QStringList(),
+ const QDateTime &userActionTime = QDateTime::currentDateTime(),
+ const QString &preferredHandler = QString());
+ PendingChannelRequest *createConferenceTextChat(
+ const QList<ChannelPtr> &channels,
+ const QStringList &initialInviteeContactsIdentifiers,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints);
+
+ TP_QT_DEPRECATED PendingChannelRequest *createConferenceTextChatRoom(
+ const QString &roomName,
+ const QList<ChannelPtr> &channels,
+ const QStringList &initialInviteeContactsIdentifiers = QStringList(),
+ const QDateTime &userActionTime = QDateTime::currentDateTime(),
+ const QString &preferredHandler = QString());
+ PendingChannelRequest *createConferenceTextChatroom(
+ const QString &roomName,
+ const QList<ChannelPtr> &channels,
+ const QStringList &initialInviteeContactsIdentifiers = QStringList(),
+ const QDateTime &userActionTime = QDateTime::currentDateTime(),
+ const QString &preferredHandler = QString(),
+ const ChannelRequestHints &hints = ChannelRequestHints());
+
+ TP_QT_DEPRECATED PendingChannelRequest *createConferenceTextChatRoom(
+ const QString &roomName,
+ const QList<ChannelPtr> &channels,
+ const QList<ContactPtr> &initialInviteeContacts = QList<ContactPtr>(),
+ const QDateTime &userActionTime = QDateTime::currentDateTime(),
+ const QString &preferredHandler = QString());
+ PendingChannelRequest *createConferenceTextChatroom(
+ const QString &roomName,
+ const QList<ChannelPtr> &channels,
+ const QList<ContactPtr> &initialInviteeContacts = QList<ContactPtr>(),
+ const QDateTime &userActionTime = QDateTime::currentDateTime(),
+ const QString &preferredHandler = QString(),
+ const ChannelRequestHints &hints = ChannelRequestHints());
+
+ PendingChannelRequest *createContactSearch(
+ const QString &server = QString(),
+ uint limit = 0,
+ const QDateTime &userActionTime = QDateTime::currentDateTime(),
+ const QString &preferredHandler = QString());
+ PendingChannelRequest *createContactSearch(
+ const QString &server,
+ uint limit,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints);
+
+ PendingChannel *ensureAndHandleTextChat(
+ const QString &contactIdentifier,
+ const QDateTime &userActionTime = QDateTime::currentDateTime());
+ PendingChannel *ensureAndHandleTextChat(
+ const ContactPtr &contact,
+ const QDateTime &userActionTime = QDateTime::currentDateTime());
+
+ PendingChannel *ensureAndHandleTextChatroom(
+ const QString &roomName,
+ const QDateTime &userActionTime = QDateTime::currentDateTime());
+
+ PendingChannel *ensureAndHandleStreamedMediaCall(
+ const QString &contactIdentifier,
+ const QDateTime &userActionTime = QDateTime::currentDateTime());
+ PendingChannel *ensureAndHandleStreamedMediaCall(
+ const ContactPtr &contact,
+ const QDateTime &userActionTime = QDateTime::currentDateTime());
+
+ PendingChannel *ensureAndHandleStreamedMediaAudioCall(
+ const QString &contactIdentifier,
+ const QDateTime &userActionTime = QDateTime::currentDateTime());
+ PendingChannel *ensureAndHandleStreamedMediaAudioCall(
+ const ContactPtr &contact,
+ const QDateTime &userActionTime = QDateTime::currentDateTime());
+
+ PendingChannel *ensureAndHandleStreamedMediaVideoCall(
+ const QString &contactIdentifier,
+ bool withAudio = true,
+ const QDateTime &userActionTime = QDateTime::currentDateTime());
+ PendingChannel *ensureAndHandleStreamedMediaVideoCall(
+ const ContactPtr &contact,
+ bool withAudio = true,
+ const QDateTime &userActionTime = QDateTime::currentDateTime());
+
+ PendingChannel *createAndHandleFileTransfer(
+ const QString &contactIdentifier,
+ const FileTransferChannelCreationProperties &properties,
+ const QDateTime &userActionTime = QDateTime::currentDateTime());
+ PendingChannel *createAndHandleFileTransfer(
+ const ContactPtr &contact,
+ const FileTransferChannelCreationProperties &properties,
+ const QDateTime &userActionTime = QDateTime::currentDateTime());
+
+ PendingChannel *createAndHandleStreamTube(
+ const QString &contactIdentifier,
+ const QString &service,
+ const QDateTime &userActionTime = QDateTime::currentDateTime());
+ PendingChannel *createAndHandleStreamTube(
+ const ContactPtr &contact,
+ const QString &service,
+ const QDateTime &userActionTime = QDateTime::currentDateTime());
+
+ PendingChannel *createAndHandleConferenceTextChat(
+ const QList<ChannelPtr> &channels,
+ const QList<ContactPtr> &initialInviteeContacts = QList<ContactPtr>(),
+ const QDateTime &userActionTime = QDateTime::currentDateTime());
+ PendingChannel *createAndHandleConferenceTextChat(
+ const QList<ChannelPtr> &channels,
+ const QStringList &initialInviteeContactsIdentifiers = QStringList(),
+ const QDateTime &userActionTime = QDateTime::currentDateTime());
+
+ PendingChannel *createAndHandleConferenceTextChatroom(
+ const QString &roomName,
+ const QList<ChannelPtr> &channels,
+ const QStringList &initialInviteeContactsIdentifiers = QStringList(),
+ const QDateTime &userActionTime = QDateTime::currentDateTime());
+ PendingChannel *createAndHandleConferenceTextChatroom(
+ const QString &roomName,
+ const QList<ChannelPtr> &channels,
+ const QList<ContactPtr> &initialInviteeContacts = QList<ContactPtr>(),
+ const QDateTime &userActionTime = QDateTime::currentDateTime());
+
+ PendingChannel *createAndHandleConferenceStreamedMediaCall(
+ const QList<ChannelPtr> &channels,
+ const QStringList &initialInviteeContactsIdentifiers = QStringList(),
+ const QDateTime &userActionTime = QDateTime::currentDateTime());
+ PendingChannel *createAndHandleConferenceStreamedMediaCall(
+ const QList<ChannelPtr> &channels,
+ const QList<ContactPtr> &initialInviteeContacts = QList<ContactPtr>(),
+ const QDateTime &userActionTime = QDateTime::currentDateTime());
+
+ PendingChannel *createAndHandleContactSearch(
+ const QString &server = QString(),
+ uint limit = 0,
+ const QDateTime &userActionTime = QDateTime::currentDateTime());
+
+ // advanced
+ PendingChannelRequest *createChannel(
+ const QVariantMap &requestedProperties,
+ const QDateTime &userActionTime = QDateTime::currentDateTime(),
+ const QString &preferredHandler = QString());
+ PendingChannelRequest *createChannel(
+ const QVariantMap &requestedProperties,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints);
+
+ PendingChannelRequest *ensureChannel(
+ const QVariantMap &requestedProperties,
+ const QDateTime &userActionTime = QDateTime::currentDateTime(),
+ const QString &preferredHandler = QString());
+ PendingChannelRequest *ensureChannel(
+ const QVariantMap &requestedProperties,
+ const QDateTime &userActionTime,
+ const QString &preferredHandler,
+ const ChannelRequestHints &hints);
+
+ PendingChannel *createAndHandleChannel(
+ const QVariantMap &requestedProperties,
+ const QDateTime &userActionTime);
+ PendingChannel *ensureAndHandleChannel(
+ const QVariantMap &requestedProperties,
+ const QDateTime &userActionTime);
+
+Q_SIGNALS:
+ void removed();
+ void serviceNameChanged(const QString &serviceName);
+ void profileChanged(const Tp::ProfilePtr &profile);
+ void displayNameChanged(const QString &displayName);
+ void iconNameChanged(const QString &iconName);
+ void nicknameChanged(const QString &nickname);
+ void normalizedNameChanged(const QString &normalizedName);
+ void validityChanged(bool validity);
+ void stateChanged(bool state);
+ void capabilitiesChanged(const Tp::ConnectionCapabilities &capabilities);
+ void connectsAutomaticallyPropertyChanged(bool connectsAutomatically);
+ void firstOnline();
+ void parametersChanged(const QVariantMap &parameters);
+ void changingPresence(bool value);
+ void automaticPresenceChanged(const Tp::Presence &automaticPresence);
+ void currentPresenceChanged(const Tp::Presence &currentPresence);
+ void requestedPresenceChanged(const Tp::Presence &requestedPresence);
+ void onlinenessChanged(bool online);
+ void avatarChanged(const Tp::Avatar &avatar);
+ void connectionStatusChanged(Tp::ConnectionStatus status);
+ void connectionChanged(const Tp::ConnectionPtr &connection);
+
+protected:
+ friend class PendingChannelRequest; // to access dispatcherInterface()
+
+ Account(const QDBusConnection &bus,
+ const QString &busName, const QString &objectPath,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory,
+ const Feature &coreFeature);
+
+ Client::AccountInterface *baseInterface() const;
+ Client::ChannelDispatcherInterface *dispatcherInterface() const;
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onDispatcherIntrospected(Tp::PendingOperation *op);
+ TP_QT_NO_EXPORT void gotMainProperties(QDBusPendingCallWatcher *);
+ TP_QT_NO_EXPORT void gotAvatar(QDBusPendingCallWatcher *);
+ TP_QT_NO_EXPORT void onAvatarChanged();
+ TP_QT_NO_EXPORT void onConnectionManagerReady(Tp::PendingOperation *);
+ TP_QT_NO_EXPORT void onConnectionReady(Tp::PendingOperation *);
+ TP_QT_NO_EXPORT void onPropertyChanged(const QVariantMap &delta);
+ TP_QT_NO_EXPORT void onRemoved();
+ TP_QT_NO_EXPORT void onConnectionBuilt(Tp::PendingOperation *);
+
+private:
+ struct Private;
+ friend struct Private;
+
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/account.xml b/TelepathyQt/account.xml
new file mode 100644
index 00000000..1cc5b2a1
--- /dev/null
+++ b/TelepathyQt/account.xml
@@ -0,0 +1,13 @@
+<tp:spec
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<tp:title>Account interfaces</tp:title>
+
+<xi:include href="../spec/Account.xml"/>
+
+<xi:include href="../spec/Account_Interface_Addressing.xml"/>
+<xi:include href="../spec/Account_Interface_Avatar.xml"/>
+<xi:include href="../spec/Account_Interface_Storage.xml"/>
+
+</tp:spec>
diff --git a/TelepathyQt/and-filter.dox b/TelepathyQt/and-filter.dox
new file mode 100644
index 00000000..85e8c304
--- /dev/null
+++ b/TelepathyQt/and-filter.dox
@@ -0,0 +1,33 @@
+/*
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * \class Tp::AndFilter
+ * \ingroup utils
+ * \headerfile TelepathyQt/and-filter.h <TelepathyQt/AndFilter>
+ *
+ * \brief The AndFilter class provides a generic filter object to be used
+ * in conjunction of other filters.
+ *
+ * The AndFilter will match if all of its given list of filters matches
+ * their criteria.
+ */
diff --git a/TelepathyQt/and-filter.h b/TelepathyQt/and-filter.h
new file mode 100644
index 00000000..4e27ea18
--- /dev/null
+++ b/TelepathyQt/and-filter.h
@@ -0,0 +1,83 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_and_filter_h_HEADER_GUARD_
+#define _TelepathyQt_and_filter_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Filter>
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+template <class T>
+class AndFilter : public Filter<T>
+{
+public:
+ static SharedPtr<AndFilter<T> > create(
+ const QList<SharedPtr<const Filter<T> > > &filters = QList<SharedPtr<const Filter<T> > >())
+ {
+ return SharedPtr<AndFilter<T> >(new AndFilter<T>(filters));
+ }
+
+ inline virtual ~AndFilter() { }
+
+ inline virtual bool isValid() const
+ {
+ Q_FOREACH (const SharedPtr<const Filter<T> > &filter, mFilters) {
+ if (!filter || !filter->isValid()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ inline virtual bool matches(const SharedPtr<T> &t) const
+ {
+ if (!isValid()) {
+ return false;
+ }
+
+ Q_FOREACH (const SharedPtr<const Filter<T> > &filter, mFilters) {
+ if (!filter->matches(t)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ inline QList<SharedPtr<const Filter<T> > > filters() const { return mFilters; }
+
+private:
+ AndFilter(const QList<SharedPtr<const Filter<T> > > &filters)
+ : Filter<T>(), mFilters(filters) { }
+
+ QList<SharedPtr<const Filter<T> > > mFilters;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/async-model.dox b/TelepathyQt/async-model.dox
new file mode 100644
index 00000000..e2dac6da
--- /dev/null
+++ b/TelepathyQt/async-model.dox
@@ -0,0 +1,56 @@
+/*
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * \page async_model Asynchronous Object Model
+ *
+ * \section async_model_overview Overview
+ *
+ * Telepathy-Qt4 uses \dbus to communicate with applications implementing the \telepathy_spec.
+ *
+ * When dealing with D-Bus, method calls can take some time to return,
+ * and in this case is not desirable to make synchronous calls,
+ * which could turn into applications hanging waiting for method returns.
+ * In order to avoid this issue, all Telepathy-Qt4 high-level methods requiring
+ * D-Bus method calls will return a \link Tp::PendingOperation \endlink which will
+ * emit the signal \link Tp::PendingOperation::finished() \endlink when the operation
+ * has ended. See individual methods' documentation for more details.
+ *
+ * Additionally Telepathy-Qt4 introduces a concept in which object features need
+ * to be enabled before usage. Information corresponding to enabled features can be inspected
+ * synchronously with no need for asynchronous D-Bus method calls and the associated
+ * programming complexity.
+ *
+ * To avoid the complexity of doing asynchronous calls when making object features ready
+ * Telepathy-Qt4 also provides so called factories for the main objects.
+ * These object features can be enabled by constructing a corresponding factory and enabling the
+ * desired features, and passing these factories to the objects responsible for creating
+ * the objects whose features are required.
+ * Doing that, applications are guaranteed that the specified features are ready in objects
+ * signaled to them by the library.
+ *
+ * However, if a particular feature is only ever used in a specific circumstance, such as an user
+ * opening some settings dialog separate from the general view of the application,
+ * features can be later enabled as needed by calling becomeReady(), or in the
+ * \link Tp::Contact \endlink case by calling \link Tp::ContactManager::upgradeContacts() \endlink,
+ * with the additional features on the object, and waiting for the resulting PendingOperation to
+ * finish.
+ */
diff --git a/TelepathyQt/avatar.cpp b/TelepathyQt/avatar.cpp
new file mode 100644
index 00000000..f7fd17ec
--- /dev/null
+++ b/TelepathyQt/avatar.cpp
@@ -0,0 +1,172 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/AvatarSpec>
+
+namespace Tp
+{
+
+/**
+ * \class AvatarData
+ * \ingroup wrappers
+ * \headerfile TelepathyQt/avatar.h <TelepathyQt/AvatarData>
+ *
+ * \brief The AvatarData class represents a Telepathy avatar.
+ */
+
+struct TP_QT_NO_EXPORT AvatarSpec::Private : public QSharedData
+{
+ Private(const QStringList &supportedMimeTypes,
+ uint minHeight, uint maxHeight, uint recommendedHeight,
+ uint minWidth, uint maxWidth, uint recommendedWidth,
+ uint maxBytes)
+ : supportedMimeTypes(supportedMimeTypes),
+ minHeight(minHeight),
+ maxHeight(maxHeight),
+ recommendedHeight(recommendedHeight),
+ minWidth(minWidth),
+ maxWidth(maxWidth),
+ recommendedWidth(recommendedWidth),
+ maxBytes(maxBytes)
+ {
+ }
+
+ QStringList supportedMimeTypes;
+ uint minHeight;
+ uint maxHeight;
+ uint recommendedHeight;
+ uint minWidth;
+ uint maxWidth;
+ uint recommendedWidth;
+ uint maxBytes;
+};
+
+/**
+ * \class AvatarSpec
+ * \ingroup wrappers
+ * \headerfile TelepathyQt/avatar.h <TelepathyQt/AvatarSpec>
+ *
+ * \brief The AvatarSpec class represents a Telepathy avatar information
+ * supported by a protocol.
+ */
+
+AvatarSpec::AvatarSpec()
+{
+}
+
+AvatarSpec::AvatarSpec(const QStringList &supportedMimeTypes,
+ uint minHeight, uint maxHeight, uint recommendedHeight,
+ uint minWidth, uint maxWidth, uint recommendedWidth,
+ uint maxBytes)
+ : mPriv(new Private(supportedMimeTypes, minHeight, maxHeight, recommendedHeight,
+ minWidth, maxWidth, recommendedWidth, maxBytes))
+{
+}
+
+AvatarSpec::AvatarSpec(const AvatarSpec &other)
+ : mPriv(other.mPriv)
+{
+}
+
+AvatarSpec::~AvatarSpec()
+{
+}
+
+AvatarSpec &AvatarSpec::operator=(const AvatarSpec &other)
+{
+ this->mPriv = other.mPriv;
+ return *this;
+}
+
+QStringList AvatarSpec::supportedMimeTypes() const
+{
+ if (!isValid()) {
+ return QStringList();
+ }
+
+ return mPriv->supportedMimeTypes;
+}
+
+uint AvatarSpec::minimumHeight() const
+{
+ if (!isValid()) {
+ return 0;
+ }
+
+ return mPriv->minHeight;
+}
+
+uint AvatarSpec::maximumHeight() const
+{
+ if (!isValid()) {
+ return 0;
+ }
+
+ return mPriv->maxHeight;
+}
+
+uint AvatarSpec::recommendedHeight() const
+{
+ if (!isValid()) {
+ return 0;
+ }
+
+ return mPriv->recommendedHeight;
+}
+
+uint AvatarSpec::minimumWidth() const
+{
+ if (!isValid()) {
+ return 0;
+ }
+
+ return mPriv->minWidth;
+}
+
+uint AvatarSpec::maximumWidth() const
+{
+ if (!isValid()) {
+ return 0;
+ }
+
+ return mPriv->maxWidth;
+}
+
+uint AvatarSpec::recommendedWidth() const
+{
+ if (!isValid()) {
+ return 0;
+ }
+
+ return mPriv->recommendedWidth;
+}
+
+uint AvatarSpec::maximumBytes() const
+{
+ if (!isValid()) {
+ return 0;
+ }
+
+ return mPriv->maxBytes;
+}
+
+} // Tp
diff --git a/TelepathyQt/avatar.h b/TelepathyQt/avatar.h
new file mode 100644
index 00000000..c48d3fd1
--- /dev/null
+++ b/TelepathyQt/avatar.h
@@ -0,0 +1,86 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010-2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010-2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_avatar_h_HEADER_GUARD_
+#define _TelepathyQt_avatar_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Global>
+
+#include <QSharedDataPointer>
+#include <QString>
+#include <QStringList>
+#include <QMetaType>
+
+namespace Tp
+{
+
+struct TP_QT_EXPORT AvatarData
+{
+public:
+ inline AvatarData(const QString &fileName, const QString &mimeType)
+ : fileName(fileName), mimeType(mimeType) {}
+ inline AvatarData() {}
+
+ QString fileName;
+ QString mimeType;
+};
+
+class TP_QT_EXPORT AvatarSpec
+{
+public:
+ AvatarSpec();
+ AvatarSpec(const QStringList &supportedMimeTypes,
+ uint minHeight, uint maxHeight, uint recommendedHeight,
+ uint minWidth, uint maxWidth, uint recommendedWidth,
+ uint maxBytes);
+ AvatarSpec(const AvatarSpec &other);
+ ~AvatarSpec();
+
+ bool isValid() const { return mPriv.constData() != 0; }
+
+ AvatarSpec &operator=(const AvatarSpec &other);
+
+ QStringList supportedMimeTypes() const;
+ uint minimumHeight() const;
+ uint maximumHeight() const;
+ uint recommendedHeight() const;
+ uint minimumWidth() const;
+ uint maximumWidth() const;
+ uint recommendedWidth() const;
+ uint maximumBytes() const;
+
+private:
+ struct Private;
+ friend struct Private;
+ QSharedDataPointer<Private> mPriv;
+};
+
+} // Tp
+
+Q_DECLARE_METATYPE(Tp::AvatarData);
+Q_DECLARE_METATYPE(Tp::AvatarSpec);
+
+#endif
diff --git a/TelepathyQt/capabilities-base.cpp b/TelepathyQt/capabilities-base.cpp
new file mode 100644
index 00000000..c55f8850
--- /dev/null
+++ b/TelepathyQt/capabilities-base.cpp
@@ -0,0 +1,348 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/CapabilitiesBase>
+
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT CapabilitiesBase::Private : public QSharedData
+{
+ Private(bool specificToContact);
+ Private(const RequestableChannelClassSpecList &rccSpecs, bool specificToContact);
+
+ RequestableChannelClassSpecList rccSpecs;
+ bool specificToContact;
+};
+
+CapabilitiesBase::Private::Private(bool specificToContact)
+ : specificToContact(specificToContact)
+{
+}
+
+CapabilitiesBase::Private::Private(const RequestableChannelClassSpecList &rccSpecs,
+ bool specificToContact)
+ : rccSpecs(rccSpecs),
+ specificToContact(specificToContact)
+{
+}
+
+/**
+ * \class CapabilitiesBase
+ * \ingroup clientconn
+ * \headerfile TelepathyQt/capabilities-base.h <TelepathyQt/CapabilitiesBase>
+ *
+ * \brief The CapabilitiesBase class represents the capabilities a Connection
+ * or a Contact supports.
+ */
+
+/**
+ * Construct a new CapabilitiesBase object.
+ */
+CapabilitiesBase::CapabilitiesBase()
+ : mPriv(new Private(false))
+{
+}
+
+/**
+ * Construct a new CapabilitiesBase object.
+ *
+ * \param specificToContact Whether this object describes the capabilities of a
+ * particular contact.
+ */
+CapabilitiesBase::CapabilitiesBase(bool specificToContact)
+ : mPriv(new Private(specificToContact))
+{
+}
+
+/**
+ * Construct a new CapabilitiesBase object using the given \a rccs.
+ *
+ * \param rccs RequestableChannelClassList representing the capabilities of a
+ * connection or contact.
+ * \param specificToContact Whether this object describes the capabilities of a
+ * particular contact.
+ */
+CapabilitiesBase::CapabilitiesBase(const RequestableChannelClassList &rccs,
+ bool specificToContact)
+ : mPriv(new Private(RequestableChannelClassSpecList(rccs), specificToContact))
+{
+}
+
+/**
+ * Construct a new CapabilitiesBase object using the given \a rccSpecs.
+ *
+ * \param rccSpecs RequestableChannelClassSpecList representing the capabilities of a
+ * connection or contact.
+ * \param specificToContact Whether this object describes the capabilities of a
+ * particular contact.
+ */
+CapabilitiesBase::CapabilitiesBase(const RequestableChannelClassSpecList &rccSpecs,
+ bool specificToContact)
+ : mPriv(new Private(rccSpecs, specificToContact))
+{
+}
+
+CapabilitiesBase::CapabilitiesBase(const CapabilitiesBase &other)
+ : mPriv(other.mPriv)
+{
+}
+
+/**
+ * Class destructor.
+ */
+CapabilitiesBase::~CapabilitiesBase()
+{
+}
+
+CapabilitiesBase &CapabilitiesBase::operator=(const CapabilitiesBase &other)
+{
+ this->mPriv = other.mPriv;
+ return *this;
+}
+
+/**
+ * Return the list of requestable channel class spec representing the requests that can succeed.
+ *
+ * This can be used by advanced clients to determine whether an unusually
+ * complex request would succeed. See the \telepathy_spec
+ * for details of how to interpret the returned list.
+ *
+ * The higher-level methods like textChats() are likely to be more
+ * useful to the majority of clients.
+ *
+ * \return A RequestableChannelClassSpecList indicating the parameters to
+ * Account::createChannel, Account::ensureChannel,
+ * Connection::createChannel and Connection::ensureChannel
+ * that can be expected to work.
+ */
+RequestableChannelClassSpecList CapabilitiesBase::allClassSpecs() const
+{
+ return mPriv->rccSpecs;
+}
+
+void CapabilitiesBase::updateRequestableChannelClasses(
+ const RequestableChannelClassList &rccs)
+{
+ mPriv->rccSpecs = RequestableChannelClassSpecList(rccs);
+}
+
+/**
+ * Return whether this object accurately describes the capabilities of a
+ * particular contact, or if it's only a guess based on the
+ * capabilities of the underlying connection.
+ *
+ * In protocols like XMPP where each contact advertises their capabilities
+ * to others, Contact::capabilities() will generally return an object where
+ * this method returns true.
+ *
+ * In protocols like SIP where contacts' capabilities are not known,
+ * Contact::capabilities() will return an object where this method returns
+ * false, whose methods textChats() etc. are based on what the
+ * underlying connection supports.
+ *
+ * This reflects the fact that the best assumption an application can make is
+ * that every contact supports every channel type supported by the connection,
+ * while indicating that requests to communicate might fail if the contact
+ * does not actually have the necessary functionality.
+ *
+ * \return \c true if this object describes the capabilities of a particular
+ * contact, \c false otherwise.
+ */
+bool CapabilitiesBase::isSpecificToContact() const
+{
+ return mPriv->specificToContact;
+}
+
+/**
+ * Return whether private text channels can be established by providing
+ * a contact identifier.
+ *
+ * If the protocol is such that text chats can be established, but only via
+ * a more elaborate D-Bus API than normal (because more information is needed),
+ * then this method will return false.
+ *
+ * \return \c true if Account::ensureTextChat() can be expected to work,
+ * \c false otherwise.
+ */
+bool CapabilitiesBase::textChats() const
+{
+ foreach (const RequestableChannelClassSpec &rccSpec, mPriv->rccSpecs) {
+ if (rccSpec.supports(RequestableChannelClassSpec::textChat())) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Return whether private audio and/or video calls can be established by
+ * providing a contact identifier.
+ *
+ * If the protocol is such that these calls can be established, but only via
+ * a more elaborate D-Bus API than normal (because more information is needed),
+ * then this method will return false.
+ *
+ * \return \c true if Account::ensureStreamedMediaCall() can be expected to work,
+ * \c false otherwise.
+ * \sa streamedMediaAudioCalls(), streamedMediaVideoCalls(),
+ * streamedMediaVideoCallsWithAudio()
+ */
+bool CapabilitiesBase::streamedMediaCalls() const
+{
+ foreach (const RequestableChannelClassSpec &rccSpec, mPriv->rccSpecs) {
+ if (rccSpec.supports(RequestableChannelClassSpec::streamedMediaCall())) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Return whether private audio calls can be established by providing a
+ * contact identifier.
+ *
+ * Call upgradingCalls() to determine whether such calls are
+ * likely to be upgradable to have a video stream later.
+ *
+ * If the protocol is such that these calls can be established, but only via
+ * a more elaborate D-Bus API than normal (because more information is needed),
+ * then this method will return false.
+ *
+ * In some older connection managers, streamedMediaAudioCalls() and
+ * streamedMediaVideoCalls() might both return false, even though streamedMediaCalls() returns
+ * true. This indicates that only an older API is supported - clients of these connection managers
+ * must call Account::ensureStreamedMediaCall() to get an empty call, then add audio and/or
+ * video streams to it.
+ *
+ * \return \c true if Account::ensureStreamedMediaAudioCall() can be expected to work,
+ * \c false otherwise.
+ * \sa streamedMediaCalls(), streamedMediaVideoCalls(), streamedMediaVideoCallsWithAudio()
+ */
+bool CapabilitiesBase::streamedMediaAudioCalls() const
+{
+ foreach (const RequestableChannelClassSpec &rccSpec, mPriv->rccSpecs) {
+ if (rccSpec.supports(RequestableChannelClassSpec::streamedMediaAudioCall())) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Return whether private video calls can be established by providing a
+ * contact identifier.
+ *
+ * The same comments as for streamedMediaAudioCalls() apply to this method.
+ *
+ * \return \c true if Account::ensureStreamedMediaVideoCall() can be expected to work,
+ * if given \c false as \a withAudio parameter, \c false otherwise.
+ * \sa streamedMediaCalls(), streamedMediaAudioCalls(), streamedMediaVideoCallsWithAudio()
+ */
+bool CapabilitiesBase::streamedMediaVideoCalls() const
+{
+ foreach (const RequestableChannelClassSpec &rccSpec, mPriv->rccSpecs) {
+ if (rccSpec.supports(RequestableChannelClassSpec::streamedMediaVideoCall())) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Return whether private video calls with audio can be established by providing a
+ * contact identifier.
+ *
+ * The same comments as for streamedMediaAudioCalls() apply to this method.
+ *
+ * \return \c true if Account::ensureStreamedMediaVideoCall() can be expected to work,
+ * if given \c true as \a withAudio parameter, \c false otherwise.
+ * \sa streamedMediaCalls(), streamedMediaAudioCalls(), streamedMediaVideoCalls()
+ */
+bool CapabilitiesBase::streamedMediaVideoCallsWithAudio() const
+{
+ foreach (const RequestableChannelClassSpec &rccSpec, mPriv->rccSpecs) {
+ if (rccSpec.supports(RequestableChannelClassSpec::streamedMediaVideoCallWithAudio())) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Return whether the protocol supports adding streams of a different type
+ * to ongoing media calls.
+ *
+ * In some protocols and clients (such as XMPP Jingle), all calls potentially
+ * support both audio and video. This is indicated by returning true.
+ *
+ * In other protocols and clients (such as MSN, and the variant of XMPP Jingle
+ * used by Google clients), the streams are fixed at the time the call is
+ * started, so if you will ever want video, you have to ask for it at the
+ * beginning, for instance with ensureStreamedMediaVideoCall(). This is indicated by
+ * returning false.
+ *
+ * User interfaces can use this method as a UI hint. If it returns false,
+ * then a UI wishing to support both audio and video calls will have to
+ * provide separate "audio call" and "video call" buttons or menu items;
+ * if it returns true, a single button that makes an audio call is sufficient,
+ * because video can be added later.
+ *
+ * (The underlying Telepathy feature is the ImmutableStreams property; if this
+ * method returns true, then ImmutableStreams is false, and vice versa).
+ *
+ * \return \c true if audio calls can be upgraded to audio + video,
+ * \c false otherwise.
+ */
+bool CapabilitiesBase::upgradingStreamedMediaCalls() const
+{
+ foreach (const RequestableChannelClassSpec &rccSpec, mPriv->rccSpecs) {
+ if (rccSpec.channelType() == TP_QT_IFACE_CHANNEL_TYPE_STREAMED_MEDIA &&
+ !rccSpec.allowsProperty(TP_QT_IFACE_CHANNEL_TYPE_STREAMED_MEDIA + QLatin1String(".ImmutableStreams"))) {
+ // TODO should we test all classes that have channelType
+ // StreamedMedia or just one is fine?
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Return whether file transfer can be established by providing a contact identifier
+ *
+ * \return \c true if file transfers can be expected to work,
+ * \c false otherwise.
+ */
+bool CapabilitiesBase::fileTransfers() const
+{
+ foreach (const RequestableChannelClassSpec &rccSpec, mPriv->rccSpecs) {
+ if (rccSpec.supports(RequestableChannelClassSpec::fileTransfer())) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // Tp
diff --git a/TelepathyQt/capabilities-base.h b/TelepathyQt/capabilities-base.h
new file mode 100644
index 00000000..a2af63fb
--- /dev/null
+++ b/TelepathyQt/capabilities-base.h
@@ -0,0 +1,85 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_capabilities_base_h_HEADER_GUARD_
+#define _TelepathyQt_capabilities_base_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/RequestableChannelClassSpec>
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+class TP_QT_EXPORT CapabilitiesBase
+{
+public:
+ CapabilitiesBase();
+ CapabilitiesBase(const CapabilitiesBase &other);
+ virtual ~CapabilitiesBase();
+
+ CapabilitiesBase &operator=(const CapabilitiesBase &other);
+
+ RequestableChannelClassSpecList allClassSpecs() const;
+
+ bool isSpecificToContact() const;
+
+ bool textChats() const;
+
+ bool streamedMediaCalls() const;
+ bool streamedMediaAudioCalls() const;
+ bool streamedMediaVideoCalls() const;
+ bool streamedMediaVideoCallsWithAudio() const;
+ bool upgradingStreamedMediaCalls() const;
+
+ bool fileTransfers() const;
+
+ // later: FIXME TODO why not now?
+ // QList<FileHashType> fileTransfersRequireHash() const;
+
+protected:
+ CapabilitiesBase(bool specificToContact);
+ CapabilitiesBase(const RequestableChannelClassList &rccs,
+ bool specificToContact);
+ CapabilitiesBase(const RequestableChannelClassSpecList &rccSpecs,
+ bool specificToContact);
+
+ virtual void updateRequestableChannelClasses(
+ const RequestableChannelClassList &rccs);
+
+private:
+ friend class Connection;
+ friend class Contact;
+
+ struct Private;
+ friend struct Private;
+ QSharedDataPointer<Private> mPriv;
+};
+
+} // Tp
+
+Q_DECLARE_METATYPE(Tp::CapabilitiesBase);
+
+#endif
diff --git a/TelepathyQt/channel-class-features.h b/TelepathyQt/channel-class-features.h
new file mode 100644
index 00000000..bd03117e
--- /dev/null
+++ b/TelepathyQt/channel-class-features.h
@@ -0,0 +1,45 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_channel_class_features_h_HEADER_GUARD_
+#define _TelepathyQt_channel_class_features_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/ChannelClassSpec>
+#include <TelepathyQt/Features>
+
+#include <QMetaType>
+#include <QPair>
+
+namespace Tp
+{
+
+typedef QPair<ChannelClassSpec, Features> ChannelClassFeatures;
+
+} // Tp
+
+Q_DECLARE_METATYPE(Tp::ChannelClassFeatures);
+
+#endif
diff --git a/TelepathyQt/channel-class-spec.cpp b/TelepathyQt/channel-class-spec.cpp
new file mode 100644
index 00000000..86083727
--- /dev/null
+++ b/TelepathyQt/channel-class-spec.cpp
@@ -0,0 +1,555 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/ChannelClassSpec>
+
+#include "TelepathyQt/_gen/future-constants.h"
+
+#include "TelepathyQt/debug-internal.h"
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT ChannelClassSpec::Private : public QSharedData
+{
+ QVariantMap props;
+};
+
+/**
+ * \class ChannelClassSpec
+ * \ingroup wrappers
+ * \headerfile TelepathyQt/channel-class-spec.h <TelepathyQt/ChannelClassSpec>
+ *
+ * \brief The ChannelClassSpec class represents a Telepathy channel class.
+ */
+
+ChannelClassSpec::ChannelClassSpec()
+{
+}
+
+ChannelClassSpec::ChannelClassSpec(const ChannelClass &cc)
+ : mPriv(new Private)
+{
+ foreach (QString key, cc.keys()) {
+ setProperty(key, cc.value(key).variant());
+ }
+}
+
+ChannelClassSpec::ChannelClassSpec(const QVariantMap &props)
+ : mPriv(new Private)
+{
+ setChannelType(qdbus_cast<QString>(
+ props.value(TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType"))));
+ setTargetHandleType((HandleType) qdbus_cast<uint>(
+ props.value(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandleType"))));
+
+ foreach (QString propName, props.keys()) {
+ setProperty(propName, props.value(propName));
+ }
+}
+
+ChannelClassSpec::ChannelClassSpec(const QString &channelType, HandleType targetHandleType,
+ const QVariantMap &otherProperties)
+ : mPriv(new Private)
+{
+ setChannelType(channelType);
+ setTargetHandleType(targetHandleType);
+ foreach (QString key, otherProperties.keys()) {
+ setProperty(key, otherProperties.value(key));
+ }
+}
+
+ChannelClassSpec::ChannelClassSpec(const QString &channelType, HandleType targetHandleType,
+ bool requested, const QVariantMap &otherProperties)
+ : mPriv(new Private)
+{
+ setChannelType(channelType);
+ setTargetHandleType(targetHandleType);
+ setRequested(requested);
+ foreach (QString key, otherProperties.keys()) {
+ setProperty(key, otherProperties.value(key));
+ }
+}
+
+ChannelClassSpec::ChannelClassSpec(const ChannelClassSpec &other,
+ const QVariantMap &additionalProperties)
+ : mPriv(other.mPriv)
+{
+ if (!additionalProperties.isEmpty()) {
+ foreach (QString key, additionalProperties.keys()) {
+ setProperty(key, additionalProperties.value(key));
+ }
+ }
+}
+
+ChannelClassSpec::~ChannelClassSpec()
+{
+}
+
+bool ChannelClassSpec::isValid() const
+{
+ return mPriv.constData() != 0 &&
+ !(qdbus_cast<QString>(
+ mPriv->props.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType")))
+ .isEmpty()) &&
+ mPriv->props.contains(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType"));
+}
+
+ChannelClassSpec &ChannelClassSpec::operator=(const ChannelClassSpec &other)
+{
+ if (this == &other) {
+ return *this;
+ }
+
+ this->mPriv = other.mPriv;
+ return *this;
+}
+
+bool ChannelClassSpec::isSubsetOf(const ChannelClassSpec &other) const
+{
+ if (!mPriv) {
+ // Invalid instances have no properties - hence they're subset of anything
+ return true;
+ }
+
+ foreach (QString propName, mPriv->props.keys()) {
+ if (!other.hasProperty(propName)) {
+ return false;
+ } else if (property(propName) != other.property(propName)) {
+ return false;
+ }
+ }
+
+ // other had all of the properties we have and they all had the same values
+
+ return true;
+}
+
+bool ChannelClassSpec::matches(const QVariantMap &immutableProperties) const
+{
+ // We construct a ChannelClassSpec for comparison so the StreamedMedia props are normalized
+ // consistently etc
+ return this->isSubsetOf(ChannelClassSpec(immutableProperties));
+}
+
+bool ChannelClassSpec::hasProperty(const QString &qualifiedName) const
+{
+ return mPriv.constData() != 0 ? mPriv->props.contains(qualifiedName) : false;
+}
+
+QVariant ChannelClassSpec::property(const QString &qualifiedName) const
+{
+ return mPriv.constData() != 0 ? mPriv->props.value(qualifiedName) : QVariant();
+}
+
+void ChannelClassSpec::setProperty(const QString &qualifiedName, const QVariant &value)
+{
+ if (mPriv.constData() == 0) {
+ mPriv = new Private;
+ }
+
+ mPriv->props.insert(qualifiedName, value);
+}
+
+void ChannelClassSpec::unsetProperty(const QString &qualifiedName)
+{
+ if (mPriv.constData() == 0) {
+ // No properties set for sure, so don't have to unset any
+ return;
+ }
+
+ mPriv->props.remove(qualifiedName);
+}
+
+QVariantMap ChannelClassSpec::allProperties() const
+{
+ return mPriv.constData() != 0 ? mPriv->props : QVariantMap();
+}
+
+ChannelClass ChannelClassSpec::bareClass() const
+{
+ ChannelClass cc;
+
+ if (!isValid()) {
+ warning() << "Tried to convert an invalid ChannelClassSpec to a ChannelClass";
+ return ChannelClass();
+ }
+
+ QVariantMap props = mPriv->props;
+ foreach (QString propName, props.keys()) {
+ QVariant value = props.value(propName);
+
+ cc.insert(propName, QDBusVariant(value));
+ }
+
+ return cc;
+}
+
+ChannelClassSpec ChannelClassSpec::textChat(const QVariantMap &additionalProperties)
+{
+ static ChannelClassSpec spec;
+
+ if (!spec.mPriv.constData()) {
+ spec = ChannelClassSpec(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_TEXT),
+ HandleTypeContact);
+ }
+
+ if (additionalProperties.isEmpty()) {
+ return spec;
+ } else {
+ return ChannelClassSpec(spec, additionalProperties);
+ }
+}
+
+ChannelClassSpec ChannelClassSpec::textChatroom(const QVariantMap &additionalProperties)
+{
+ static ChannelClassSpec spec;
+
+ if (!spec.mPriv.constData()) {
+ spec = ChannelClassSpec(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_TEXT),
+ HandleTypeRoom);
+ }
+
+ if (additionalProperties.isEmpty()) {
+ return spec;
+ } else {
+ return ChannelClassSpec(spec, additionalProperties);
+ }
+}
+
+ChannelClassSpec ChannelClassSpec::unnamedTextChat(const QVariantMap &additionalProperties)
+{
+ static ChannelClassSpec spec;
+
+ if (!spec.mPriv.constData()) {
+ spec = ChannelClassSpec(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_TEXT),
+ HandleTypeNone);
+ }
+
+ if (additionalProperties.isEmpty()) {
+ return spec;
+ } else {
+ return ChannelClassSpec(spec, additionalProperties);
+ }
+}
+
+ChannelClassSpec ChannelClassSpec::streamedMediaCall(const QVariantMap &additionalProperties)
+{
+ static ChannelClassSpec spec;
+
+ if (!spec.mPriv.constData()) {
+ spec = ChannelClassSpec(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA),
+ HandleTypeContact);
+ }
+
+ if (additionalProperties.isEmpty()) {
+ return spec;
+ } else {
+ return ChannelClassSpec(spec, additionalProperties);
+ }
+}
+
+ChannelClassSpec ChannelClassSpec::streamedMediaAudioCall(const QVariantMap &additionalProperties)
+{
+ static ChannelClassSpec spec;
+
+ if (!spec.mPriv.constData()) {
+ spec = ChannelClassSpec(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA),
+ HandleTypeContact);
+ spec.setStreamedMediaInitialAudioFlag();
+ }
+
+ if (additionalProperties.isEmpty()) {
+ return spec;
+ } else {
+ return ChannelClassSpec(spec, additionalProperties);
+ }
+}
+
+ChannelClassSpec ChannelClassSpec::streamedMediaVideoCall(const QVariantMap &additionalProperties)
+{
+ static ChannelClassSpec spec;
+
+ if (!spec.mPriv.constData()) {
+ spec = ChannelClassSpec(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA),
+ HandleTypeContact);
+ spec.setStreamedMediaInitialVideoFlag();
+ }
+
+ if (additionalProperties.isEmpty()) {
+ return spec;
+ } else {
+ return ChannelClassSpec(spec, additionalProperties);
+ }
+}
+
+ChannelClassSpec ChannelClassSpec::streamedMediaVideoCallWithAudio(const QVariantMap &additionalProperties)
+{
+ static ChannelClassSpec spec;
+
+ if (!spec.mPriv.constData()) {
+ spec = ChannelClassSpec(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA),
+ HandleTypeContact);
+ spec.setStreamedMediaInitialAudioFlag();
+ spec.setStreamedMediaInitialVideoFlag();
+ }
+
+ if (additionalProperties.isEmpty()) {
+ return spec;
+ } else {
+ return ChannelClassSpec(spec, additionalProperties);
+ }
+}
+
+ChannelClassSpec ChannelClassSpec::unnamedStreamedMediaCall(const QVariantMap &additionalProperties)
+{
+ static ChannelClassSpec spec;
+
+ if (!spec.mPriv.constData()) {
+ spec = ChannelClassSpec(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA),
+ HandleTypeNone);
+ }
+
+ if (additionalProperties.isEmpty()) {
+ return spec;
+ } else {
+ return ChannelClassSpec(spec, additionalProperties);
+ }
+}
+
+ChannelClassSpec ChannelClassSpec::unnamedStreamedMediaAudioCall(const QVariantMap &additionalProperties)
+{
+ static ChannelClassSpec spec;
+
+ if (!spec.mPriv.constData()) {
+ spec = ChannelClassSpec(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA),
+ HandleTypeNone);
+ spec.setStreamedMediaInitialAudioFlag();
+ }
+
+ if (additionalProperties.isEmpty()) {
+ return spec;
+ } else {
+ return ChannelClassSpec(spec, additionalProperties);
+ }
+}
+
+ChannelClassSpec ChannelClassSpec::unnamedStreamedMediaVideoCall(const QVariantMap &additionalProperties)
+{
+ static ChannelClassSpec spec;
+
+ if (!spec.mPriv.constData()) {
+ spec = ChannelClassSpec(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA),
+ HandleTypeNone);
+ spec.setStreamedMediaInitialVideoFlag();
+ }
+
+ if (additionalProperties.isEmpty()) {
+ return spec;
+ } else {
+ return ChannelClassSpec(spec, additionalProperties);
+ }
+}
+
+ChannelClassSpec ChannelClassSpec::unnamedStreamedMediaVideoCallWithAudio(const QVariantMap &additionalProperties)
+{
+ static ChannelClassSpec spec;
+
+ if (!spec.mPriv.constData()) {
+ spec = ChannelClassSpec(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA),
+ HandleTypeNone);
+ spec.setStreamedMediaInitialAudioFlag();
+ spec.setStreamedMediaInitialVideoFlag();
+ }
+
+ if (additionalProperties.isEmpty()) {
+ return spec;
+ } else {
+ return ChannelClassSpec(spec, additionalProperties);
+ }
+}
+
+ChannelClassSpec ChannelClassSpec::roomList(const QVariantMap &additionalProperties)
+{
+ static ChannelClassSpec spec;
+
+ if (!spec.mPriv.constData()) {
+ spec = ChannelClassSpec(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_ROOM_LIST),
+ HandleTypeNone);
+ }
+
+ if (additionalProperties.isEmpty()) {
+ return spec;
+ } else {
+ return ChannelClassSpec(spec, additionalProperties);
+ }
+}
+
+ChannelClassSpec ChannelClassSpec::outgoingFileTransfer(const QVariantMap &additionalProperties)
+{
+ static ChannelClassSpec spec;
+
+ if (!spec.mPriv.constData()) {
+ spec = ChannelClassSpec(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_FILE_TRANSFER),
+ HandleTypeContact, true);
+ }
+
+ if (additionalProperties.isEmpty()) {
+ return spec;
+ } else {
+ return ChannelClassSpec(spec, additionalProperties);
+ }
+}
+
+ChannelClassSpec ChannelClassSpec::incomingFileTransfer(const QVariantMap &additionalProperties)
+{
+ static ChannelClassSpec spec;
+
+ if (!spec.mPriv.constData()) {
+ spec = ChannelClassSpec(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_FILE_TRANSFER),
+ HandleTypeContact, false);
+ }
+
+ if (additionalProperties.isEmpty()) {
+ return spec;
+ } else {
+ return ChannelClassSpec(spec, additionalProperties);
+ }
+}
+
+ChannelClassSpec ChannelClassSpec::outgoingStreamTube(const QString &service,
+ const QVariantMap &additionalProperties)
+{
+ static ChannelClassSpec spec;
+
+ if (!spec.mPriv.constData()) {
+ spec = ChannelClassSpec(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAM_TUBE),
+ HandleTypeContact, true);
+ }
+
+ QVariantMap props = additionalProperties;
+ if (!service.isEmpty()) {
+ props.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAM_TUBE ".Service"),
+ service);
+ }
+
+ if (props.isEmpty()) {
+ return spec;
+ } else {
+ return ChannelClassSpec(spec, props);
+ }
+}
+
+ChannelClassSpec ChannelClassSpec::incomingStreamTube(const QString &service,
+ const QVariantMap &additionalProperties)
+{
+ static ChannelClassSpec spec;
+
+ if (!spec.mPriv.constData()) {
+ spec = ChannelClassSpec(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAM_TUBE),
+ HandleTypeContact, false);
+ }
+
+ QVariantMap props = additionalProperties;
+ if (!service.isEmpty()) {
+ props.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAM_TUBE ".Service"),
+ service);
+ }
+
+ if (props.isEmpty()) {
+ return spec;
+ } else {
+ return ChannelClassSpec(spec, props);
+ }
+}
+
+ChannelClassSpec ChannelClassSpec::outgoingRoomStreamTube(const QString &service,
+ const QVariantMap &additionalProperties)
+{
+ static ChannelClassSpec spec;
+
+ if (!spec.mPriv.constData()) {
+ spec = ChannelClassSpec(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAM_TUBE),
+ HandleTypeRoom, true);
+ }
+
+ QVariantMap props = additionalProperties;
+ if (!service.isEmpty()) {
+ props.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAM_TUBE ".Service"),
+ service);
+ }
+
+ if (props.isEmpty()) {
+ return spec;
+ } else {
+ return ChannelClassSpec(spec, props);
+ }
+}
+
+ChannelClassSpec ChannelClassSpec::incomingRoomStreamTube(const QString &service,
+ const QVariantMap &additionalProperties)
+{
+ static ChannelClassSpec spec;
+
+ if (!spec.mPriv.constData()) {
+ spec = ChannelClassSpec(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAM_TUBE),
+ HandleTypeRoom, false);
+ }
+
+ QVariantMap props = additionalProperties;
+ if (!service.isEmpty()) {
+ props.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAM_TUBE ".Service"),
+ service);
+ }
+
+ if (props.isEmpty()) {
+ return spec;
+ } else {
+ return ChannelClassSpec(spec, props);
+ }
+}
+
+ChannelClassSpec ChannelClassSpec::contactSearch(const QVariantMap &additionalProperties)
+{
+ static ChannelClassSpec spec;
+
+ if (!spec.mPriv.constData()) {
+ spec = ChannelClassSpec(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_CONTACT_SEARCH),
+ HandleTypeNone);
+ }
+
+ if (additionalProperties.isEmpty()) {
+ return spec;
+ } else {
+ return ChannelClassSpec(spec, additionalProperties);
+ }
+}
+
+/**
+ * \class ChannelClassSpecList
+ * \ingroup wrappers
+ * \headerfile TelepathyQt/channel-class-spec.h <TelepathyQt/ChannelClassSpecList>
+ *
+ * \brief The ChannelClassSpecList class represents a list of ChannelClassSpec.
+ */
+
+} // Tp
diff --git a/TelepathyQt/channel-class-spec.h b/TelepathyQt/channel-class-spec.h
new file mode 100644
index 00000000..5b8bf357
--- /dev/null
+++ b/TelepathyQt/channel-class-spec.h
@@ -0,0 +1,277 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_channel_class_spec_h_HEADER_GUARD_
+#define _TelepathyQt_channel_class_spec_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/Global>
+#include <TelepathyQt/Types>
+
+#include <QSharedDataPointer>
+#include <QVariant>
+#include <QVariantMap>
+#include <QPair>
+
+namespace Tp
+{
+
+class TP_QT_EXPORT ChannelClassSpec
+{
+public:
+ ChannelClassSpec();
+ ChannelClassSpec(const ChannelClass &cc);
+ ChannelClassSpec(const QVariantMap &props);
+ ChannelClassSpec(const QString &channelType, HandleType targetHandleType,
+ const QVariantMap &otherProperties = QVariantMap());
+ ChannelClassSpec(const QString &channelType, HandleType targetHandleType, bool requested,
+ const QVariantMap &otherProperties = QVariantMap());
+ ChannelClassSpec(const ChannelClassSpec &other,
+ const QVariantMap &additionalProperties = QVariantMap());
+ ~ChannelClassSpec();
+
+ bool isValid() const;
+
+ ChannelClassSpec &operator=(const ChannelClassSpec &other);
+
+ bool operator==(const ChannelClassSpec &other) const
+ {
+ return this->allProperties() == other.allProperties();
+ }
+
+ bool isSubsetOf(const ChannelClassSpec &other) const;
+ bool matches(const QVariantMap &immutableProperties) const;
+
+ // TODO: Use new TP_QT_... constants
+ QString channelType() const
+ {
+ return qdbus_cast<QString>(
+ property(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType")));
+ }
+
+ void setChannelType(const QString &type)
+ {
+ setProperty(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType"),
+ QVariant::fromValue(type));
+ }
+
+ HandleType targetHandleType() const
+ {
+ return (HandleType) qdbus_cast<uint>(
+ property(
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType")));
+ }
+
+ void setTargetHandleType(HandleType type)
+ {
+ setProperty(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType"),
+ QVariant::fromValue((uint) type));
+ }
+
+ bool hasRequested() const
+ {
+ return hasProperty(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".Requested"));
+ }
+
+ bool isRequested() const
+ {
+ return qdbus_cast<bool>(
+ property(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".Requested")));
+ }
+
+ void setRequested(bool requested)
+ {
+ setProperty(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".Requested"),
+ QVariant::fromValue(requested));
+ }
+
+ void unsetRequested()
+ {
+ unsetProperty(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".Requested"));
+ }
+
+ bool hasStreamedMediaInitialAudioFlag() const
+ {
+ return qdbus_cast<bool>(
+ property(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA
+ ".InitialAudio")));
+ }
+
+ void setStreamedMediaInitialAudioFlag()
+ {
+ setProperty(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialAudio"),
+ QVariant::fromValue(true));
+ }
+
+ void unsetStreamedMediaInitialAudioFlag()
+ {
+ unsetProperty(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA
+ ".InitialAudio"));
+ }
+
+ bool hasStreamedMediaInitialVideoFlag() const
+ {
+ return qdbus_cast<bool>(
+ property(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA
+ ".InitialVideo")));
+ }
+
+ void setStreamedMediaInitialVideoFlag()
+ {
+ setProperty(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialVideo"),
+ QVariant::fromValue(true));
+ }
+
+ void unsetStreamedMediaInitialVideoFlag()
+ {
+ unsetProperty(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA
+ ".InitialVideo"));
+ }
+
+ bool hasProperty(const QString &qualifiedName) const;
+ QVariant property(const QString &qualifiedName) const;
+
+ void setProperty(const QString &qualifiedName, const QVariant &value);
+ void unsetProperty(const QString &qualifiedName);
+
+ QVariantMap allProperties() const;
+ ChannelClass bareClass() const;
+
+ static ChannelClassSpec textChat(const QVariantMap &additionalProperties = QVariantMap());
+ static ChannelClassSpec textChatroom(const QVariantMap &additionalProperties = QVariantMap());
+ static ChannelClassSpec unnamedTextChat(const QVariantMap &additionalProperties = QVariantMap());
+
+ static ChannelClassSpec streamedMediaCall(const QVariantMap &additionalProperties = QVariantMap());
+ static ChannelClassSpec streamedMediaAudioCall(const QVariantMap &additionalProperties =
+ QVariantMap());
+ static ChannelClassSpec streamedMediaVideoCall(const QVariantMap &additionalProperties =
+ QVariantMap());
+ static ChannelClassSpec streamedMediaVideoCallWithAudio(const QVariantMap &additionalProperties =
+ QVariantMap());
+
+ static ChannelClassSpec unnamedStreamedMediaCall(const QVariantMap &additionalProperties =
+ QVariantMap());
+ static ChannelClassSpec unnamedStreamedMediaAudioCall(const QVariantMap &additionalProperties =
+ QVariantMap());
+ static ChannelClassSpec unnamedStreamedMediaVideoCall(const QVariantMap &additionalProperties =
+ QVariantMap());
+ static ChannelClassSpec unnamedStreamedMediaVideoCallWithAudio(const QVariantMap &additionalProperties =
+ QVariantMap());
+
+ // TODO: add Call when it's undrafted
+ static ChannelClassSpec roomList(const QVariantMap &additionalProperties = QVariantMap());
+ static ChannelClassSpec outgoingFileTransfer(const QVariantMap &additionalProperties = QVariantMap());
+ static ChannelClassSpec incomingFileTransfer(const QVariantMap &additionalProperties = QVariantMap());
+ static ChannelClassSpec outgoingStreamTube(const QString &service = QString(),
+ const QVariantMap &additionalProperties = QVariantMap());
+ static ChannelClassSpec incomingStreamTube(const QString &service = QString(),
+ const QVariantMap &additionalProperties = QVariantMap());
+ static ChannelClassSpec outgoingRoomStreamTube(const QString &service = QString(),
+ const QVariantMap &additionalProperties = QVariantMap());
+ static ChannelClassSpec incomingRoomStreamTube(const QString &service = QString(),
+ const QVariantMap &additionalProperties = QVariantMap());
+ // TODO: add dbus tubes when they're implemented
+ static ChannelClassSpec contactSearch(const QVariantMap &additionalProperties = QVariantMap());
+
+private:
+ struct Private;
+ friend struct Private;
+ QSharedDataPointer<Private> mPriv;
+};
+
+class TP_QT_EXPORT ChannelClassSpecList :
+ public QList<ChannelClassSpec>
+{
+public:
+ ChannelClassSpecList() { }
+
+ ChannelClassSpecList(const ChannelClassSpec &spec)
+ {
+ append(spec);
+ }
+
+ ChannelClassSpecList(const QList<ChannelClassSpec> &other)
+ : QList<ChannelClassSpec>(other)
+ {
+ }
+
+ ChannelClassSpecList(const ChannelClassList &classes)
+ {
+ // Why doesn't Qt have range constructors like STL... stupid, so stupid.
+ Q_FOREACH (const ChannelClass &cc, classes) {
+ append(cc);
+ }
+ }
+
+ ChannelClassList bareClasses() const
+ {
+ ChannelClassList list;
+ Q_FOREACH (const ChannelClassSpec &spec, *this) {
+ list.append(spec.bareClass());
+ }
+ return list;
+ }
+};
+
+inline uint qHash(const ChannelClassSpec &spec)
+{
+ uint ret = 0;
+ QVariantMap::const_iterator it = spec.allProperties().constBegin();
+ QVariantMap::const_iterator end = spec.allProperties().constEnd();
+ int i = spec.allProperties().size() + 1;
+ for (; it != end; ++it) {
+ // all D-Bus types should be convertible to QString
+ QPair<QString, QString> p(it.key(), it.value().toString());
+ int h = qHash(p);
+ ret ^= ((h << (2 << i)) | (h >> (2 >> i)));
+ i--;
+ }
+ return ret;
+}
+
+inline uint qHash(const QSet<ChannelClassSpec> &specSet)
+{
+ int ret = 0;
+ Q_FOREACH (const ChannelClassSpec &spec, specSet) {
+ int h = qHash(spec);
+ ret ^= h;
+ }
+ return ret;
+}
+
+inline uint qHash(const ChannelClassSpecList &specList)
+{
+ // Make it unique by converting to QSet
+ QSet<ChannelClassSpec> uniqueSet = specList.toSet();
+ return qHash(uniqueSet);
+}
+
+} // Tp
+
+Q_DECLARE_METATYPE(Tp::ChannelClassSpec);
+Q_DECLARE_METATYPE(Tp::ChannelClassSpecList);
+
+#endif
diff --git a/TelepathyQt/channel-dispatch-operation-internal.h b/TelepathyQt/channel-dispatch-operation-internal.h
new file mode 100644
index 00000000..7eef7253
--- /dev/null
+++ b/TelepathyQt/channel-dispatch-operation-internal.h
@@ -0,0 +1,51 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_channel_dispatch_operation_internal_h_HEADER_GUARD_
+#define _TelepathyQt_channel_dispatch_operation_internal_h_HEADER_GUARD_
+
+#include <TelepathyQt/AbstractClientHandler>
+#include <TelepathyQt/PendingOperation>
+
+namespace Tp
+{
+
+class TP_QT_NO_EXPORT ChannelDispatchOperation::PendingClaim : public PendingOperation
+{
+ Q_OBJECT
+
+public:
+ PendingClaim(const ChannelDispatchOperationPtr &op,
+ const AbstractClientHandlerPtr &handler = AbstractClientHandlerPtr());
+ ~PendingClaim();
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onClaimFinished(Tp::PendingOperation *op);
+
+private:
+ ChannelDispatchOperationPtr mDispatchOp;
+ AbstractClientHandlerPtr mHandler;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/channel-dispatch-operation.cpp b/TelepathyQt/channel-dispatch-operation.cpp
new file mode 100644
index 00000000..a24dbcb3
--- /dev/null
+++ b/TelepathyQt/channel-dispatch-operation.cpp
@@ -0,0 +1,623 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/ChannelDispatchOperation>
+#include "TelepathyQt/channel-dispatch-operation-internal.h"
+
+#include "TelepathyQt/_gen/cli-channel-dispatch-operation-body.hpp"
+#include "TelepathyQt/_gen/cli-channel-dispatch-operation.moc.hpp"
+#include "TelepathyQt/_gen/channel-dispatch-operation.moc.hpp"
+#include "TelepathyQt/_gen/channel-dispatch-operation-internal.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+#include "TelepathyQt/fake-handler-manager-internal.h"
+
+#include <TelepathyQt/Account>
+#include <TelepathyQt/AccountFactory>
+#include <TelepathyQt/Channel>
+#include <TelepathyQt/ChannelFactory>
+#include <TelepathyQt/Connection>
+#include <TelepathyQt/ConnectionFactory>
+#include <TelepathyQt/ContactFactory>
+#include <TelepathyQt/PendingComposite>
+#include <TelepathyQt/PendingReady>
+#include <TelepathyQt/PendingVoid>
+
+#include <QPointer>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT ChannelDispatchOperation::Private
+{
+ Private(ChannelDispatchOperation *parent);
+ ~Private();
+
+ static void introspectMain(Private *self);
+
+ void extractMainProps(const QVariantMap &props,
+ bool immutableProperties);
+
+ // Public object
+ ChannelDispatchOperation *parent;
+
+ // Context
+ AccountFactoryConstPtr accFactory;
+ ConnectionFactoryConstPtr connFactory;
+ ChannelFactoryConstPtr chanFactory;
+ ContactFactoryConstPtr contactFactory;
+
+ // Instance of generated interface class
+ Client::ChannelDispatchOperationInterface *baseInterface;
+
+ // Mandatory properties interface proxy
+ Client::DBus::PropertiesInterface *properties;
+
+ ReadinessHelper *readinessHelper;
+
+ // Introspection
+ QVariantMap immutableProperties;
+ ConnectionPtr connection;
+ AccountPtr account;
+ QList<ChannelPtr> channels;
+ QStringList possibleHandlers;
+ bool gotPossibleHandlers;
+};
+
+ChannelDispatchOperation::Private::Private(ChannelDispatchOperation *parent)
+ : parent(parent),
+ baseInterface(new Client::ChannelDispatchOperationInterface(parent)),
+ properties(parent->interface<Client::DBus::PropertiesInterface>()),
+ readinessHelper(parent->readinessHelper()),
+ gotPossibleHandlers(false)
+{
+ debug() << "Creating new ChannelDispatchOperation:" << parent->objectPath();
+
+ parent->connect(baseInterface,
+ SIGNAL(Finished()),
+ SLOT(onFinished()));
+
+ parent->connect(baseInterface,
+ SIGNAL(ChannelLost(QDBusObjectPath,QString,QString)),
+ SLOT(onChannelLost(QDBusObjectPath,QString,QString)));
+
+ ReadinessHelper::Introspectables introspectables;
+
+ // As ChannelDispatchOperation does not have predefined statuses let's simulate one (0)
+ ReadinessHelper::Introspectable introspectableCore(
+ QSet<uint>() << 0, // makesSenseForStatuses
+ Features(), // dependsOnFeatures
+ QStringList(), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &Private::introspectMain,
+ this);
+ introspectables[FeatureCore] = introspectableCore;
+
+ readinessHelper->addIntrospectables(introspectables);
+}
+
+ChannelDispatchOperation::Private::~Private()
+{
+}
+
+void ChannelDispatchOperation::Private::introspectMain(ChannelDispatchOperation::Private *self)
+{
+ QVariantMap mainProps;
+ foreach (QString key, self->immutableProperties.keys()) {
+ if (key.startsWith(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_DISPATCH_OPERATION "."))) {
+ QVariant value = self->immutableProperties.value(key);
+ mainProps.insert(
+ key.remove(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_DISPATCH_OPERATION ".")),
+ value);
+ }
+ }
+
+ if (!self->channels.isEmpty() && mainProps.contains(QLatin1String("Account"))
+ && mainProps.contains(QLatin1String("Connection"))
+ && mainProps.contains(QLatin1String("Interfaces"))
+ && mainProps.contains(QLatin1String("PossibleHandlers"))) {
+ debug() << "Supplied properties were sufficient, not introspecting"
+ << self->parent->objectPath();
+ self->extractMainProps(mainProps, true);
+ return;
+ }
+
+ debug() << "Calling Properties::GetAll(ChannelDispatchOperation)";
+ QDBusPendingCallWatcher *watcher =
+ new QDBusPendingCallWatcher(
+ self->properties->GetAll(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_DISPATCH_OPERATION)),
+ self->parent);
+ self->parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotMainProperties(QDBusPendingCallWatcher*)));
+}
+
+void ChannelDispatchOperation::Private::extractMainProps(const QVariantMap &props,
+ bool immutableProperties)
+{
+ parent->setInterfaces(qdbus_cast<QStringList>(props.value(QLatin1String("Interfaces"))));
+
+ QList<PendingOperation *> readyOps;
+
+ if (!connection && props.contains(QLatin1String("Connection"))) {
+ QDBusObjectPath connectionObjectPath =
+ qdbus_cast<QDBusObjectPath>(props.value(QLatin1String("Connection")));
+ QString connectionBusName =
+ connectionObjectPath.path().mid(1).replace(QLatin1String("/"),
+ QLatin1String("."));
+
+ PendingReady *readyOp =
+ connFactory->proxy(connectionBusName, connectionObjectPath.path(),
+ chanFactory, contactFactory);
+ connection = ConnectionPtr::qObjectCast(readyOp->proxy());
+ readyOps.append(readyOp);
+ }
+
+ if (!account && props.contains(QLatin1String("Account"))) {
+ QDBusObjectPath accountObjectPath =
+ qdbus_cast<QDBusObjectPath>(props.value(QLatin1String("Account")));
+
+ PendingReady *readyOp =
+ accFactory->proxy(TP_QT_ACCOUNT_MANAGER_BUS_NAME,
+ accountObjectPath.path(), connFactory, chanFactory, contactFactory);
+ account = AccountPtr::qObjectCast(readyOp->proxy());
+ readyOps.append(readyOp);
+ }
+
+ if (!immutableProperties) {
+ // If we're here, it means we had to introspect the object, and now for sure have the
+ // correct channels list, so let's overwrite the initial channels - but keep the refs around
+ // for a while as an optimization enabling the factory to still return the same ones instead
+ // of constructing everything anew. Note that this is not done at all in the case the
+ // immutable props and initial channels etc were sufficient.
+ QList<ChannelPtr> saveChannels = channels;
+ channels.clear();
+
+ ChannelDetailsList channelDetailsList =
+ qdbus_cast<ChannelDetailsList>(props.value(QLatin1String("Channels")));
+ ChannelPtr channel;
+ foreach (const ChannelDetails &channelDetails, channelDetailsList) {
+ PendingReady *readyOp =
+ chanFactory->proxy(connection,
+ channelDetails.channel.path(), channelDetails.properties);
+ channels.append(ChannelPtr::qObjectCast(readyOp->proxy()));
+ readyOps.append(readyOp);
+ }
+
+ // saveChannels goes out of scope now, so any initial channels which don't exist anymore are
+ // freed
+ }
+
+ if (props.contains(QLatin1String("PossibleHandlers"))) {
+ possibleHandlers = qdbus_cast<QStringList>(props.value(QLatin1String("PossibleHandlers")));
+ gotPossibleHandlers = true;
+ }
+
+ if (readyOps.isEmpty()) {
+ debug() << "No proxies to prepare for CDO" << parent->objectPath();
+ readinessHelper->setIntrospectCompleted(FeatureCore, true);
+ } else {
+ parent->connect(new PendingComposite(readyOps, ChannelDispatchOperationPtr(parent)),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onProxiesPrepared(Tp::PendingOperation*)));
+ }
+}
+
+ChannelDispatchOperation::PendingClaim::PendingClaim(const ChannelDispatchOperationPtr &op,
+ const AbstractClientHandlerPtr &handler)
+ : PendingOperation(op),
+ mDispatchOp(op),
+ mHandler(handler)
+{
+ debug() << "Invoking CDO.Claim";
+ connect(new PendingVoid(op->baseInterface()->Claim(), op),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onClaimFinished(Tp::PendingOperation*)));
+}
+
+ChannelDispatchOperation::PendingClaim::~PendingClaim()
+{
+}
+
+void ChannelDispatchOperation::PendingClaim::onClaimFinished(
+ PendingOperation *op)
+{
+ if (!op->isError()) {
+ debug() << "CDO.Claim returned successfully, updating HandledChannels";
+ if (mHandler) {
+ // register the channels in HandledChannels
+ FakeHandlerManager::instance()->registerChannels(
+ mDispatchOp->channels());
+ }
+ setFinished();
+ } else {
+ warning() << "CDO.Claim failed with" << op->errorName() << "-" << op->errorMessage();
+ setFinishedWithError(op->errorName(), op->errorMessage());
+ }
+}
+
+/**
+ * \class ChannelDispatchOperation
+ * \ingroup clientchanneldispatchoperation
+ * \headerfile TelepathyQt/channel-dispatch-operation.h <TelepathyQt/ChannelDispatchOperation>
+ *
+ * \brief The ChannelDispatchOperation class represents a Telepathy channel
+ * dispatch operation.
+ *
+ * One of the channel dispatcher's functions is to offer incoming channels to
+ * Approver clients for approval. An approver should generally ask the user
+ * whether they want to participate in the requested communication channels
+ * (join the chat or chatroom, answer the call, accept the file transfer, or
+ * whatever is appropriate). A collection of channels offered in this way
+ * is represented by a ChannelDispatchOperation object.
+ *
+ * If the user wishes to accept the communication channels, the approver
+ * should call handleWith() to indicate the user's or approver's preferred
+ * handler for the channels (the empty string indicates no particular
+ * preference, and will cause any suitable handler to be used).
+ *
+ * If the user wishes to reject the communication channels, or if the user
+ * accepts the channels and the approver will handle them itself, the approver
+ * should call claim(). If the resulting PendingOperation succeeds, the approver
+ * immediately has control over the channels as their primary handler,
+ * and may do anything with them (in particular, it may close them in whatever
+ * way seems most appropriate).
+ *
+ * There are various situations in which the channel dispatch operation will
+ * be closed, causing the DBusProxy::invalidated() signal to be emitted. If this
+ * happens, the approver should stop prompting the user.
+ *
+ * Because all approvers are launched simultaneously, the user might respond
+ * to another approver; if this happens, the invalidated signal will be
+ * emitted with the error code #TP_QT_ERROR_OBJECT_REMOVED.
+ *
+ * If a channel closes, the signal channelLost() is emitted. If all channels
+ * close, there is nothing more to dispatch, so the invalidated signal will be
+ * emitted with the error code #TP_QT_ERROR_OBJECT_REMOVED.
+ *
+ * If the channel dispatcher crashes or exits, the invalidated
+ * signal will be emitted with the error code
+ * #TP_QT_DBUS_ERROR_NAME_HAS_NO_OWNER. In a high-quality implementation,
+ * the dispatcher should be restarted, at which point it will create new
+ * channel dispatch operations for any undispatched channels, and the approver
+ * will be notified again.
+ */
+
+/**
+ * Feature representing the core that needs to become ready to make the
+ * ChannelDispatchOperation object usable.
+ *
+ * Note that this feature must be enabled in order to use most
+ * ChannelDispatchOperation methods.
+ *
+ * When calling isReady(), becomeReady(), this feature is implicitly added
+ * to the requested features.
+ */
+const Feature ChannelDispatchOperation::FeatureCore = Feature(QLatin1String(ChannelDispatchOperation::staticMetaObject.className()), 0, true);
+
+/**
+ * Create a new channel dispatch operation object using the given \a bus, the given factories and
+ * the given initial channels.
+ *
+ * \param bus QDBusConnection to use.
+ * \param objectPath The channel dispatch operation object path.
+ * \param immutableProperties The channel dispatch operation immutable properties.
+ * \param initialChannels The channels this CDO has initially (further tracking is done internally).
+ * \param accountFactory The account factory to use.
+ * \param connectionFactory The connection factory to use.
+ * \param channelFactory The channel factory to use.
+ * \param contactFactory The contact factory to use.
+ * \return A ChannelDispatchOperationPtr object pointing to the newly created
+ * ChannelDispatchOperation object.
+ */
+ChannelDispatchOperationPtr ChannelDispatchOperation::create(const QDBusConnection &bus,
+ const QString &objectPath, const QVariantMap &immutableProperties,
+ const QList<ChannelPtr> &initialChannels,
+ const AccountFactoryConstPtr &accountFactory,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory)
+{
+ return ChannelDispatchOperationPtr(new ChannelDispatchOperation(
+ bus, objectPath, immutableProperties, initialChannels, accountFactory,
+ connectionFactory, channelFactory, contactFactory));
+}
+
+/**
+ * Construct a new channel dispatch operation object using the given \a bus, the given factories and
+ * the given initial channels.
+ *
+ * \param bus QDBusConnection to use
+ * \param objectPath The channel dispatch operation object path.
+ * \param immutableProperties The channel dispatch operation immutable properties.
+ * \param initialChannels The channels this CDO has initially (further tracking is done internally).
+ * \param accountFactory The account factory to use.
+ * \param connectionFactory The connection factory to use.
+ * \param channelFactory The channel factory to use.
+ * \param contactFactory The contact factory to use.
+ */
+ChannelDispatchOperation::ChannelDispatchOperation(const QDBusConnection &bus,
+ const QString &objectPath, const QVariantMap &immutableProperties,
+ const QList<ChannelPtr> &initialChannels,
+ const AccountFactoryConstPtr &accountFactory,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory)
+ : StatefulDBusProxy(bus,
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL_DISPATCHER),
+ objectPath, FeatureCore),
+ OptionalInterfaceFactory<ChannelDispatchOperation>(this),
+ mPriv(new Private(this))
+{
+ if (accountFactory->dbusConnection().name() != bus.name()) {
+ warning() << " The D-Bus connection in the account factory is not the proxy connection";
+ }
+
+ if (connectionFactory->dbusConnection().name() != bus.name()) {
+ warning() << " The D-Bus connection in the connection factory is not the proxy connection";
+ }
+
+ if (channelFactory->dbusConnection().name() != bus.name()) {
+ warning() << " The D-Bus connection in the channel factory is not the proxy connection";
+ }
+
+ mPriv->channels = initialChannels;
+
+ mPriv->accFactory = accountFactory;
+ mPriv->connFactory = connectionFactory;
+ mPriv->chanFactory = channelFactory;
+ mPriv->contactFactory = contactFactory;
+
+ mPriv->immutableProperties = immutableProperties;
+}
+
+/**
+ * Class destructor.
+ */
+ChannelDispatchOperation::~ChannelDispatchOperation()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the connection with which the channels for this dispatch
+ * operation are associated.
+ *
+ * This method requires ChannelDispatchOperation::FeatureCore to be ready.
+ *
+ * \return A pointer to the Connection object.
+ */
+ConnectionPtr ChannelDispatchOperation::connection() const
+{
+ return mPriv->connection;
+}
+
+/**
+ * Return the account with which the connection and channels for this dispatch
+ * operation are associated.
+ *
+ * This method requires ChannelDispatchOperation::FeatureCore to be ready.
+ *
+ * \return A pointer to the Account object.
+ */
+AccountPtr ChannelDispatchOperation::account() const
+{
+ return mPriv->account;
+}
+
+/**
+ * Return the channels to be dispatched.
+ *
+ * This method requires ChannelDispatchOperation::FeatureCore to be ready.
+ *
+ * \return A list of pointers to Channel objects.
+ */
+QList<ChannelPtr> ChannelDispatchOperation::channels() const
+{
+ if (!isReady()) {
+ warning() << "ChannelDispatchOperation::channels called with channel "
+ "not ready";
+ }
+ return mPriv->channels;
+}
+
+/**
+ * Return the well known bus names (starting with
+ * org.freedesktop.Telepathy.Client.) of the possible Handlers for this
+ * dispatch operation channels with the preferred handlers first.
+ *
+ * As a result, approvers should use the first handler by default, unless they
+ * have a reason to do otherwise.
+ *
+ * This method requires ChannelDispatchOperation::FeatureCore to be ready.
+ *
+ * \return List of possible handlers names.
+ */
+QStringList ChannelDispatchOperation::possibleHandlers() const
+{
+ return mPriv->possibleHandlers;
+}
+
+/**
+ * Called by an approver to accept a channel bundle and request that the given
+ * handler be used to handle it.
+ *
+ * If successful, this method will cause the ChannelDispatchOperation object to
+ * disappear, emitting invalidated with error
+ * #TP_QT_ERROR_OBJECT_REMOVED.
+ *
+ * However, this method may fail because the dispatch has already been completed
+ * and the object has already gone. If this occurs, it indicates that another
+ * approver has asked for the bundle to be handled by a particular handler. The
+ * approver must not attempt to interact with the channels further in this case,
+ * unless it is separately invoked as the handler.
+ *
+ * Approvers which are also channel handlers should use claim() instead of
+ * this method to request that they can handle a channel bundle themselves.
+ *
+ * \param handler The well-known bus name (starting with
+ * org.freedesktop.Telepathy.Client.) of the channel handler that
+ * should handle the channel, or an empty string if
+ * the client has no preferred channel handler.
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the call has finished.
+ */
+PendingOperation *ChannelDispatchOperation::handleWith(const QString &handler)
+{
+ return new PendingVoid(
+ mPriv->baseInterface->HandleWith(handler),
+ ChannelDispatchOperationPtr(this));
+}
+
+/**
+ * Called by an approver to claim channels for closing them.
+ *
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the call has finished.
+ */
+PendingOperation *ChannelDispatchOperation::claim()
+{
+ return new PendingClaim(ChannelDispatchOperationPtr(this));
+}
+
+/**
+ * Called by an approver to claim channels for handling internally. If this
+ * method is called successfully, the \a handler becomes the
+ * handler for the channel, but does not have the
+ * AbstractClientHandler::handleChannels() method called on it.
+ *
+ * Approvers wishing to reject channels must call this method to claim ownership
+ * of them, and must not call requestClose() on the channels unless/until this
+ * method returns successfully.
+ *
+ * The channel dispatcher can't know how best to close arbitrary channel types,
+ * so it leaves it up to the approver to do so. For instance, for text channels
+ * it is necessary to acknowledge any messages that have already been displayed
+ * to the user first - ideally, the approver would display and then acknowledge
+ * the messages - or to call Channel::requestClose() if the destructive
+ * behaviour of that method is desired.
+ *
+ * Similarly, an approver for streamed media channels can close the channel with
+ * a reason (e.g. "busy") if desired. The channel dispatcher, which is designed
+ * to have no specific knowledge of particular channel types, can't do that.
+ *
+ * If successful, this method will cause the ChannelDispatchOperation object to
+ * disappear, emitting Finished, in the same way as for handleWith().
+ *
+ * This method may fail because the dispatch operation has already been
+ * completed. Again, see handleWith() for more details. The approver must not
+ * attempt to interact with the channels further in this case.
+ *
+ * \param handler The channel handler, that should remain registered during the
+ * lifetime of channels(), otherwise dispatching will fail if the
+ * channel dispatcher restarts.
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the call has finished.
+ * \sa claim(), handleWith()
+ */
+PendingOperation *ChannelDispatchOperation::claim(const AbstractClientHandlerPtr &handler)
+{
+ if (!handler->isRegistered()) {
+ return new PendingFailure(TP_QT_ERROR_INVALID_ARGUMENT,
+ QLatin1String("Handler must be registered for using claim(handler)"),
+ ChannelDispatchOperationPtr(this));
+ }
+
+ return new PendingClaim(ChannelDispatchOperationPtr(this),
+ handler);
+}
+
+/**
+ * \fn void ChannelDispatchOperation::channelLost(const ChannelPtr &channel,
+ * const QString &errorName, const QString &errorMessage);
+ *
+ * Emitted when a channel has closed before it could be claimed or handled. If this is
+ * emitted for the last remaining channel in a channel dispatch operation, it
+ * will immediately be followed by invalidated() with error
+ * #TP_QT_ERROR_OBJECT_REMOVED.
+ *
+ * \param channel The channel that was closed.
+ * \param error The name of a D-Bus error indicating why the channel closed.
+ * \param errorMessage The error message.
+ */
+
+/**
+ * Return the ChannelDispatchOperationInterface for this ChannelDispatchOperation
+ * class. This method is protected since the convenience methods provided by
+ * this class should always be used instead of the interface by users of the
+ * class.
+ *
+ * \return A pointer to the existing Client::ChannelDispatchOperationInterface object for this
+ * ChannelDispatchOperation object.
+ */
+Client::ChannelDispatchOperationInterface *ChannelDispatchOperation::baseInterface() const
+{
+ return mPriv->baseInterface;
+}
+
+void ChannelDispatchOperation::onFinished()
+{
+ debug() << "ChannelDispatchOperation finished and was removed";
+ invalidate(TP_QT_ERROR_OBJECT_REMOVED,
+ QLatin1String("ChannelDispatchOperation finished and was removed"));
+}
+
+void ChannelDispatchOperation::gotMainProperties(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QVariantMap> reply = *watcher;
+
+ // Watcher is NULL if we didn't have to introspect at all
+ if (!reply.isError()) {
+ debug() << "Got reply to Properties::GetAll(ChannelDispatchOperation)";
+ mPriv->extractMainProps(reply.value(), false);
+ } else {
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureCore,
+ false, reply.error());
+ warning().nospace() << "Properties::GetAll(ChannelDispatchOperation) failed with "
+ << reply.error().name() << ": " << reply.error().message();
+ }
+}
+
+void ChannelDispatchOperation::onChannelLost(
+ const QDBusObjectPath &channelObjectPath,
+ const QString &errorName, const QString &errorMessage)
+{
+ foreach (const ChannelPtr &channel, mPriv->channels) {
+ if (channel->objectPath() == channelObjectPath.path()) {
+ emit channelLost(channel, errorName, errorMessage);
+ mPriv->channels.removeOne(channel);
+ return;
+ }
+ }
+}
+
+void ChannelDispatchOperation::onProxiesPrepared(Tp::PendingOperation *op)
+{
+ if (op->isError()) {
+ warning() << "Preparing proxies for CDO" << objectPath() << "failed with"
+ << op->errorName() << ":" << op->errorMessage();
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureCore, false);
+ } else {
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureCore, true);
+ }
+}
+
+} // Tp
diff --git a/TelepathyQt/channel-dispatch-operation.h b/TelepathyQt/channel-dispatch-operation.h
new file mode 100644
index 00000000..9d319d23
--- /dev/null
+++ b/TelepathyQt/channel-dispatch-operation.h
@@ -0,0 +1,114 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_channel_dispatch_operation_h_HEADER_GUARD_
+#define _TelepathyQt_channel_dispatch_operation_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/_gen/cli-channel-dispatch-operation.h>
+
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/DBus>
+#include <TelepathyQt/DBusProxy>
+#include <TelepathyQt/Feature>
+#include <TelepathyQt/OptionalInterfaceFactory>
+#include <TelepathyQt/ReadinessHelper>
+#include <TelepathyQt/Types>
+#include <TelepathyQt/SharedPtr>
+
+#include <QString>
+#include <QStringList>
+#include <QVariantMap>
+
+namespace Tp
+{
+
+class PendingOperation;
+
+class TP_QT_EXPORT ChannelDispatchOperation : public StatefulDBusProxy,
+ public OptionalInterfaceFactory<ChannelDispatchOperation>
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(ChannelDispatchOperation)
+
+public:
+ static const Feature FeatureCore;
+
+ static ChannelDispatchOperationPtr create(const QDBusConnection &bus,
+ const QString &objectPath, const QVariantMap &immutableProperties,
+ const QList<ChannelPtr> &initialChannels,
+ const AccountFactoryConstPtr &accountFactory,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory);
+ virtual ~ChannelDispatchOperation();
+
+ ConnectionPtr connection() const;
+
+ AccountPtr account() const;
+
+ QList<ChannelPtr> channels() const;
+
+ QStringList possibleHandlers() const;
+
+ PendingOperation *handleWith(const QString &handler);
+
+ PendingOperation *claim();
+ PendingOperation *claim(const AbstractClientHandlerPtr &handler);
+
+Q_SIGNALS:
+ void channelLost(const Tp::ChannelPtr &channel, const QString &errorName,
+ const QString &errorMessage);
+
+protected:
+ ChannelDispatchOperation(const QDBusConnection &bus,
+ const QString &objectPath, const QVariantMap &immutableProperties,
+ const QList<ChannelPtr> &initialChannels,
+ const AccountFactoryConstPtr &accountFactory,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory);
+
+ Client::ChannelDispatchOperationInterface *baseInterface() const;
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onFinished();
+ TP_QT_NO_EXPORT void gotMainProperties(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void onChannelLost(const QDBusObjectPath &channelObjectPath,
+ const QString &errorName, const QString &errorMessage);
+ TP_QT_NO_EXPORT void onProxiesPrepared(Tp::PendingOperation *op);
+
+private:
+ class PendingClaim;
+ friend class PendingClaim;
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/channel-dispatch-operation.xml b/TelepathyQt/channel-dispatch-operation.xml
new file mode 100644
index 00000000..d7a6cdda
--- /dev/null
+++ b/TelepathyQt/channel-dispatch-operation.xml
@@ -0,0 +1,9 @@
+<tp:spec
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<tp:title>Channel Dispatch Operation interface</tp:title>
+
+<xi:include href="../spec/Channel_Dispatch_Operation.xml"/>
+
+</tp:spec>
diff --git a/TelepathyQt/channel-dispatcher.cpp b/TelepathyQt/channel-dispatcher.cpp
new file mode 100644
index 00000000..b86b617a
--- /dev/null
+++ b/TelepathyQt/channel-dispatcher.cpp
@@ -0,0 +1,26 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/ChannelDispatcher>
+
+#include "TelepathyQt/_gen/cli-channel-dispatcher-body.hpp"
+#include "TelepathyQt/_gen/cli-channel-dispatcher.moc.hpp"
diff --git a/TelepathyQt/channel-dispatcher.h b/TelepathyQt/channel-dispatcher.h
new file mode 100644
index 00000000..d94284e9
--- /dev/null
+++ b/TelepathyQt/channel-dispatcher.h
@@ -0,0 +1,32 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_channel_dispatcher_h_HEADER_GUARD_
+#define _TelepathyQt_channel_dispatcher_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/_gen/cli-channel-dispatcher.h>
+
+#endif
diff --git a/TelepathyQt/channel-dispatcher.xml b/TelepathyQt/channel-dispatcher.xml
new file mode 100644
index 00000000..a1588a42
--- /dev/null
+++ b/TelepathyQt/channel-dispatcher.xml
@@ -0,0 +1,9 @@
+<tp:spec
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<tp:title>Channel Dispatcher interface</tp:title>
+
+<xi:include href="../spec/Channel_Dispatcher.xml"/>
+
+</tp:spec>
diff --git a/TelepathyQt/channel-factory.cpp b/TelepathyQt/channel-factory.cpp
new file mode 100644
index 00000000..10bf4cbd
--- /dev/null
+++ b/TelepathyQt/channel-factory.cpp
@@ -0,0 +1,529 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/ChannelFactory>
+
+#include "TelepathyQt/_gen/channel-factory.moc.hpp"
+
+#include "TelepathyQt/_gen/future-constants.h"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/Channel>
+#include <TelepathyQt/ChannelClassSpec>
+#include <TelepathyQt/ChannelClassFeatures>
+#include <TelepathyQt/Connection>
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/ContactSearchChannel>
+#include <TelepathyQt/FileTransferChannel>
+#include <TelepathyQt/IncomingFileTransferChannel>
+#include <TelepathyQt/IncomingStreamTubeChannel>
+#include <TelepathyQt/OutgoingFileTransferChannel>
+#include <TelepathyQt/OutgoingStreamTubeChannel>
+#include <TelepathyQt/RoomListChannel>
+#include <TelepathyQt/StreamTubeChannel>
+#include <TelepathyQt/StreamedMediaChannel>
+#include <TelepathyQt/TextChannel>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT ChannelFactory::Private
+{
+ Private();
+
+ QList<ChannelClassFeatures> features;
+
+ typedef QPair<ChannelClassSpec, ConstructorConstPtr> CtorPair;
+ QList<CtorPair> ctors;
+};
+
+ChannelFactory::Private::Private()
+{
+}
+
+/**
+ * \class ChannelFactory
+ * \ingroup utils
+ * \headerfile TelepathyQt/channel-factory.h <TelepathyQt/ChannelFactory>
+ *
+ * \brief The ChannelFactory class is responsible for constructing Channel
+ * objects according to application-defined settings.
+ */
+
+/**
+ * Create a new ChannelFactory object.
+ *
+ * The returned factory will construct channel subclasses provided by TelepathyQt as appropriate
+ * for the channel immutable properties, but not make any features ready.
+ *
+ * \param bus The QDBusConnection the proxies constructed using this factory should use.
+ * \return An ChannelFactoryPtr pointing to the newly created factory.
+ */
+ChannelFactoryPtr ChannelFactory::create(const QDBusConnection &bus)
+{
+ return ChannelFactoryPtr(new ChannelFactory(bus));
+}
+
+/**
+ * Construct a new ChannelFactory object.
+ *
+ * The constructed factory will construct channel subclasses provided by TelepathyQt as appropriate
+ * for the channel immutable properties, but not make any features ready.
+ *
+ * \param bus The QDBusConnection the proxies constructed using this factory should use.
+ */
+ChannelFactory::ChannelFactory(const QDBusConnection &bus)
+ : DBusProxyFactory(bus), mPriv(new Private)
+{
+ setSubclassForTextChats<TextChannel>();
+ setSubclassForTextChatrooms<TextChannel>();
+ setSubclassForStreamedMediaCalls<StreamedMediaChannel>();
+ setSubclassForRoomLists<RoomListChannel>();
+ setSubclassForIncomingFileTransfers<IncomingFileTransferChannel>();
+ setSubclassForOutgoingFileTransfers<OutgoingFileTransferChannel>();
+ setSubclassForIncomingStreamTubes<IncomingStreamTubeChannel>();
+ setSubclassForOutgoingStreamTubes<OutgoingStreamTubeChannel>();
+ setSubclassForIncomingRoomStreamTubes<IncomingStreamTubeChannel>();
+ setSubclassForOutgoingRoomStreamTubes<OutgoingStreamTubeChannel>();
+ setSubclassForContactSearches<ContactSearchChannel>();
+ setFallbackSubclass<Channel>();
+}
+
+/**
+ * Class destructor.
+ */
+ChannelFactory::~ChannelFactory()
+{
+ delete mPriv;
+}
+
+Features ChannelFactory::featuresForTextChats(const QVariantMap &additionalProps) const
+{
+ return featuresFor(ChannelClassSpec::textChat(additionalProps));
+}
+
+void ChannelFactory::addFeaturesForTextChats(const Features &features,
+ const QVariantMap &additionalProps)
+{
+ addFeaturesFor(ChannelClassSpec::textChat(additionalProps), features);
+ addFeaturesFor(ChannelClassSpec::unnamedTextChat(additionalProps), features);
+}
+
+ChannelFactory::ConstructorConstPtr ChannelFactory::constructorForTextChats(
+ const QVariantMap &additionalProps) const
+{
+ return constructorFor(ChannelClassSpec::textChat(additionalProps));
+}
+
+void ChannelFactory::setConstructorForTextChats(const ConstructorConstPtr &ctor,
+ const QVariantMap &additionalProps)
+{
+ setConstructorFor(ChannelClassSpec::textChat(additionalProps), ctor);
+ setConstructorFor(ChannelClassSpec::unnamedTextChat(additionalProps), ctor);
+}
+
+Features ChannelFactory::featuresForTextChatrooms(const QVariantMap &additionalProps) const
+{
+ return featuresFor(ChannelClassSpec::textChatroom(additionalProps));
+}
+
+void ChannelFactory::addFeaturesForTextChatrooms(const Features &features,
+ const QVariantMap &additionalProps)
+{
+ addFeaturesFor(ChannelClassSpec::textChatroom(additionalProps), features);
+}
+
+ChannelFactory::ConstructorConstPtr ChannelFactory::constructorForTextChatrooms(
+ const QVariantMap &additionalProps) const
+{
+ return constructorFor(ChannelClassSpec::textChatroom(additionalProps));
+}
+
+void ChannelFactory::setConstructorForTextChatrooms(const ConstructorConstPtr &ctor,
+ const QVariantMap &additionalProps)
+{
+ setConstructorFor(ChannelClassSpec::textChatroom(additionalProps), ctor);
+}
+
+Features ChannelFactory::featuresForStreamedMediaCalls(const QVariantMap &additionalProps) const
+{
+ return featuresFor(ChannelClassSpec::streamedMediaCall(additionalProps));
+}
+
+void ChannelFactory::addFeaturesForStreamedMediaCalls(const Features &features,
+ const QVariantMap &additionalProps)
+{
+ ChannelClassSpec smSpec = ChannelClassSpec::streamedMediaCall(additionalProps);
+ ChannelClassSpec unnamedSMSpec = ChannelClassSpec::unnamedStreamedMediaCall(additionalProps);
+
+ addFeaturesFor(smSpec, features);
+ addFeaturesFor(unnamedSMSpec, features);
+}
+
+ChannelFactory::ConstructorConstPtr ChannelFactory::constructorForStreamedMediaCalls(
+ const QVariantMap &additionalProps) const
+{
+ return constructorFor(ChannelClassSpec::streamedMediaCall(additionalProps));
+}
+
+void ChannelFactory::setConstructorForStreamedMediaCalls(const ConstructorConstPtr &ctor,
+ const QVariantMap &additionalProps)
+{
+ ChannelClassSpec smSpec = ChannelClassSpec::streamedMediaCall(additionalProps);
+ ChannelClassSpec unnamedSMSpec = ChannelClassSpec::unnamedStreamedMediaCall(additionalProps);
+
+ setConstructorFor(smSpec, ctor);
+ setConstructorFor(unnamedSMSpec, ctor);
+}
+
+Features ChannelFactory::featuresForRoomLists(const QVariantMap &additionalProps) const
+{
+ return featuresFor(ChannelClassSpec::roomList(additionalProps));
+}
+
+void ChannelFactory::addFeaturesForRoomLists(const Features &features,
+ const QVariantMap &additionalProps)
+{
+ addFeaturesFor(ChannelClassSpec::roomList(additionalProps), features);
+}
+
+ChannelFactory::ConstructorConstPtr ChannelFactory::constructorForRoomLists(
+ const QVariantMap &additionalProps) const
+{
+ return constructorFor(ChannelClassSpec::roomList(additionalProps));
+}
+
+void ChannelFactory::setConstructorForRoomLists(const ConstructorConstPtr &ctor,
+ const QVariantMap &additionalProps)
+{
+ setConstructorFor(ChannelClassSpec::roomList(additionalProps), ctor);
+}
+
+Features ChannelFactory::featuresForOutgoingFileTransfers(const QVariantMap &additionalProps) const
+{
+ return featuresFor(ChannelClassSpec::outgoingFileTransfer(additionalProps));
+}
+
+void ChannelFactory::addFeaturesForOutgoingFileTransfers(const Features &features,
+ const QVariantMap &additionalProps)
+{
+ addFeaturesFor(ChannelClassSpec::outgoingFileTransfer(additionalProps), features);
+}
+
+ChannelFactory::ConstructorConstPtr ChannelFactory::constructorForOutgoingFileTransfers(
+ const QVariantMap &additionalProps) const
+{
+ return constructorFor(ChannelClassSpec::outgoingFileTransfer(additionalProps));
+}
+
+void ChannelFactory::setConstructorForOutgoingFileTransfers(const ConstructorConstPtr &ctor,
+ const QVariantMap &additionalProps)
+{
+ setConstructorFor(ChannelClassSpec::outgoingFileTransfer(additionalProps), ctor);
+}
+
+Features ChannelFactory::featuresForIncomingFileTransfers(const QVariantMap &additionalProps) const
+{
+ return featuresFor(ChannelClassSpec::incomingFileTransfer(additionalProps));
+}
+
+void ChannelFactory::addFeaturesForIncomingFileTransfers(const Features &features,
+ const QVariantMap &additionalProps)
+{
+ addFeaturesFor(ChannelClassSpec::incomingFileTransfer(additionalProps), features);
+}
+
+ChannelFactory::ConstructorConstPtr ChannelFactory::constructorForIncomingFileTransfers(
+ const QVariantMap &additionalProps) const
+{
+ return constructorFor(ChannelClassSpec::incomingFileTransfer(additionalProps));
+}
+
+void ChannelFactory::setConstructorForIncomingFileTransfers(const ConstructorConstPtr &ctor,
+ const QVariantMap &additionalProps)
+{
+ setConstructorFor(ChannelClassSpec::incomingFileTransfer(additionalProps), ctor);
+}
+
+Features ChannelFactory::featuresForOutgoingStreamTubes(const QVariantMap &additionalProps) const
+{
+ return featuresFor(ChannelClassSpec::outgoingStreamTube(QString(), additionalProps));
+}
+
+void ChannelFactory::addFeaturesForOutgoingStreamTubes(const Features &features,
+ const QVariantMap &additionalProps)
+{
+ addFeaturesFor(ChannelClassSpec::outgoingStreamTube(QString(), additionalProps), features);
+}
+
+ChannelFactory::ConstructorConstPtr ChannelFactory::constructorForOutgoingStreamTubes(
+ const QVariantMap &additionalProps) const
+{
+ return constructorFor(ChannelClassSpec::outgoingStreamTube(QString(), additionalProps));
+}
+
+void ChannelFactory::setConstructorForOutgoingStreamTubes(const ConstructorConstPtr &ctor,
+ const QVariantMap &additionalProps)
+{
+ setConstructorFor(ChannelClassSpec::outgoingStreamTube(QString(), additionalProps), ctor);
+}
+
+Features ChannelFactory::featuresForIncomingStreamTubes(const QVariantMap &additionalProps) const
+{
+ return featuresFor(ChannelClassSpec::incomingStreamTube(QString(), additionalProps));
+}
+
+void ChannelFactory::addFeaturesForIncomingStreamTubes(const Features &features,
+ const QVariantMap &additionalProps)
+{
+ addFeaturesFor(ChannelClassSpec::incomingStreamTube(QString(), additionalProps), features);
+}
+
+ChannelFactory::ConstructorConstPtr ChannelFactory::constructorForIncomingStreamTubes(
+ const QVariantMap &additionalProps) const
+{
+ return constructorFor(ChannelClassSpec::incomingStreamTube(QString(), additionalProps));
+}
+
+void ChannelFactory::setConstructorForIncomingStreamTubes(const ConstructorConstPtr &ctor,
+ const QVariantMap &additionalProps)
+{
+ setConstructorFor(ChannelClassSpec::incomingStreamTube(QString(), additionalProps), ctor);
+}
+
+Features ChannelFactory::featuresForOutgoingRoomStreamTubes(const QVariantMap &additionalProps) const
+{
+ return featuresFor(ChannelClassSpec::outgoingRoomStreamTube(QString(), additionalProps));
+}
+
+void ChannelFactory::addFeaturesForOutgoingRoomStreamTubes(const Features &features,
+ const QVariantMap &additionalProps)
+{
+ addFeaturesFor(ChannelClassSpec::outgoingRoomStreamTube(QString(), additionalProps), features);
+}
+
+ChannelFactory::ConstructorConstPtr ChannelFactory::constructorForOutgoingRoomStreamTubes(
+ const QVariantMap &additionalProps) const
+{
+ return constructorFor(ChannelClassSpec::outgoingRoomStreamTube(QString(), additionalProps));
+}
+
+void ChannelFactory::setConstructorForOutgoingRoomStreamTubes(const ConstructorConstPtr &ctor,
+ const QVariantMap &additionalProps)
+{
+ setConstructorFor(ChannelClassSpec::outgoingRoomStreamTube(QString(), additionalProps), ctor);
+}
+
+Features ChannelFactory::featuresForIncomingRoomStreamTubes(const QVariantMap &additionalProps) const
+{
+ return featuresFor(ChannelClassSpec::incomingRoomStreamTube(QString(), additionalProps));
+}
+
+void ChannelFactory::addFeaturesForIncomingRoomStreamTubes(const Features &features,
+ const QVariantMap &additionalProps)
+{
+ addFeaturesFor(ChannelClassSpec::incomingRoomStreamTube(QString(), additionalProps), features);
+}
+
+ChannelFactory::ConstructorConstPtr ChannelFactory::constructorForIncomingRoomStreamTubes(
+ const QVariantMap &additionalProps) const
+{
+ return constructorFor(ChannelClassSpec::incomingRoomStreamTube(QString(), additionalProps));
+}
+
+void ChannelFactory::setConstructorForIncomingRoomStreamTubes(const ConstructorConstPtr &ctor,
+ const QVariantMap &additionalProps)
+{
+ setConstructorFor(ChannelClassSpec::incomingRoomStreamTube(QString(), additionalProps), ctor);
+}
+
+Features ChannelFactory::featuresForContactSearches(const QVariantMap &additionalProps) const
+{
+ return featuresFor(ChannelClassSpec::contactSearch(additionalProps));
+}
+
+void ChannelFactory::addFeaturesForContactSearches(const Features &features,
+ const QVariantMap &additionalProps)
+{
+ addFeaturesFor(ChannelClassSpec::contactSearch(additionalProps), features);
+}
+
+ChannelFactory::ConstructorConstPtr ChannelFactory::constructorForContactSearches(
+ const QVariantMap &additionalProps) const
+{
+ return constructorFor(ChannelClassSpec::contactSearch(additionalProps));
+}
+
+void ChannelFactory::setConstructorForContactSearches(const ConstructorConstPtr &ctor,
+ const QVariantMap &additionalProps)
+{
+ setConstructorFor(ChannelClassSpec::contactSearch(additionalProps), ctor);
+}
+
+Features ChannelFactory::commonFeatures() const
+{
+ return featuresFor(ChannelClassSpec());
+}
+
+void ChannelFactory::addCommonFeatures(const Features &features)
+{
+ addFeaturesFor(ChannelClassSpec(), features);
+}
+
+ChannelFactory::ConstructorConstPtr ChannelFactory::fallbackConstructor() const
+{
+ return constructorFor(ChannelClassSpec());
+}
+
+void ChannelFactory::setFallbackConstructor(const ConstructorConstPtr &ctor)
+{
+ setConstructorFor(ChannelClassSpec(), ctor);
+}
+
+Features ChannelFactory::featuresFor(const ChannelClassSpec &channelClass) const
+{
+ Features features;
+
+ foreach (const ChannelClassFeatures &pair, mPriv->features) {
+ if (pair.first.isSubsetOf(channelClass)) {
+ features.unite(pair.second);
+ }
+ }
+
+ return features;
+}
+
+void ChannelFactory::addFeaturesFor(const ChannelClassSpec &channelClass, const Features &features)
+{
+ QList<ChannelClassFeatures>::iterator i;
+ for (i = mPriv->features.begin(); i != mPriv->features.end(); ++i) {
+ if (channelClass.allProperties().size() > i->first.allProperties().size()) {
+ break;
+ }
+
+ if (i->first == channelClass) {
+ i->second.unite(features);
+ return;
+ }
+ }
+
+ // We ran out of feature specifications (for the given size/specificity of a channel class)
+ // before finding a matching one, so let's create a new entry
+ mPriv->features.insert(i, qMakePair(channelClass, features));
+}
+
+ChannelFactory::ConstructorConstPtr ChannelFactory::constructorFor(const ChannelClassSpec &cc) const
+{
+ QList<Private::CtorPair>::iterator i;
+ for (i = mPriv->ctors.begin(); i != mPriv->ctors.end(); ++i) {
+ if (i->first.isSubsetOf(cc)) {
+ return i->second;
+ }
+ }
+
+ // If this is reached, we didn't have a proper fallback constructor
+ Q_ASSERT(false);
+ return ConstructorConstPtr();
+}
+
+void ChannelFactory::setConstructorFor(const ChannelClassSpec &channelClass,
+ const ConstructorConstPtr &ctor)
+{
+ if (ctor.isNull()) {
+ warning().nospace() << "Tried to set a NULL ctor for ChannelClass("
+ << channelClass.channelType() << ", " << channelClass.targetHandleType() << ", "
+ << channelClass.allProperties().size() << "props in total)";
+ return;
+ }
+
+ QList<Private::CtorPair>::iterator i;
+ for (i = mPriv->ctors.begin(); i != mPriv->ctors.end(); ++i) {
+ if (channelClass.allProperties().size() > i->first.allProperties().size()) {
+ break;
+ }
+
+ if (i->first == channelClass) {
+ i->second = ctor;
+ return;
+ }
+ }
+
+ // We ran out of constructors (for the given size/specificity of a channel class)
+ // before finding a matching one, so let's create a new entry
+ mPriv->ctors.insert(i, qMakePair(channelClass, ctor));
+}
+
+/**
+ * Constructs a Channel proxy and begins making it ready.
+ *
+ * If a valid proxy already exists in the factory cache for the given combination of \a busName and
+ * \a objectPath, it is returned instead. All newly created proxies are automatically cached until
+ * they're either DBusProxy::invalidated() or the last reference to them outside the factory has
+ * been dropped.
+ *
+ * The proxy can be accessed immediately after this function returns using PendingReady::proxy().
+ *
+ * \param connection Proxy for the owning connection of the channel.
+ * \param channelPath The object path of the channel.
+ * \param immutableProperties The immutable properties of the channel.
+ * \return A PendingReady operation with the proxy in PendingReady::proxy().
+ */
+PendingReady *ChannelFactory::proxy(const ConnectionPtr &connection, const QString &channelPath,
+ const QVariantMap &immutableProperties) const
+{
+ DBusProxyPtr proxy = cachedProxy(connection->busName(), channelPath);
+ if (proxy.isNull()) {
+ proxy = constructorFor(ChannelClassSpec(immutableProperties))->construct(connection,
+ channelPath, immutableProperties);
+ }
+
+ return nowHaveProxy(proxy);
+}
+
+/**
+ * Transforms well-known names to the corresponding unique names, as is appropriate for Channel
+ *
+ * \param uniqueOrWellKnown The name to transform.
+ * \return The unique name corresponding to \a uniqueOrWellKnown (which may be it itself).
+ */
+QString ChannelFactory::finalBusNameFrom(const QString &uniqueOrWellKnown) const
+{
+ return StatefulDBusProxy::uniqueNameFrom(dbusConnection(), uniqueOrWellKnown);
+}
+
+/**
+ * Return features as configured for the channel class given by the Channel::immutableProperties()
+ * of \a proxy.
+ *
+ * \param proxy The Channel proxy to determine the features for.
+ * \return A list of Feature objects.
+ */
+Features ChannelFactory::featuresFor(const DBusProxyPtr &proxy) const
+{
+ ChannelPtr chan = ChannelPtr::qObjectCast(proxy);
+ Q_ASSERT(!chan.isNull());
+
+ return featuresFor(ChannelClassSpec(chan->immutableProperties()));
+}
+
+} // Tp
diff --git a/TelepathyQt/channel-factory.h b/TelepathyQt/channel-factory.h
new file mode 100644
index 00000000..52503369
--- /dev/null
+++ b/TelepathyQt/channel-factory.h
@@ -0,0 +1,305 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_channel_factory_h_HEADER_GUARD_
+#define _TelepathyQt_channel_factory_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/DBusProxyFactory>
+#include <TelepathyQt/SharedPtr>
+#include <TelepathyQt/Types>
+
+// For Q_DISABLE_COPY
+#include <QtGlobal>
+#include <QString>
+#include <QVariantMap>
+
+class QDBusConnection;
+
+namespace Tp
+{
+
+class ChannelClassSpec;
+
+class TP_QT_EXPORT ChannelFactory : public DBusProxyFactory
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(ChannelFactory)
+
+public:
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+ struct TP_QT_EXPORT Constructor : public RefCounted
+ {
+ virtual ~Constructor() {}
+
+ virtual ChannelPtr construct(const ConnectionPtr &conn, const QString &objectPath,
+ const QVariantMap &immutableProperties) const = 0;
+ };
+ typedef SharedPtr<Constructor> ConstructorPtr;
+ typedef SharedPtr<const Constructor> ConstructorConstPtr;
+#endif /* DOXYGEN_SHOULD_SKIP_THIS */
+
+ static ChannelFactoryPtr create(const QDBusConnection &bus);
+
+ virtual ~ChannelFactory();
+
+ Features featuresForTextChats(const QVariantMap &additionalProps = QVariantMap()) const;
+ void addFeaturesForTextChats(const Features &features,
+ const QVariantMap &additionalProps = QVariantMap());
+
+ ConstructorConstPtr constructorForTextChats(
+ const QVariantMap &additionalProps = QVariantMap()) const;
+
+ template<typename Subclass>
+ void setSubclassForTextChats(const QVariantMap &additionalProps = QVariantMap())
+ {
+ setConstructorForTextChats(SubclassCtor<Subclass>::create(), additionalProps);
+ }
+
+ void setConstructorForTextChats(const ConstructorConstPtr &ctor,
+ const QVariantMap &additionalProps = QVariantMap());
+
+ Features featuresForTextChatrooms(const QVariantMap &additionalProps = QVariantMap()) const;
+ void addFeaturesForTextChatrooms(const Features &features,
+ const QVariantMap &additionalProps = QVariantMap());
+
+ ConstructorConstPtr constructorForTextChatrooms(
+ const QVariantMap &additionalProps = QVariantMap()) const;
+
+ template<typename Subclass>
+ void setSubclassForTextChatrooms(const QVariantMap &additionalProps = QVariantMap())
+ {
+ setConstructorForTextChatrooms(SubclassCtor<Subclass>::create(), additionalProps);
+ }
+
+ void setConstructorForTextChatrooms(const ConstructorConstPtr &ctor,
+ const QVariantMap &additionalProps = QVariantMap());
+
+ Features featuresForStreamedMediaCalls(const QVariantMap &additionalProps = QVariantMap()) const;
+ void addFeaturesForStreamedMediaCalls(const Features &features,
+ const QVariantMap &additionalProps = QVariantMap());
+
+ ConstructorConstPtr constructorForStreamedMediaCalls(
+ const QVariantMap &additionalProps = QVariantMap()) const;
+
+ template<typename Subclass>
+ void setSubclassForStreamedMediaCalls(const QVariantMap &additionalProps = QVariantMap())
+ {
+ setConstructorForStreamedMediaCalls(SubclassCtor<Subclass>::create(), additionalProps);
+ }
+
+ void setConstructorForStreamedMediaCalls(const ConstructorConstPtr &ctor,
+ const QVariantMap &additionalProps = QVariantMap());
+
+ Features featuresForRoomLists(const QVariantMap &additionalProps = QVariantMap()) const;
+ void addFeaturesForRoomLists(const Features &features,
+ const QVariantMap &additionalProps = QVariantMap());
+
+ ConstructorConstPtr constructorForRoomLists(
+ const QVariantMap &additionalProps = QVariantMap()) const;
+
+ template<typename Subclass>
+ void setSubclassForRoomLists(const QVariantMap &additionalProps = QVariantMap())
+ {
+ setConstructorForRoomLists(SubclassCtor<Subclass>::create(), additionalProps);
+ }
+
+ void setConstructorForRoomLists(const ConstructorConstPtr &ctor,
+ const QVariantMap &additionalProps = QVariantMap());
+
+ Features featuresForOutgoingFileTransfers(const QVariantMap &additionalProps = QVariantMap()) const;
+ void addFeaturesForOutgoingFileTransfers(const Features &features,
+ const QVariantMap &additionalProps = QVariantMap());
+
+ ConstructorConstPtr constructorForOutgoingFileTransfers(
+ const QVariantMap &additionalProps = QVariantMap()) const;
+
+ template<typename Subclass>
+ void setSubclassForOutgoingFileTransfers(const QVariantMap &additionalProps = QVariantMap())
+ {
+ setConstructorForOutgoingFileTransfers(SubclassCtor<Subclass>::create(), additionalProps);
+ }
+
+ void setConstructorForOutgoingFileTransfers(const ConstructorConstPtr &ctor,
+ const QVariantMap &additionalProps = QVariantMap());
+
+ Features featuresForIncomingFileTransfers(const QVariantMap &additionalProps = QVariantMap()) const;
+ void addFeaturesForIncomingFileTransfers(const Features &features,
+ const QVariantMap &additionalProps = QVariantMap());
+
+ ConstructorConstPtr constructorForIncomingFileTransfers(
+ const QVariantMap &additionalProps = QVariantMap()) const;
+
+ template<typename Subclass>
+ void setSubclassForIncomingFileTransfers(const QVariantMap &additionalProps = QVariantMap())
+ {
+ setConstructorForIncomingFileTransfers(SubclassCtor<Subclass>::create(), additionalProps);
+ }
+
+ void setConstructorForIncomingFileTransfers(const ConstructorConstPtr &ctor,
+ const QVariantMap &additionalProps = QVariantMap());
+
+ Features featuresForOutgoingStreamTubes(const QVariantMap &additionalProps = QVariantMap()) const;
+ void addFeaturesForOutgoingStreamTubes(const Features &features,
+ const QVariantMap &additionalProps = QVariantMap());
+
+ ConstructorConstPtr constructorForOutgoingStreamTubes(
+ const QVariantMap &additionalProps = QVariantMap()) const;
+
+ template<typename Subclass>
+ void setSubclassForOutgoingStreamTubes(const QVariantMap &additionalProps = QVariantMap())
+ {
+ setConstructorForOutgoingStreamTubes(SubclassCtor<Subclass>::create(), additionalProps);
+ }
+
+ void setConstructorForOutgoingStreamTubes(const ConstructorConstPtr &ctor,
+ const QVariantMap &additionalProps = QVariantMap());
+
+ Features featuresForIncomingStreamTubes(const QVariantMap &additionalProps = QVariantMap()) const;
+ void addFeaturesForIncomingStreamTubes(const Features &features,
+ const QVariantMap &additionalProps = QVariantMap());
+
+ ConstructorConstPtr constructorForIncomingStreamTubes(
+ const QVariantMap &additionalProps = QVariantMap()) const;
+
+ template<typename Subclass>
+ void setSubclassForIncomingStreamTubes(const QVariantMap &additionalProps = QVariantMap())
+ {
+ setConstructorForIncomingStreamTubes(SubclassCtor<Subclass>::create(), additionalProps);
+ }
+
+ void setConstructorForIncomingStreamTubes(const ConstructorConstPtr &ctor,
+ const QVariantMap &additionalProps = QVariantMap());
+
+ Features featuresForOutgoingRoomStreamTubes(const QVariantMap &additionalProps = QVariantMap()) const;
+ void addFeaturesForOutgoingRoomStreamTubes(const Features &features,
+ const QVariantMap &additionalProps = QVariantMap());
+
+ ConstructorConstPtr constructorForOutgoingRoomStreamTubes(
+ const QVariantMap &additionalProps = QVariantMap()) const;
+
+ template<typename Subclass>
+ void setSubclassForOutgoingRoomStreamTubes(const QVariantMap &additionalProps = QVariantMap())
+ {
+ setConstructorForOutgoingRoomStreamTubes(SubclassCtor<Subclass>::create(), additionalProps);
+ }
+
+ void setConstructorForOutgoingRoomStreamTubes(const ConstructorConstPtr &ctor,
+ const QVariantMap &additionalProps = QVariantMap());
+
+ Features featuresForIncomingRoomStreamTubes(const QVariantMap &additionalProps = QVariantMap()) const;
+ void addFeaturesForIncomingRoomStreamTubes(const Features &features,
+ const QVariantMap &additionalProps = QVariantMap());
+
+ ConstructorConstPtr constructorForIncomingRoomStreamTubes(
+ const QVariantMap &additionalProps = QVariantMap()) const;
+
+ template<typename Subclass>
+ void setSubclassForIncomingRoomStreamTubes(const QVariantMap &additionalProps = QVariantMap())
+ {
+ setConstructorForIncomingRoomStreamTubes(SubclassCtor<Subclass>::create(), additionalProps);
+ }
+
+ void setConstructorForIncomingRoomStreamTubes(const ConstructorConstPtr &ctor,
+ const QVariantMap &additionalProps = QVariantMap());
+
+ Features featuresForContactSearches(const QVariantMap &additionalProps = QVariantMap()) const;
+ void addFeaturesForContactSearches(const Features &features,
+ const QVariantMap &additionalProps = QVariantMap());
+
+ ConstructorConstPtr constructorForContactSearches(
+ const QVariantMap &additionalProps = QVariantMap()) const;
+
+ template<typename Subclass>
+ void setSubclassForContactSearches(const QVariantMap &additionalProps = QVariantMap())
+ {
+ setConstructorForContactSearches(SubclassCtor<Subclass>::create(), additionalProps);
+ }
+
+ void setConstructorForContactSearches(const ConstructorConstPtr &ctor,
+ const QVariantMap &additionalProps = QVariantMap());
+
+ Features commonFeatures() const;
+ void addCommonFeatures(const Features &features);
+
+ ConstructorConstPtr fallbackConstructor() const;
+
+ template <typename Subclass>
+ void setFallbackSubclass()
+ {
+ setFallbackConstructor(SubclassCtor<Subclass>::create());
+ }
+
+ void setFallbackConstructor(const ConstructorConstPtr &ctor);
+
+ Features featuresFor(const ChannelClassSpec &channelClass) const;
+ void addFeaturesFor(const ChannelClassSpec &channelClass, const Features &features);
+
+ template <typename Subclass>
+ void setSubclassFor(const ChannelClassSpec &channelClass)
+ {
+ setConstructorFor(channelClass, SubclassCtor<Subclass>::create());
+ }
+
+ ConstructorConstPtr constructorFor(const ChannelClassSpec &channelClass) const;
+ void setConstructorFor(const ChannelClassSpec &channelClass, const ConstructorConstPtr &ctor);
+
+ PendingReady *proxy(const ConnectionPtr &connection, const QString &channelPath,
+ const QVariantMap &immutableProperties) const;
+
+protected:
+ ChannelFactory(const QDBusConnection &bus);
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+ template <typename Subclass>
+ struct SubclassCtor : public Constructor
+ {
+ static ConstructorPtr create()
+ {
+ return ConstructorPtr(new SubclassCtor<Subclass>());
+ }
+
+ virtual ~SubclassCtor() {}
+
+ ChannelPtr construct(const ConnectionPtr &conn, const QString &objectPath,
+ const QVariantMap &immutableProperties) const
+ {
+ return Subclass::create(conn, objectPath, immutableProperties);
+ }
+ };
+#endif /* DOXYGEN_SHOULD_SKIP_THIS */
+
+ virtual QString finalBusNameFrom(const QString &uniqueOrWellKnown) const;
+ // Nothing we'd like to prepare()
+ virtual Features featuresFor(const DBusProxyPtr &proxy) const;
+
+private:
+ struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/channel-internal.h b/TelepathyQt/channel-internal.h
new file mode 100644
index 00000000..ff5641f0
--- /dev/null
+++ b/TelepathyQt/channel-internal.h
@@ -0,0 +1,50 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_channel_internal_h_HEADER_GUARD_
+#define _TelepathyQt_channel_internal_h_HEADER_GUARD_
+
+#include <TelepathyQt/Channel>
+#include <TelepathyQt/PendingOperation>
+
+namespace Tp
+{
+
+class TP_QT_NO_EXPORT Channel::PendingLeave : public PendingOperation
+{
+ Q_OBJECT
+
+public:
+ PendingLeave(const ChannelPtr &channel, const QString &message,
+ ChannelGroupChangeReason reason);
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onChanInvalidated(Tp::DBusProxy *proxy);
+ TP_QT_NO_EXPORT void onRemoveFinished(Tp::PendingOperation *);
+ TP_QT_NO_EXPORT void onMembersChanged(const Tp::Contacts &, const Tp::Contacts &, const Tp::Contacts &,
+ const Tp::Contacts &);
+ TP_QT_NO_EXPORT void onCloseFinished(Tp::PendingOperation *);
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/channel-request.cpp b/TelepathyQt/channel-request.cpp
new file mode 100644
index 00000000..5e84f57c
--- /dev/null
+++ b/TelepathyQt/channel-request.cpp
@@ -0,0 +1,803 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/ChannelRequest>
+
+#include "TelepathyQt/_gen/cli-channel-request-body.hpp"
+#include "TelepathyQt/_gen/cli-channel-request.moc.hpp"
+#include "TelepathyQt/_gen/channel-request.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/Account>
+#include <TelepathyQt/AccountFactory>
+#include <TelepathyQt/PendingFailure>
+#include <TelepathyQt/PendingReady>
+#include <TelepathyQt/PendingVoid>
+
+#include <QDateTime>
+#include <QSharedData>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT ChannelRequest::Private
+{
+ Private(ChannelRequest *parent, const QVariantMap &immutableProperties,
+ const AccountFactoryConstPtr &, const ConnectionFactoryConstPtr &,
+ const ChannelFactoryConstPtr &, const ContactFactoryConstPtr &);
+ ~Private();
+
+ static void introspectMain(Private *self);
+
+ // \param lastCall Is this the last call to extractMainProps ie. should actions that only must
+ // be done once be done in this call
+ void extractMainProps(const QVariantMap &props, bool lastCall);
+
+ // Public object
+ ChannelRequest *parent;
+
+ // Context
+ AccountFactoryConstPtr accFact;
+ ConnectionFactoryConstPtr connFact;
+ ChannelFactoryConstPtr chanFact;
+ ContactFactoryConstPtr contactFact;
+
+ // Instance of generated interface class
+ Client::ChannelRequestInterface *baseInterface;
+
+ // Mandatory properties interface proxy
+ Client::DBus::PropertiesInterface *properties;
+
+ QVariantMap immutableProperties;
+
+ ReadinessHelper *readinessHelper;
+
+ // Introspection
+ AccountPtr account;
+ QDateTime userActionTime;
+ QString preferredHandler;
+ QualifiedPropertyValueMapList requests;
+ ChannelRequestHints hints;
+ bool propertiesDone;
+
+ bool gotSWC;
+ ChannelPtr chan;
+};
+
+ChannelRequest::Private::Private(ChannelRequest *parent,
+ const QVariantMap &immutableProperties,
+ const AccountFactoryConstPtr &accFact,
+ const ConnectionFactoryConstPtr &connFact,
+ const ChannelFactoryConstPtr &chanFact,
+ const ContactFactoryConstPtr &contactFact)
+ : parent(parent),
+ accFact(accFact),
+ connFact(connFact),
+ chanFact(chanFact),
+ contactFact(contactFact),
+ baseInterface(new Client::ChannelRequestInterface(parent)),
+ properties(parent->interface<Client::DBus::PropertiesInterface>()),
+ immutableProperties(immutableProperties),
+ readinessHelper(parent->readinessHelper()),
+ propertiesDone(false),
+ gotSWC(false)
+{
+ debug() << "Creating new ChannelRequest:" << parent->objectPath();
+
+ parent->connect(baseInterface,
+ SIGNAL(Failed(QString,QString)),
+ SIGNAL(failed(QString,QString)));
+ parent->connect(baseInterface,
+ SIGNAL(Succeeded()),
+ SLOT(onLegacySucceeded()));
+ parent->connect(baseInterface,
+ SIGNAL(SucceededWithChannel(QDBusObjectPath,QVariantMap,QDBusObjectPath,QVariantMap)),
+ SLOT(onSucceededWithChannel(QDBusObjectPath,QVariantMap,QDBusObjectPath,QVariantMap)));
+
+ ReadinessHelper::Introspectables introspectables;
+
+ // As ChannelRequest does not have predefined statuses let's simulate one (0)
+ ReadinessHelper::Introspectable introspectableCore(
+ QSet<uint>() << 0, // makesSenseForStatuses
+ Features(), // dependsOnFeatures
+ QStringList(), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &Private::introspectMain,
+ this);
+ introspectables[FeatureCore] = introspectableCore;
+
+ readinessHelper->addIntrospectables(introspectables);
+
+ // For early access to the immutable properties through the friendly getters - will be called
+ // again with lastCall = true eventually, if/when becomeReady is called, though
+ QVariantMap mainProps;
+ foreach (QString key, immutableProperties.keys()) {
+ // The key.count thing is so that we don't match "org.fdo.Tp.CR.OptionalInterface.Prop" too
+ if (key.startsWith(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_REQUEST "."))
+ && key.count(QLatin1Char('.')) ==
+ QString::fromAscii(TELEPATHY_INTERFACE_CHANNEL_REQUEST ".").count(QLatin1Char('.'))) {
+ QVariant value = immutableProperties.value(key);
+ mainProps.insert(key.remove(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_REQUEST ".")), value);
+ }
+ }
+ extractMainProps(mainProps, false);
+}
+
+ChannelRequest::Private::~Private()
+{
+}
+
+void ChannelRequest::Private::introspectMain(ChannelRequest::Private *self)
+{
+ QVariantMap props;
+ QString key;
+ bool needIntrospectMainProps = false;
+ const char *propertiesNames[] = { "Account", "UserActionTime",
+ "PreferredHandler", "Requests", "Interfaces",
+ NULL };
+ for (unsigned i = 0; propertiesNames[i] != NULL; ++i) {
+ key = QLatin1String(TELEPATHY_INTERFACE_CHANNEL_REQUEST ".");
+ key += QLatin1String(propertiesNames[i]);
+ if (!self->immutableProperties.contains(key)) {
+ needIntrospectMainProps = true;
+ break;
+ }
+ props.insert(QLatin1String(propertiesNames[i]),
+ self->immutableProperties[key]);
+ }
+
+ if (needIntrospectMainProps) {
+ debug() << "Calling Properties::GetAll(ChannelRequest)";
+ QDBusPendingCallWatcher *watcher =
+ new QDBusPendingCallWatcher(
+ self->properties->GetAll(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_REQUEST)),
+ self->parent);
+ // FIXME: This is a Qt bug fixed upstream, should be in the next Qt release.
+ // We should not need to check watcher->isFinished() here, remove the
+ // check when a fixed Qt version is released.
+ if (watcher->isFinished()) {
+ self->parent->gotMainProperties(watcher);
+ } else {
+ self->parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotMainProperties(QDBusPendingCallWatcher*)));
+ }
+ } else {
+ self->extractMainProps(props, true);
+ }
+}
+
+void ChannelRequest::Private::extractMainProps(const QVariantMap &props, bool lastCall)
+{
+ PendingReady *readyOp = 0;
+
+ if (props.contains(QLatin1String("Account"))) {
+ QDBusObjectPath accountObjectPath =
+ qdbus_cast<QDBusObjectPath>(props.value(QLatin1String("Account")));
+
+ if (!account.isNull()) {
+ if (accountObjectPath.path() == account->objectPath()) {
+ // Most often a no-op, but we want this to guarantee the old behavior in all cases
+ readyOp = account->becomeReady();
+ } else {
+ warning() << "The account" << accountObjectPath.path() << "was not the expected"
+ << account->objectPath() << "for CR" << parent->objectPath();
+ // Construct a new one instead
+ account.reset();
+ }
+ }
+
+ // We need to check again because we might have dropped the expected account just a sec ago
+ if (account.isNull() && !accountObjectPath.path().isEmpty()) {
+ if (!accFact.isNull()) {
+ readyOp = accFact->proxy(
+ TP_QT_ACCOUNT_MANAGER_BUS_NAME, accountObjectPath.path(),
+ connFact, chanFact, contactFact);
+ account = AccountPtr::qObjectCast(readyOp->proxy());
+ } else {
+ account = Account::create(
+ TP_QT_ACCOUNT_MANAGER_BUS_NAME,
+ accountObjectPath.path(), connFact, chanFact, contactFact);
+ readyOp = account->becomeReady();
+ }
+ }
+ }
+
+ // FIXME See http://bugs.freedesktop.org/show_bug.cgi?id=21690
+ uint stamp = (uint) qdbus_cast<qlonglong>(props.value(QLatin1String("UserActionTime")));
+ if (stamp != 0) {
+ userActionTime = QDateTime::fromTime_t(stamp);
+ }
+
+ preferredHandler = qdbus_cast<QString>(props.value(QLatin1String("PreferredHandler")));
+ requests = qdbus_cast<QualifiedPropertyValueMapList>(props.value(QLatin1String("Requests")));
+
+ parent->setInterfaces(qdbus_cast<QStringList>(props[QLatin1String("Interfaces")]));
+ readinessHelper->setInterfaces(parent->interfaces());
+
+ if (props.contains(QLatin1String("Hints"))) {
+ hints = qdbus_cast<QVariantMap>(props.value(QLatin1String("Hints")));
+ }
+
+ if (lastCall) {
+ propertiesDone = true;
+ }
+
+ if (account) {
+ parent->connect(readyOp,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onAccountReady(Tp::PendingOperation*)));
+ } else if (lastCall) {
+ warning() << "No account for ChannelRequest" << parent->objectPath();
+ readinessHelper->setIntrospectCompleted(FeatureCore, true);
+ }
+}
+
+/**
+ * \class ChannelRequest
+ * \ingroup clientchannelrequest
+ * \headerfile TelepathyQt/channel-request.h <TelepathyQt/ChannelRequest>
+ *
+ * \brief The ChannelRequest class represents a Telepathy channel request.
+ *
+ * A channel request is an object in the channel dispatcher representing an
+ * ongoing request for some channels to be created or found. There can be any
+ * number of channel request objects at the same time.
+ *
+ * A channel request can be cancelled by any client (not just the one that
+ * requested it). This means that the channel dispatcher will close the
+ * resulting channel, or refrain from requesting it at all, rather than
+ * dispatching it to a handler.
+ *
+ * See \ref async_model
+ */
+
+/**
+ * Feature representing the core that needs to become ready to make the
+ * ChannelRequest object usable.
+ *
+ * Note that this feature must be enabled in order to use most
+ * ChannelRequest methods.
+ *
+ * When calling isReady(), becomeReady(), this feature is implicitly added
+ * to the requested features.
+ */
+const Feature ChannelRequest::FeatureCore = Feature(QLatin1String(ChannelRequest::staticMetaObject.className()), 0, true);
+
+/**
+ * Create a new channel request object using the given \a bus and the given factories.
+ *
+ * \param objectPath The channel request object path.
+ * \param immutableProperties The channel request immutable properties.
+ * \param accountFactory The account factory to use.
+ * \param connectionFactory The connection factory to use.
+ * \param channelFactory The channel factory to use.
+ * \param contactFactory The contact factory to use.
+ * \return A ChannelRequestPtr object pointing to the newly created ChannelRequest object.
+ */
+ChannelRequestPtr ChannelRequest::create(const QDBusConnection &bus, const QString &objectPath,
+ const QVariantMap &immutableProperties,
+ const AccountFactoryConstPtr &accountFactory,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory)
+{
+ return ChannelRequestPtr(new ChannelRequest(bus, objectPath, immutableProperties,
+ accountFactory, connectionFactory, channelFactory, contactFactory));
+}
+
+/**
+ * Create a new channel request object for the given \a account.
+ *
+ * The returned instance will use factories from the account.
+ *
+ * \param account The account that the request was made through.
+ * \param objectPath The channel request object path.
+ * \param immutableProperties The channel request immutable properties.
+ * \return A ChannelRequestPtr object pointing to the newly created ChannelRequest object.
+ */
+ChannelRequestPtr ChannelRequest::create(const AccountPtr &account, const QString &objectPath,
+ const QVariantMap &immutableProperties)
+{
+ return ChannelRequestPtr(new ChannelRequest(account, objectPath, immutableProperties));
+}
+
+/**
+ * Construct a new channel request object using the given \a bus and the given factories.
+ *
+ * \param bus QDBusConnection to use.
+ * \param objectPath The channel request object path.
+ * \param accountFactory The account factory to use.
+ * \param connectionFactory The connection factory to use.
+ * \param channelFactory The channel factory to use.
+ * \param contactFactory The contact factory to use.
+ * \param immutableProperties The immutable properties of the channel request.
+ * \return A ChannelRequestPtr object pointing to the newly created ChannelRequest.
+ */
+ChannelRequest::ChannelRequest(const QDBusConnection &bus,
+ const QString &objectPath, const QVariantMap &immutableProperties,
+ const AccountFactoryConstPtr &accountFactory,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory)
+ : StatefulDBusProxy(bus,
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL_DISPATCHER),
+ objectPath, FeatureCore),
+ OptionalInterfaceFactory<ChannelRequest>(this),
+ mPriv(new Private(this, immutableProperties, accountFactory, connectionFactory,
+ channelFactory, contactFactory))
+{
+ if (accountFactory->dbusConnection().name() != bus.name()) {
+ warning() << " The D-Bus connection in the account factory is not the proxy connection";
+ }
+
+ if (connectionFactory->dbusConnection().name() != bus.name()) {
+ warning() << " The D-Bus connection in the connection factory is not the proxy connection";
+ }
+
+ if (channelFactory->dbusConnection().name() != bus.name()) {
+ warning() << " The D-Bus connection in the channel factory is not the proxy connection";
+ }
+}
+
+/**
+ * Construct a new channel request object using the given \a account.
+ *
+ * The constructed instance will use the factories from the account.
+ *
+ * \param account Account to use.
+ * \param objectPath The channel request object path.
+ * \param immutableProperties The immutable properties of the channel request.
+ * \return A ChannelRequestPtr object pointing to the newly created ChannelRequest.
+ */
+ChannelRequest::ChannelRequest(const AccountPtr &account,
+ const QString &objectPath, const QVariantMap &immutableProperties)
+ : StatefulDBusProxy(account->dbusConnection(),
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL_DISPATCHER),
+ objectPath, FeatureCore),
+ OptionalInterfaceFactory<ChannelRequest>(this),
+ mPriv(new Private(this, immutableProperties, AccountFactoryPtr(),
+ account->connectionFactory(),
+ account->channelFactory(),
+ account->contactFactory()))
+{
+ mPriv->account = account;
+}
+
+/**
+ * Class destructor.
+ */
+ChannelRequest::~ChannelRequest()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the account on which this request was made.
+ *
+ * This method can be used even before the ChannelRequest is ready, in which case the account object
+ * corresponding to the immutable properties is returned. In this case, the Account object is not
+ * necessarily ready either. This is useful for eg. matching ChannelRequests from
+ * ClientHandlerInterface::addRequest() with existing accounts in the application: either by object
+ * path, or if account factories are in use, even by object identity.
+ *
+ * If the account is not provided in the immutable properties, this will only return a non-\c NULL
+ * AccountPtr once ChannelRequest::FeatureCore is ready on this object.
+ *
+ * \return A pointer to the Account object.
+ */
+AccountPtr ChannelRequest::account() const
+{
+ return mPriv->account;
+}
+
+/**
+ * Return the time at which the user action occurred, or 0 if this channel
+ * request is for some reason not involving user action.
+ *
+ * Unix developers: this corresponds to the _NET_WM_USER_TIME property in EWMH.
+ *
+ * This property is set when the channel request is created, and can never
+ * change.
+ *
+ * This method can be used even before the ChannelRequest is ready: in this case, the user action
+ * time from the immutable properties, if any, is returned.
+ *
+ * \return The time at which the user action occurred as QDateTime.
+ */
+QDateTime ChannelRequest::userActionTime() const
+{
+ return mPriv->userActionTime;
+}
+
+/**
+ * Return either the well-known bus name (starting with
+ * org.freedesktop.Telepathy.Client.) of the preferred handler for this channel,
+ * or an empty string to indicate that any handler would be acceptable.
+ *
+ * This property is set when the channel request is created, and can never
+ * change.
+ *
+ * This method can be used even before the ChannelRequest is ready: in this case, the preferred
+ * handler from the immutable properties, if any, is returned.
+ *
+ * \return The preferred handler, or an empty string if any handler would be
+ * acceptable.
+ */
+QString ChannelRequest::preferredHandler() const
+{
+ return mPriv->preferredHandler;
+}
+
+/**
+ * Return the desirable properties for the channel or channels to be created, as specified when
+ * placing the request in the first place.
+ *
+ * This property is set when the channel request is created, and can never
+ * change.
+ *
+ * This method can be used even before the ChannelRequest is ready: in this case, the requested
+ * channel properties from the immutable properties, if any, are returned. This is useful for e.g.
+ * matching ChannelRequests from ClientHandlerInterface::addRequest() with existing requests in the
+ * application (by the target ID or handle, most likely).
+ *
+ * \return The requested desirable channel properties as a list of
+ * QualifiedPropertyValueMap objects.
+ */
+QualifiedPropertyValueMapList ChannelRequest::requests() const
+{
+ return mPriv->requests;
+}
+
+/**
+ * Return the dictionary of metadata provided by the channel requester when requesting the channel.
+ *
+ * This property is set when the channel request is created, and can never change.
+ *
+ * This method can be used even before the ChannelRequest is ready: in this case, the requested
+ * channel properties from the immutable properties, if any, are returned. This is useful for e.g.
+ * matching ChannelRequests from ClientHandlerInterface::addRequest() with existing requests in the
+ * application (by the target ID or handle, most likely).
+ *
+ * \sa Account::supportsRequestHints()
+ * \return The hints in the request as a ChannelRequestHints object, if any.
+ */
+ChannelRequestHints ChannelRequest::hints() const
+{
+ return mPriv->hints;
+}
+
+/**
+ * Return all of the immutable properties passed to this object when created.
+ *
+ * This is useful for e.g. getting to domain-specific properties of channel requests.
+ *
+ * \return The immutable properties as QVariantMap.
+ */
+QVariantMap ChannelRequest::immutableProperties() const
+{
+ QVariantMap props = mPriv->immutableProperties;
+
+ if (!account().isNull()) {
+ props.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_REQUEST ".Account"),
+ QVariant::fromValue(QDBusObjectPath(account()->objectPath())));
+ }
+
+ if (userActionTime().isValid()) {
+ props.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_REQUEST ".UserActionTime"),
+ QVariant::fromValue(userActionTime().toTime_t()));
+ }
+
+ if (!preferredHandler().isNull()) {
+ props.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_REQUEST ".PreferredHandler"),
+ preferredHandler());
+ }
+
+ if (!requests().isEmpty()) {
+ props.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_REQUEST ".Requests"),
+ QVariant::fromValue(requests()));
+ }
+
+ props.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_REQUEST ".Interfaces"),
+ QVariant::fromValue(interfaces()));
+
+ return props;
+}
+
+/**
+ * Cancel the channel request.
+ *
+ * If failed() is emitted in response to this method, the error will be
+ * #TP_QT_ERROR_CANCELLED.
+ *
+ * If the channel has already been dispatched to a handler, then it's too late
+ * to call this method, and the channel request will no longer exist.
+ *
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the call has finished.
+ */
+PendingOperation *ChannelRequest::cancel()
+{
+ return new PendingVoid(mPriv->baseInterface->Cancel(),
+ ChannelRequestPtr(this));
+}
+
+/**
+ * Return the Channel which this request succeeded with, if any.
+ *
+ * This will only ever be populated if Account::requestsSucceedWithChannel() is \c true, and
+ * succeeded() has already been emitted on this ChannelRequest. Note that a PendingChannelRequest
+ * being successfully finished already implies succeeded() has been emitted.
+ *
+ * \return A pointer to the Channel object, or a null ChannelPtr if there isn't any.
+ */
+ChannelPtr ChannelRequest::channel() const
+{
+ return mPriv->chan;
+}
+
+/**
+ * Proceed with the channel request.
+ *
+ * The client that created this object calls this method when it has connected
+ * signal handlers for succeeded() and failed(). Note that this is done
+ * automatically when using PendingChannelRequest.
+ *
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the call has finished.
+ */
+PendingOperation *ChannelRequest::proceed()
+{
+ return new PendingVoid(mPriv->baseInterface->Proceed(),
+ ChannelRequestPtr(this));
+}
+
+/**
+ * Return the ChannelRequestInterface for this ChannelRequest class. This method is
+ * protected since the convenience methods provided by this class should
+ * always be used instead of the interface by users of the class.
+ *
+ * \return A pointer to the existing Client::ChannelRequestInterface object for this
+ * ChannelRequest object.
+ */
+Client::ChannelRequestInterface *ChannelRequest::baseInterface() const
+{
+ return mPriv->baseInterface;
+}
+
+void ChannelRequest::gotMainProperties(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QVariantMap> reply = *watcher;
+ QVariantMap props;
+
+ if (!reply.isError()) {
+ debug() << "Got reply to Properties::GetAll(ChannelRequest)";
+ props = reply.value();
+
+ mPriv->extractMainProps(props, true);
+ } else {
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureCore,
+ false, reply.error());
+ warning().nospace() << "Properties::GetAll(ChannelRequest) failed with "
+ << reply.error().name() << ": " << reply.error().message();
+ }
+
+ watcher->deleteLater();
+}
+
+void ChannelRequest::onAccountReady(PendingOperation *op)
+{
+ if (op->isError()) {
+ warning() << "Unable to make ChannelRequest.Account ready";
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureCore, false,
+ op->errorName(), op->errorMessage());
+ return;
+ }
+
+ if (mPriv->propertiesDone && !isReady()) {
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureCore, true);
+ }
+}
+
+void ChannelRequest::onLegacySucceeded()
+{
+ if (mPriv->gotSWC) {
+ return;
+ }
+
+ emit succeeded(); // API/ABI break TODO: don't
+ emit succeeded(ChannelPtr());
+}
+
+void ChannelRequest::onSucceededWithChannel(
+ const QDBusObjectPath &connPath,
+ const QVariantMap &connProps,
+ const QDBusObjectPath &chanPath,
+ const QVariantMap &chanProps)
+{
+ if (mPriv->gotSWC) {
+ warning().nospace() << "Got SucceededWithChannel again for CR(" << objectPath() << ")!";
+ return;
+ }
+
+ mPriv->gotSWC = true;
+
+ QList<PendingOperation *> readyOps;
+
+ QString connBusName = connPath.path().mid(1).replace(
+ QLatin1String("/"), QLatin1String("."));
+ PendingReady *connReady = mPriv->connFact->proxy(connBusName, connPath.path(),
+ mPriv->chanFact, mPriv->contactFact);
+ ConnectionPtr conn = ConnectionPtr::qObjectCast(connReady->proxy());
+ readyOps.append(connReady);
+
+ PendingReady *chanReady = mPriv->chanFact->proxy(conn, chanPath.path(), chanProps);
+ mPriv->chan = ChannelPtr::qObjectCast(chanReady->proxy());
+ readyOps.append(chanReady);
+
+ connect(new PendingComposite(readyOps, ChannelRequestPtr(this)),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onChanBuilt(Tp::PendingOperation*)));
+}
+
+void ChannelRequest::onChanBuilt(Tp::PendingOperation *op)
+{
+ if (op->isError()) {
+ warning() << "Failed to build Channel which the ChannelRequest succeeded with,"
+ << "succeeding with NULL channel:" << op->errorName() << ',' << op->errorMessage();
+ mPriv->chan.reset();
+ }
+
+ emit succeeded(); // API/ABI break TODO: don't
+ emit succeeded(mPriv->chan);
+}
+
+/**
+ * \fn void ChannelRequest::failed(const QString &errorName,
+ * const QString &errorMessage)
+ *
+ * Emitted when the channel request has failed. No further
+ * methods must not be called on it.
+ *
+ * \param errorName The name of a D-Bus error.
+ * \param errorMessage The error message.
+ * \sa succeeded()
+ */
+
+/**
+ * \fn void ChannelRequest::succeeded()
+ *
+ * \deprecated Use ChannelRequest::succeeded(const ChannelPtr &) instead.
+ */
+
+/**
+ * \fn void ChannelRequest::succeeded(const Tp::ChannelPtr &channel)
+ *
+ * Emitted when the channel request has succeeded. No further
+ * methods must not be called on it.
+ *
+ * The \a channel parameter can be used to observe the channel resulting from the request (e.g. for
+ * it getting closed). The pointer may be NULL if the Channel Dispatcher implementation is too old.
+ * Whether a non-NULL channel can be expected can be checked with
+ * Account::requestsSucceedWithChannel().
+ *
+ * If there is a channel, it will be of the subclass determined by and made ready (or not) according
+ * to the settings of the ChannelFactory on the Account the request was made through.
+ *
+ * \param channel Pointer to a proxy for the resulting channel, if the Channel Dispatcher reported it.
+ * \sa failed()
+ */
+
+void ChannelRequest::connectNotify(const char *signalName)
+{
+ if (qstrcmp(signalName, SIGNAL(succeeded())) == 0) {
+ warning() << "Connecting to deprecated signal ChannelRequest::succeeded()";
+ }
+}
+
+/**
+ * \class ChannelRequestHints
+ * \ingroup clientchannelrequest
+ * \headerfile TelepathyQt/channel-request.h <TelepathyQt/ChannelRequestHints>
+ *
+ * \brief The ChannelRequestHints class represents a dictionary of metadata
+ * provided by the channel requester when requesting a channel.
+ */
+
+struct TP_QT_NO_EXPORT ChannelRequestHints::Private : public QSharedData
+{
+ Private() {}
+ Private(const QVariantMap &hints) : hints(hints) {}
+
+ QVariantMap hints;
+};
+
+ChannelRequestHints::ChannelRequestHints()
+{
+}
+
+ChannelRequestHints::ChannelRequestHints(const QVariantMap &hints)
+ : mPriv(new Private(hints))
+{
+}
+
+ChannelRequestHints::ChannelRequestHints(const ChannelRequestHints &crh)
+ : mPriv(crh.mPriv)
+{
+}
+
+ChannelRequestHints::~ChannelRequestHints()
+{
+}
+
+ChannelRequestHints &ChannelRequestHints::operator=(const ChannelRequestHints &other)
+{
+ if (this == &other) {
+ return *this;
+ }
+
+ this->mPriv = other.mPriv;
+ return *this;
+}
+
+bool ChannelRequestHints::isValid() const
+{
+ return mPriv.constData() != 0;
+}
+
+bool ChannelRequestHints::hasHint(const QString &reversedDomain, const QString &localName) const
+{
+ if (!isValid()) {
+ return false;
+ }
+
+ const QString qualifiedName = reversedDomain + QLatin1Char('.') + localName;
+ return mPriv->hints.contains(qualifiedName);
+}
+
+QVariant ChannelRequestHints::hint(const QString &reversedDomain, const QString &localName) const
+{
+ if (!isValid()) {
+ return QVariant();
+ }
+
+ const QString qualifiedName = reversedDomain + QLatin1Char('.') + localName;
+ return mPriv->hints.value(qualifiedName);
+}
+
+void ChannelRequestHints::setHint(const QString &reversedDomain, const QString &localName, const QVariant &value)
+{
+ const QString qualifiedName = reversedDomain + QLatin1Char('.') + localName;
+
+ if (!isValid()) {
+ mPriv = new Private();
+ }
+
+ mPriv->hints.insert(qualifiedName, value);
+}
+
+QVariantMap ChannelRequestHints::allHints() const
+{
+ return isValid() ? mPriv->hints : QVariantMap();
+}
+
+} // Tp
diff --git a/TelepathyQt/channel-request.h b/TelepathyQt/channel-request.h
new file mode 100644
index 00000000..5e42154d
--- /dev/null
+++ b/TelepathyQt/channel-request.h
@@ -0,0 +1,154 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_channel_request_h_HEADER_GUARD_
+#define _TelepathyQt_channel_request_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/_gen/cli-channel-request.h>
+
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/DBus>
+#include <TelepathyQt/DBusProxy>
+#include <TelepathyQt/Feature>
+#include <TelepathyQt/OptionalInterfaceFactory>
+#include <TelepathyQt/ReadinessHelper>
+#include <TelepathyQt/Types>
+#include <TelepathyQt/SharedPtr>
+
+#include <QSharedDataPointer>
+#include <QString>
+#include <QStringList>
+#include <QVariantMap>
+
+namespace Tp
+{
+
+class ChannelRequestHints;
+class PendingOperation;
+
+class TP_QT_EXPORT ChannelRequest : public StatefulDBusProxy,
+ public OptionalInterfaceFactory<ChannelRequest>
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(ChannelRequest)
+
+public:
+ static const Feature FeatureCore;
+
+ static ChannelRequestPtr create(const QDBusConnection &bus,
+ const QString &objectPath, const QVariantMap &immutableProperties,
+ const AccountFactoryConstPtr &accountFactory,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory);
+
+ static ChannelRequestPtr create(const AccountPtr &account,
+ const QString &objectPath, const QVariantMap &immutableProperties);
+
+ virtual ~ChannelRequest();
+
+ AccountPtr account() const;
+ QDateTime userActionTime() const;
+ QString preferredHandler() const;
+ QualifiedPropertyValueMapList requests() const;
+ ChannelRequestHints hints() const;
+
+ QVariantMap immutableProperties() const;
+
+ PendingOperation *cancel();
+
+ ChannelPtr channel() const;
+
+Q_SIGNALS:
+ void failed(const QString &errorName, const QString &errorMessage);
+ void succeeded(); // TODO API/ABI break: remove
+ void succeeded(const Tp::ChannelPtr &channel);
+
+protected:
+ ChannelRequest(const QDBusConnection &bus,
+ const QString &objectPath, const QVariantMap &immutableProperties,
+ const AccountFactoryConstPtr &accountFactory,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory);
+
+ ChannelRequest(const AccountPtr &account,
+ const QString &objectPath, const QVariantMap &immutableProperties);
+
+ Client::ChannelRequestInterface *baseInterface() const;
+
+protected:
+ // TODO: (API/ABI break) Remove connectNotify
+ void connectNotify(const char *);
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void gotMainProperties(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void onAccountReady(Tp::PendingOperation *op);
+
+ TP_QT_NO_EXPORT void onLegacySucceeded();
+ TP_QT_NO_EXPORT void onSucceededWithChannel(const QDBusObjectPath &connPath, const QVariantMap &connProps,
+ const QDBusObjectPath &chanPath, const QVariantMap &chanProps);
+ TP_QT_NO_EXPORT void onChanBuilt(Tp::PendingOperation *op);
+
+private:
+ friend class PendingChannelRequest;
+
+ PendingOperation *proceed();
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+class TP_QT_EXPORT ChannelRequestHints
+{
+public:
+ ChannelRequestHints();
+ ChannelRequestHints(const QVariantMap &hints);
+ ChannelRequestHints(const ChannelRequestHints &other);
+ ~ChannelRequestHints();
+
+ ChannelRequestHints &operator=(const ChannelRequestHints &other);
+
+ bool isValid() const;
+
+ bool hasHint(const QString &reversedDomain, const QString &localName) const;
+ QVariant hint(const QString &reversedDomain, const QString &localName) const;
+ void setHint(const QString &reversedDomain, const QString &localName, const QVariant &value);
+
+ QVariantMap allHints() const;
+
+private:
+ struct Private;
+ friend struct Private;
+ QSharedDataPointer<Private> mPriv;
+};
+
+} // Tp
+
+Q_DECLARE_METATYPE(Tp::ChannelRequestHints);
+
+#endif
diff --git a/TelepathyQt/channel-request.xml b/TelepathyQt/channel-request.xml
new file mode 100644
index 00000000..92fc8817
--- /dev/null
+++ b/TelepathyQt/channel-request.xml
@@ -0,0 +1,9 @@
+<tp:spec
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<tp:title>Channel Request interface</tp:title>
+
+<xi:include href="../spec/Channel_Request.xml"/>
+
+</tp:spec>
diff --git a/TelepathyQt/channel.cpp b/TelepathyQt/channel.cpp
new file mode 100644
index 00000000..7a6872ae
--- /dev/null
+++ b/TelepathyQt/channel.cpp
@@ -0,0 +1,3596 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/Channel>
+#include "TelepathyQt/channel-internal.h"
+
+#include "TelepathyQt/_gen/cli-channel-body.hpp"
+#include "TelepathyQt/_gen/cli-channel.moc.hpp"
+#include "TelepathyQt/_gen/channel.moc.hpp"
+#include "TelepathyQt/_gen/channel-internal.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include "TelepathyQt/future-internal.h"
+
+#include <TelepathyQt/ChannelFactory>
+#include <TelepathyQt/Connection>
+#include <TelepathyQt/ConnectionCapabilities>
+#include <TelepathyQt/ConnectionLowlevel>
+#include <TelepathyQt/ContactManager>
+#include <TelepathyQt/PendingContacts>
+#include <TelepathyQt/PendingFailure>
+#include <TelepathyQt/PendingOperation>
+#include <TelepathyQt/PendingReady>
+#include <TelepathyQt/PendingSuccess>
+#include <TelepathyQt/StreamTubeChannel>
+#include <TelepathyQt/ReferencedHandles>
+#include <TelepathyQt/Constants>
+
+#include <QHash>
+#include <QQueue>
+#include <QSharedData>
+#include <QTimer>
+
+namespace Tp
+{
+
+using TpFuture::Client::ChannelInterfaceMergeableConferenceInterface;
+using TpFuture::Client::ChannelInterfaceSplittableInterface;
+
+struct TP_QT_NO_EXPORT Channel::Private
+{
+ Private(Channel *parent, const ConnectionPtr &connection,
+ const QVariantMap &immutableProperties);
+ ~Private();
+
+ static void introspectMain(Private *self);
+ void introspectMainProperties();
+ void introspectMainFallbackChannelType();
+ void introspectMainFallbackHandle();
+ void introspectMainFallbackInterfaces();
+ void introspectGroup();
+ void introspectGroupFallbackFlags();
+ void introspectGroupFallbackMembers();
+ void introspectGroupFallbackLocalPendingWithInfo();
+ void introspectGroupFallbackSelfHandle();
+ void introspectConference();
+
+ static void introspectConferenceInitialInviteeContacts(Private *self);
+
+ void continueIntrospection();
+
+ void extractMainProps(const QVariantMap &props);
+ void extract0176GroupProps(const QVariantMap &props);
+
+ void nowHaveInterfaces();
+ void nowHaveInitialMembers();
+
+ bool setGroupFlags(uint groupFlags);
+
+ void buildContacts();
+ void doMembersChangedDetailed(const UIntList &, const UIntList &, const UIntList &,
+ const UIntList &, const QVariantMap &);
+ void processMembersChanged();
+ void updateContacts(const QList<ContactPtr> &contacts =
+ QList<ContactPtr>());
+ bool fakeGroupInterfaceIfNeeded();
+ void setReady();
+
+ QString groupMemberChangeDetailsTelepathyError(
+ const GroupMemberChangeDetails &details);
+
+ inline ChannelInterfaceMergeableConferenceInterface *mergeableConferenceInterface(
+ InterfaceSupportedChecking check = CheckInterfaceSupported) const
+ {
+ return parent->optionalInterface<ChannelInterfaceMergeableConferenceInterface>(check);
+ }
+
+ inline ChannelInterfaceSplittableInterface *splittableInterface(
+ InterfaceSupportedChecking check = CheckInterfaceSupported) const
+ {
+ return parent->optionalInterface<ChannelInterfaceSplittableInterface>(check);
+ }
+
+ void processConferenceChannelRemoved();
+
+ struct GroupMembersChangedInfo;
+ struct ConferenceChannelRemovedInfo;
+
+ // Public object
+ Channel *parent;
+
+ // Instance of generated interface class
+ Client::ChannelInterface *baseInterface;
+
+ // Mandatory properties interface proxy
+ Client::DBus::PropertiesInterface *properties;
+
+ // Owning connection - it can be a SharedPtr as Connection does not cache
+ // channels
+ ConnectionPtr connection;
+
+ QVariantMap immutableProperties;
+
+ // Optional interface proxies
+ Client::ChannelInterfaceGroupInterface *group;
+ Client::ChannelInterfaceConferenceInterface *conference;
+
+ ReadinessHelper *readinessHelper;
+
+ // Introspection
+ QQueue<void (Private::*)()> introspectQueue;
+
+ // Introspected properties
+
+ // Main interface
+ QString channelType;
+ uint targetHandleType;
+ uint targetHandle;
+ QString targetId;
+ ContactPtr targetContact;
+ bool requested;
+ uint initiatorHandle;
+ ContactPtr initiatorContact;
+
+ // Group flags
+ uint groupFlags;
+ bool usingMembersChangedDetailed;
+
+ // Group member introspection
+ bool groupHaveMembers;
+ bool buildingContacts;
+
+ // Queue of received MCD signals to process
+ QQueue<GroupMembersChangedInfo *> groupMembersChangedQueue;
+ GroupMembersChangedInfo *currentGroupMembersChangedInfo;
+
+ // Pending from the MCD signal currently processed, but contacts not yet built
+ QSet<uint> pendingGroupMembers;
+ QSet<uint> pendingGroupLocalPendingMembers;
+ QSet<uint> pendingGroupRemotePendingMembers;
+ UIntList groupMembersToRemove;
+ UIntList groupLocalPendingMembersToRemove;
+ UIntList groupRemotePendingMembersToRemove;
+
+ // Initial members
+ UIntList groupInitialMembers;
+ LocalPendingInfoList groupInitialLP;
+ UIntList groupInitialRP;
+
+ // Current members
+ QHash<uint, ContactPtr> groupContacts;
+ QHash<uint, ContactPtr> groupLocalPendingContacts;
+ QHash<uint, ContactPtr> groupRemotePendingContacts;
+
+ // Stored change info
+ QHash<uint, GroupMemberChangeDetails> groupLocalPendingContactsChangeInfo;
+ GroupMemberChangeDetails groupSelfContactRemoveInfo;
+
+ // Group handle owners
+ bool groupAreHandleOwnersAvailable;
+ HandleOwnerMap groupHandleOwners;
+
+ // Group self identity
+ bool pendingRetrieveGroupSelfContact;
+ bool groupIsSelfHandleTracked;
+ uint groupSelfHandle;
+ ContactPtr groupSelfContact;
+
+ // Conference
+ bool introspectingConference;
+ QHash<QString, ChannelPtr> conferenceChannels;
+ QHash<QString, ChannelPtr> conferenceInitialChannels;
+ QString conferenceInvitationMessage;
+ QHash<uint, ChannelPtr> conferenceOriginalChannels;
+ UIntList conferenceInitialInviteeHandles;
+ Contacts conferenceInitialInviteeContacts;
+ QQueue<ConferenceChannelRemovedInfo *> conferenceChannelRemovedQueue;
+ bool buildingConferenceChannelRemovedActorContact;
+
+ static const QString keyActor;
+};
+
+struct TP_QT_NO_EXPORT Channel::Private::GroupMembersChangedInfo
+{
+ GroupMembersChangedInfo(const UIntList &added, const UIntList &removed,
+ const UIntList &localPending, const UIntList &remotePending,
+ const QVariantMap &details)
+ : added(added),
+ removed(removed),
+ localPending(localPending),
+ remotePending(remotePending),
+ details(details),
+ // TODO most of these probably should be removed once the rest of the code using them is sanitized
+ actor(qdbus_cast<uint>(details.value(keyActor))),
+ reason(qdbus_cast<uint>(details.value(keyChangeReason))),
+ message(qdbus_cast<QString>(details.value(keyMessage)))
+ {
+ }
+
+ UIntList added;
+ UIntList removed;
+ UIntList localPending;
+ UIntList remotePending;
+ QVariantMap details;
+ uint actor;
+ uint reason;
+ QString message;
+
+ static const QString keyChangeReason;
+ static const QString keyMessage;
+ static const QString keyContactIds;
+};
+
+struct TP_QT_NO_EXPORT Channel::Private::ConferenceChannelRemovedInfo
+{
+ ConferenceChannelRemovedInfo(const QDBusObjectPath &channelPath, const QVariantMap &details)
+ : channelPath(channelPath),
+ details(details)
+ {
+ }
+
+ QDBusObjectPath channelPath;
+ QVariantMap details;
+};
+
+const QString Channel::Private::keyActor(QLatin1String("actor"));
+const QString Channel::Private::GroupMembersChangedInfo::keyChangeReason(
+ QLatin1String("change-reason"));
+const QString Channel::Private::GroupMembersChangedInfo::keyMessage(QLatin1String("message"));
+const QString Channel::Private::GroupMembersChangedInfo::keyContactIds(QLatin1String("contact-ids"));
+
+Channel::Private::Private(Channel *parent, const ConnectionPtr &connection,
+ const QVariantMap &immutableProperties)
+ : parent(parent),
+ baseInterface(new Client::ChannelInterface(parent)),
+ properties(parent->interface<Client::DBus::PropertiesInterface>()),
+ connection(connection),
+ immutableProperties(immutableProperties),
+ group(0),
+ conference(0),
+ readinessHelper(parent->readinessHelper()),
+ targetHandleType(0),
+ targetHandle(0),
+ requested(false),
+ initiatorHandle(0),
+ groupFlags(0),
+ usingMembersChangedDetailed(false),
+ groupHaveMembers(false),
+ buildingContacts(false),
+ currentGroupMembersChangedInfo(0),
+ groupAreHandleOwnersAvailable(false),
+ pendingRetrieveGroupSelfContact(false),
+ groupIsSelfHandleTracked(false),
+ groupSelfHandle(0),
+ introspectingConference(false),
+ buildingConferenceChannelRemovedActorContact(false)
+{
+ debug() << "Creating new Channel:" << parent->objectPath();
+
+ if (connection->isValid()) {
+ debug() << " Connecting to Channel::Closed() signal";
+ parent->connect(baseInterface,
+ SIGNAL(Closed()),
+ SLOT(onClosed()));
+
+ debug() << " Connection to owning connection's lifetime signals";
+ parent->connect(connection.data(),
+ SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
+ SLOT(onConnectionInvalidated()));
+ }
+ else {
+ warning() << "Connection given as the owner for a Channel was "
+ "invalid! Channel will be stillborn.";
+ parent->invalidate(QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT),
+ QLatin1String("Connection given as the owner of this channel was invalid"));
+ }
+
+ ReadinessHelper::Introspectables introspectables;
+
+ // As Channel does not have predefined statuses let's simulate one (0)
+ ReadinessHelper::Introspectable introspectableCore(
+ QSet<uint>() << 0, // makesSenseForStatuses
+ Features(), // dependsOnFeatures
+ QStringList(), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &Private::introspectMain,
+ this);
+ introspectables[FeatureCore] = introspectableCore;
+
+ // As Channel does not have predefined statuses let's simulate one (0)
+ ReadinessHelper::Introspectable introspectableConferenceInitialInviteeContacts(
+ QSet<uint>() << 0, // makesSenseForStatuses
+ Features() << FeatureCore, // dependsOnFeatures
+ QStringList() << TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE, // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &Private::introspectConferenceInitialInviteeContacts,
+ this);
+ introspectables[FeatureConferenceInitialInviteeContacts] =
+ introspectableConferenceInitialInviteeContacts;
+
+ readinessHelper->addIntrospectables(introspectables);
+}
+
+Channel::Private::~Private()
+{
+ delete currentGroupMembersChangedInfo;
+ foreach (GroupMembersChangedInfo *info, groupMembersChangedQueue) {
+ delete info;
+ }
+ foreach (ConferenceChannelRemovedInfo *info, conferenceChannelRemovedQueue) {
+ delete info;
+ }
+}
+
+void Channel::Private::introspectMain(Channel::Private *self)
+{
+ // Make sure connection object is ready, as we need to use some methods that
+ // are only available after connection object gets ready.
+ debug() << "Calling Connection::becomeReady()";
+ self->parent->connect(self->connection->becomeReady(),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onConnectionReady(Tp::PendingOperation*)));
+}
+
+void Channel::Private::introspectMainProperties()
+{
+ QVariantMap props;
+ QString key;
+ bool needIntrospectMainProps = false;
+ const unsigned numNames = 8;
+ const static QString names[numNames] = {
+ QLatin1String("ChannelType"),
+ QLatin1String("Interfaces"),
+ QLatin1String("TargetHandleType"),
+ QLatin1String("TargetHandle"),
+ QLatin1String("TargetID"),
+ QLatin1String("Requested"),
+ QLatin1String("InitiatorHandle"),
+ QLatin1String("InitiatorID")
+ };
+ const static QString qualifiedNames[numNames] = {
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType"),
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".Interfaces"),
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType"),
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandle"),
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetID"),
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".Requested"),
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".InitiatorHandle"),
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".InitiatorID")
+ };
+ for (unsigned i = 0; i < numNames; ++i) {
+ const QString &qualified = qualifiedNames[i];
+ if (!immutableProperties.contains(qualified)) {
+ needIntrospectMainProps = true;
+ break;
+ }
+ props.insert(names[i], immutableProperties.value(qualified));
+ }
+
+ // Save Requested and InitiatorHandle here, so even if the GetAll return doesn't have them but
+ // the given immutable props do (eg. due to the PendingChannel fallback guesses) we use them
+ requested = qdbus_cast<bool>(props[QLatin1String("Requested")]);
+ initiatorHandle = qdbus_cast<uint>(props[QLatin1String("InitiatorHandle")]);
+
+ if (props.contains(QLatin1String("InitiatorID"))) {
+ QString initiatorId = qdbus_cast<QString>(props[QLatin1String("InitiatorID")]);
+ connection->lowlevel()->injectContactId(initiatorHandle, initiatorId);
+ }
+
+ if (needIntrospectMainProps) {
+ debug() << "Calling Properties::GetAll(Channel)";
+ QDBusPendingCallWatcher *watcher =
+ new QDBusPendingCallWatcher(
+ properties->GetAll(QLatin1String(TELEPATHY_INTERFACE_CHANNEL)),
+ parent);
+ parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotMainProperties(QDBusPendingCallWatcher*)));
+ } else {
+ extractMainProps(props);
+ continueIntrospection();
+ }
+}
+
+void Channel::Private::introspectMainFallbackChannelType()
+{
+ debug() << "Calling Channel::GetChannelType()";
+ QDBusPendingCallWatcher *watcher =
+ new QDBusPendingCallWatcher(baseInterface->GetChannelType(), parent);
+ parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotChannelType(QDBusPendingCallWatcher*)));
+}
+
+void Channel::Private::introspectMainFallbackHandle()
+{
+ debug() << "Calling Channel::GetHandle()";
+ QDBusPendingCallWatcher *watcher =
+ new QDBusPendingCallWatcher(baseInterface->GetHandle(), parent);
+ parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotHandle(QDBusPendingCallWatcher*)));
+}
+
+void Channel::Private::introspectMainFallbackInterfaces()
+{
+ debug() << "Calling Channel::GetInterfaces()";
+ QDBusPendingCallWatcher *watcher =
+ new QDBusPendingCallWatcher(baseInterface->GetInterfaces(), parent);
+ parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotInterfaces(QDBusPendingCallWatcher*)));
+}
+
+void Channel::Private::introspectGroup()
+{
+ Q_ASSERT(properties != 0);
+
+ if (!group) {
+ group = parent->interface<Client::ChannelInterfaceGroupInterface>();
+ Q_ASSERT(group != 0);
+ }
+
+ debug() << "Introspecting Channel.Interface.Group for" << parent->objectPath();
+
+ parent->connect(group,
+ SIGNAL(GroupFlagsChanged(uint,uint)),
+ SLOT(onGroupFlagsChanged(uint,uint)));
+
+ parent->connect(group,
+ SIGNAL(MembersChanged(QString,Tp::UIntList,
+ Tp::UIntList,Tp::UIntList,
+ Tp::UIntList,uint,uint)),
+ SLOT(onMembersChanged(QString,Tp::UIntList,
+ Tp::UIntList,Tp::UIntList,
+ Tp::UIntList,uint,uint)));
+ parent->connect(group,
+ SIGNAL(MembersChangedDetailed(Tp::UIntList,
+ Tp::UIntList,Tp::UIntList,
+ Tp::UIntList,QVariantMap)),
+ SLOT(onMembersChangedDetailed(Tp::UIntList,
+ Tp::UIntList,Tp::UIntList,
+ Tp::UIntList,QVariantMap)));
+
+ parent->connect(group,
+ SIGNAL(HandleOwnersChanged(Tp::HandleOwnerMap,
+ Tp::UIntList)),
+ SLOT(onHandleOwnersChanged(Tp::HandleOwnerMap,
+ Tp::UIntList)));
+
+ parent->connect(group,
+ SIGNAL(SelfHandleChanged(uint)),
+ SLOT(onSelfHandleChanged(uint)));
+
+ debug() << "Calling Properties::GetAll(Channel.Interface.Group)";
+ QDBusPendingCallWatcher *watcher =
+ new QDBusPendingCallWatcher(
+ properties->GetAll(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_GROUP)),
+ parent);
+ parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotGroupProperties(QDBusPendingCallWatcher*)));
+}
+
+void Channel::Private::introspectGroupFallbackFlags()
+{
+ Q_ASSERT(group != 0);
+
+ debug() << "Calling Channel.Interface.Group::GetGroupFlags()";
+ QDBusPendingCallWatcher *watcher =
+ new QDBusPendingCallWatcher(group->GetGroupFlags(), parent);
+ parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotGroupFlags(QDBusPendingCallWatcher*)));
+}
+
+void Channel::Private::introspectGroupFallbackMembers()
+{
+ Q_ASSERT(group != 0);
+
+ debug() << "Calling Channel.Interface.Group::GetAllMembers()";
+ QDBusPendingCallWatcher *watcher =
+ new QDBusPendingCallWatcher(group->GetAllMembers(), parent);
+ parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotAllMembers(QDBusPendingCallWatcher*)));
+}
+
+void Channel::Private::introspectGroupFallbackLocalPendingWithInfo()
+{
+ Q_ASSERT(group != 0);
+
+ debug() << "Calling Channel.Interface.Group::GetLocalPendingMembersWithInfo()";
+ QDBusPendingCallWatcher *watcher =
+ new QDBusPendingCallWatcher(group->GetLocalPendingMembersWithInfo(),
+ parent);
+ parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotLocalPendingMembersWithInfo(QDBusPendingCallWatcher*)));
+}
+
+void Channel::Private::introspectGroupFallbackSelfHandle()
+{
+ Q_ASSERT(group != 0);
+
+ debug() << "Calling Channel.Interface.Group::GetSelfHandle()";
+ QDBusPendingCallWatcher *watcher =
+ new QDBusPendingCallWatcher(group->GetSelfHandle(), parent);
+ parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotSelfHandle(QDBusPendingCallWatcher*)));
+}
+
+void Channel::Private::introspectConference()
+{
+ Q_ASSERT(properties != 0);
+ Q_ASSERT(conference == 0);
+
+ debug() << "Introspecting Conference interface";
+ conference = parent->interface<Client::ChannelInterfaceConferenceInterface>();
+ Q_ASSERT(conference != 0);
+
+ introspectingConference = true;
+
+ debug() << "Connecting to Channel.Interface.Conference.ChannelMerged/Removed";
+ parent->connect(conference,
+ SIGNAL(ChannelMerged(QDBusObjectPath,uint,QVariantMap)),
+ SLOT(onConferenceChannelMerged(QDBusObjectPath,uint,QVariantMap)));
+ parent->connect(conference,
+ SIGNAL(ChannelRemoved(QDBusObjectPath,QVariantMap)),
+ SLOT(onConferenceChannelRemoved(QDBusObjectPath,QVariantMap)));
+
+ debug() << "Calling Properties::GetAll(Channel.Interface.Conference)";
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+ properties->GetAll(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_CONFERENCE)),
+ parent);
+ parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotConferenceProperties(QDBusPendingCallWatcher*)));
+}
+
+void Channel::Private::introspectConferenceInitialInviteeContacts(Private *self)
+{
+ if (!self->conferenceInitialInviteeHandles.isEmpty()) {
+ ContactManagerPtr manager = self->connection->contactManager();
+ PendingContacts *pendingContacts = manager->contactsForHandles(
+ self->conferenceInitialInviteeHandles);
+ self->parent->connect(pendingContacts,
+ SIGNAL(finished(Tp::PendingOperation *)),
+ SLOT(gotConferenceInitialInviteeContacts(Tp::PendingOperation *)));
+ } else {
+ self->readinessHelper->setIntrospectCompleted(
+ FeatureConferenceInitialInviteeContacts, true);
+ }
+}
+
+void Channel::Private::continueIntrospection()
+{
+ if (introspectQueue.isEmpty()) {
+ // this should always be true, but let's make sure
+ if (!parent->isReady(Channel::FeatureCore)) {
+ if (groupMembersChangedQueue.isEmpty() && !buildingContacts &&
+ !introspectingConference) {
+ debug() << "Both the IS and the MCD queue empty for the first time. Ready.";
+ setReady();
+ } else {
+ debug() << "Introspection done before contacts done - contacts sets ready";
+ }
+ }
+ } else {
+ (this->*(introspectQueue.dequeue()))();
+ }
+}
+
+void Channel::Private::extractMainProps(const QVariantMap &props)
+{
+ const static QString keyChannelType(QLatin1String("ChannelType"));
+ const static QString keyInterfaces(QLatin1String("Interfaces"));
+ const static QString keyTargetHandle(QLatin1String("TargetHandle"));
+ const static QString keyTargetHandleType(QLatin1String("TargetHandleType"));
+
+ bool haveProps = props.size() >= 4
+ && props.contains(keyChannelType)
+ && !qdbus_cast<QString>(props[keyChannelType]).isEmpty()
+ && props.contains(keyInterfaces)
+ && props.contains(keyTargetHandle)
+ && props.contains(keyTargetHandleType);
+
+ if (!haveProps) {
+ warning() << "Channel properties specified in 0.17.7 not found";
+
+ introspectQueue.enqueue(&Private::introspectMainFallbackChannelType);
+ introspectQueue.enqueue(&Private::introspectMainFallbackHandle);
+ introspectQueue.enqueue(&Private::introspectMainFallbackInterfaces);
+ } else {
+ parent->setInterfaces(qdbus_cast<QStringList>(props[keyInterfaces]));
+ readinessHelper->setInterfaces(parent->interfaces());
+ channelType = qdbus_cast<QString>(props[keyChannelType]);
+ targetHandle = qdbus_cast<uint>(props[keyTargetHandle]);
+ targetHandleType = qdbus_cast<uint>(props[keyTargetHandleType]);
+
+ const static QString keyTargetId(QLatin1String("TargetID"));
+ const static QString keyRequested(QLatin1String("Requested"));
+ const static QString keyInitiatorHandle(QLatin1String("InitiatorHandle"));
+ const static QString keyInitiatorId(QLatin1String("InitiatorID"));
+
+ if (props.contains(keyTargetId)) {
+ targetId = qdbus_cast<QString>(props[keyTargetId]);
+
+ if (targetHandleType == HandleTypeContact) {
+ connection->lowlevel()->injectContactId(targetHandle, targetId);
+ }
+ }
+
+ if (props.contains(keyRequested)) {
+ requested = qdbus_cast<uint>(props[keyRequested]);
+ }
+
+ if (props.contains(keyInitiatorHandle)) {
+ initiatorHandle = qdbus_cast<uint>(props[keyInitiatorHandle]);
+ }
+
+ if (props.contains(keyInitiatorId)) {
+ QString initiatorId = qdbus_cast<QString>(props[keyInitiatorId]);
+ connection->lowlevel()->injectContactId(initiatorHandle, initiatorId);
+ }
+
+ if (!fakeGroupInterfaceIfNeeded() &&
+ !parent->interfaces().contains(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_GROUP)) &&
+ initiatorHandle) {
+ // No group interface, so nobody will build the poor fellow for us. Will do it ourselves
+ // out of pity for him.
+ // TODO: needs testing. I would imagine some of the elaborate updateContacts logic
+ // tripping over with just this.
+ buildContacts();
+ }
+
+ nowHaveInterfaces();
+ }
+
+ debug() << "Have initiator handle:" << (initiatorHandle ? "yes" : "no");
+}
+
+void Channel::Private::extract0176GroupProps(const QVariantMap &props)
+{
+ const static QString keyGroupFlags(QLatin1String("GroupFlags"));
+ const static QString keyHandleOwners(QLatin1String("HandleOwners"));
+ const static QString keyLPMembers(QLatin1String("LocalPendingMembers"));
+ const static QString keyMembers(QLatin1String("Members"));
+ const static QString keyRPMembers(QLatin1String("RemotePendingMembers"));
+ const static QString keySelfHandle(QLatin1String("SelfHandle"));
+
+ bool haveProps = props.size() >= 6
+ && (props.contains(keyGroupFlags)
+ && (qdbus_cast<uint>(props[keyGroupFlags]) &
+ ChannelGroupFlagProperties))
+ && props.contains(keyHandleOwners)
+ && props.contains(keyLPMembers)
+ && props.contains(keyMembers)
+ && props.contains(keyRPMembers)
+ && props.contains(keySelfHandle);
+
+ if (!haveProps) {
+ warning() << " Properties specified in 0.17.6 not found";
+ warning() << " Handle owners and self handle tracking disabled";
+
+ introspectQueue.enqueue(&Private::introspectGroupFallbackFlags);
+ introspectQueue.enqueue(&Private::introspectGroupFallbackMembers);
+ introspectQueue.enqueue(&Private::introspectGroupFallbackLocalPendingWithInfo);
+ introspectQueue.enqueue(&Private::introspectGroupFallbackSelfHandle);
+ } else {
+ debug() << " Found properties specified in 0.17.6";
+
+ groupAreHandleOwnersAvailable = true;
+ groupIsSelfHandleTracked = true;
+
+ setGroupFlags(qdbus_cast<uint>(props[keyGroupFlags]));
+ groupHandleOwners = qdbus_cast<HandleOwnerMap>(props[keyHandleOwners]);
+
+ groupInitialMembers = qdbus_cast<UIntList>(props[keyMembers]);
+ groupInitialLP = qdbus_cast<LocalPendingInfoList>(props[keyLPMembers]);
+ groupInitialRP = qdbus_cast<UIntList>(props[keyRPMembers]);
+
+ uint propSelfHandle = qdbus_cast<uint>(props[keySelfHandle]);
+ // Don't overwrite the self handle we got from the Connection with 0
+ if (propSelfHandle) {
+ groupSelfHandle = propSelfHandle;
+ }
+
+ nowHaveInitialMembers();
+ }
+}
+
+void Channel::Private::nowHaveInterfaces()
+{
+ debug() << "Channel has" << parent->interfaces().size() <<
+ "optional interfaces:" << parent->interfaces();
+
+ QStringList interfaces = parent->interfaces();
+
+ if (interfaces.contains(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_GROUP))) {
+ introspectQueue.enqueue(&Private::introspectGroup);
+ }
+
+ if (interfaces.contains(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_CONFERENCE))) {
+ introspectQueue.enqueue(&Private::introspectConference);
+ }
+}
+
+void Channel::Private::nowHaveInitialMembers()
+{
+ // Must be called with no contacts anywhere in the first place
+ Q_ASSERT(!parent->isReady(Channel::FeatureCore));
+ Q_ASSERT(!buildingContacts);
+
+ Q_ASSERT(pendingGroupMembers.isEmpty());
+ Q_ASSERT(pendingGroupLocalPendingMembers.isEmpty());
+ Q_ASSERT(pendingGroupRemotePendingMembers.isEmpty());
+
+ Q_ASSERT(groupContacts.isEmpty());
+ Q_ASSERT(groupLocalPendingContacts.isEmpty());
+ Q_ASSERT(groupRemotePendingContacts.isEmpty());
+
+ // Set groupHaveMembers so we start queueing fresh MCD signals
+ Q_ASSERT(!groupHaveMembers);
+ groupHaveMembers = true;
+
+ // Synthesize MCD for current + RP
+ groupMembersChangedQueue.enqueue(new GroupMembersChangedInfo(
+ groupInitialMembers, // Members
+ UIntList(), // Removed - obviously, none
+ UIntList(), // LP - will be handled separately, see below
+ groupInitialRP, // Remote pending
+ QVariantMap())); // No details for members + RP
+
+ // Synthesize one MCD for each initial LP member - they might have different details
+ foreach (const LocalPendingInfo &info, groupInitialLP) {
+ QVariantMap details;
+
+ if (info.actor != 0) {
+ details.insert(QLatin1String("actor"), info.actor);
+ }
+
+ if (info.reason != ChannelGroupChangeReasonNone) {
+ details.insert(QLatin1String("change-reason"), info.reason);
+ }
+
+ if (!info.message.isEmpty()) {
+ details.insert(QLatin1String("message"), info.message);
+ }
+
+ groupMembersChangedQueue.enqueue(new GroupMembersChangedInfo(UIntList(), UIntList(),
+ UIntList() << info.toBeAdded, UIntList(), details));
+ }
+
+ // At least our added MCD event to process
+ processMembersChanged();
+}
+
+bool Channel::Private::setGroupFlags(uint newGroupFlags)
+{
+ if (groupFlags == newGroupFlags) {
+ return false;
+ }
+
+ groupFlags = newGroupFlags;
+
+ // this shouldn't happen but let's make sure
+ if (!parent->interfaces().contains(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_GROUP))) {
+ return false;
+ }
+
+ if ((groupFlags & ChannelGroupFlagMembersChangedDetailed) &&
+ !usingMembersChangedDetailed) {
+ usingMembersChangedDetailed = true;
+ debug() << "Starting to exclusively listen to MembersChangedDetailed for" <<
+ parent->objectPath();
+ parent->disconnect(group,
+ SIGNAL(MembersChanged(QString,Tp::UIntList,
+ Tp::UIntList,Tp::UIntList,
+ Tp::UIntList,uint,uint)),
+ parent,
+ SLOT(onMembersChanged(QString,Tp::UIntList,
+ Tp::UIntList,Tp::UIntList,
+ Tp::UIntList,uint,uint)));
+ } else if (!(groupFlags & ChannelGroupFlagMembersChangedDetailed) &&
+ usingMembersChangedDetailed) {
+ warning() << " Channel service did spec-incompliant removal of MCD from GroupFlags";
+ usingMembersChangedDetailed = false;
+ parent->connect(group,
+ SIGNAL(MembersChanged(QString,Tp::UIntList,
+ Tp::UIntList,Tp::UIntList,
+ Tp::UIntList,uint,uint)),
+ parent,
+ SLOT(onMembersChanged(QString,Tp::UIntList,
+ Tp::UIntList,Tp::UIntList,
+ Tp::UIntList,uint,uint)));
+ }
+
+ return true;
+}
+
+void Channel::Private::buildContacts()
+{
+ buildingContacts = true;
+
+ ContactManagerPtr manager = connection->contactManager();
+ UIntList toBuild = QSet<uint>(pendingGroupMembers +
+ pendingGroupLocalPendingMembers +
+ pendingGroupRemotePendingMembers).toList();
+
+ if (currentGroupMembersChangedInfo &&
+ currentGroupMembersChangedInfo->actor != 0) {
+ toBuild.append(currentGroupMembersChangedInfo->actor);
+ }
+
+ if (!initiatorContact && initiatorHandle) {
+ // No initiator contact, but Yes initiator handle - might do something about it with just
+ // that information
+ toBuild.append(initiatorHandle);
+ }
+
+ if (!targetContact && targetHandleType == HandleTypeContact && targetHandle != 0) {
+ toBuild.append(targetHandle);
+ }
+
+ // always try to retrieve selfContact and check if it changed on
+ // updateContacts or on gotContacts, in case we were not able to retrieve it
+ if (groupSelfHandle) {
+ toBuild.append(groupSelfHandle);
+ }
+
+ // group self handle changed to 0 <- strange but it may happen, and contacts
+ // were being built at the time, so check now
+ if (toBuild.isEmpty()) {
+ if (!groupSelfHandle && groupSelfContact) {
+ groupSelfContact.reset();
+ if (parent->isReady(Channel::FeatureCore)) {
+ emit parent->groupSelfContactChanged();
+ }
+ }
+
+ buildingContacts = false;
+ return;
+ }
+
+ PendingContacts *pendingContacts = manager->contactsForHandles(
+ toBuild);
+ parent->connect(pendingContacts,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(gotContacts(Tp::PendingOperation*)));
+}
+
+void Channel::Private::processMembersChanged()
+{
+ Q_ASSERT(!buildingContacts);
+
+ if (groupMembersChangedQueue.isEmpty()) {
+ if (pendingRetrieveGroupSelfContact) {
+ pendingRetrieveGroupSelfContact = false;
+ // nothing queued but selfContact changed
+ buildContacts();
+ return;
+ }
+
+ if (!parent->isReady(Channel::FeatureCore)) {
+ if (introspectQueue.isEmpty()) {
+ debug() << "Both the MCD and the introspect queue empty for the first time. Ready!";
+
+ if (initiatorHandle && !initiatorContact) {
+ warning() << " Unable to create contact object for initiator with handle" <<
+ initiatorHandle;
+ }
+
+ if (targetHandleType == HandleTypeContact && targetHandle != 0 && !targetContact) {
+ warning() << " Unable to create contact object for target with handle" <<
+ targetHandle;
+ }
+
+ if (groupSelfHandle && !groupSelfContact) {
+ warning() << " Unable to create contact object for self handle" <<
+ groupSelfHandle;
+ }
+
+ continueIntrospection();
+ } else {
+ debug() << "Contact queue empty but introspect queue isn't. IS will set ready.";
+ }
+ }
+
+ return;
+ }
+
+ Q_ASSERT(pendingGroupMembers.isEmpty());
+ Q_ASSERT(pendingGroupLocalPendingMembers.isEmpty());
+ Q_ASSERT(pendingGroupRemotePendingMembers.isEmpty());
+
+ // always set this to false here, as buildContacts will always try to
+ // retrieve the selfContact and updateContacts will check if the built
+ // contact is the same as the current contact.
+ pendingRetrieveGroupSelfContact = false;
+
+ currentGroupMembersChangedInfo = groupMembersChangedQueue.dequeue();
+
+ foreach (uint handle, currentGroupMembersChangedInfo->added) {
+ if (!groupContacts.contains(handle)) {
+ pendingGroupMembers.insert(handle);
+ }
+
+ // the member was added to current members, check if it was in the
+ // local/pending lists and if true, schedule for removal from that list
+ if (groupLocalPendingContacts.contains(handle)) {
+ groupLocalPendingMembersToRemove.append(handle);
+ } else if(groupRemotePendingContacts.contains(handle)) {
+ groupRemotePendingMembersToRemove.append(handle);
+ }
+ }
+
+ foreach (uint handle, currentGroupMembersChangedInfo->localPending) {
+ if (!groupLocalPendingContacts.contains(handle)) {
+ pendingGroupLocalPendingMembers.insert(handle);
+ }
+ }
+
+ foreach (uint handle, currentGroupMembersChangedInfo->remotePending) {
+ if (!groupRemotePendingContacts.contains(handle)) {
+ pendingGroupRemotePendingMembers.insert(handle);
+ }
+ }
+
+ foreach (uint handle, currentGroupMembersChangedInfo->removed) {
+ groupMembersToRemove.append(handle);
+ }
+
+ // Always go through buildContacts - we might have a self/initiator/whatever handle to build
+ buildContacts();
+}
+
+void Channel::Private::updateContacts(const QList<ContactPtr> &contacts)
+{
+ Contacts groupContactsAdded;
+ Contacts groupLocalPendingContactsAdded;
+ Contacts groupRemotePendingContactsAdded;
+ ContactPtr actorContact;
+ bool selfContactUpdated = false;
+
+ debug() << "Entering Chan::Priv::updateContacts() with" << contacts.size() << "contacts";
+
+ // FIXME: simplify. Some duplication of logic present.
+ foreach (ContactPtr contact, contacts) {
+ uint handle = contact->handle()[0];
+ if (pendingGroupMembers.contains(handle)) {
+ groupContactsAdded.insert(contact);
+ groupContacts[handle] = contact;
+ } else if (pendingGroupLocalPendingMembers.contains(handle)) {
+ groupLocalPendingContactsAdded.insert(contact);
+ groupLocalPendingContacts[handle] = contact;
+ // FIXME: should set the details and actor here too
+ groupLocalPendingContactsChangeInfo[handle] = GroupMemberChangeDetails();
+ } else if (pendingGroupRemotePendingMembers.contains(handle)) {
+ groupRemotePendingContactsAdded.insert(contact);
+ groupRemotePendingContacts[handle] = contact;
+ }
+
+ if (groupSelfHandle == handle && groupSelfContact != contact) {
+ groupSelfContact = contact;
+ selfContactUpdated = true;
+ }
+
+ if (!initiatorContact && initiatorHandle == handle) {
+ // No initiator contact stored, but there's a contact for the initiator handle
+ // We can use that!
+ initiatorContact = contact;
+ }
+
+ if (!targetContact && targetHandleType == HandleTypeContact && targetHandle == handle) {
+ targetContact = contact;
+
+ if (targetId.isEmpty()) {
+ // For some reason, TargetID was missing from the property map. We can initialize it
+ // here in that case.
+ targetId = targetContact->id();
+ }
+ }
+
+ if (currentGroupMembersChangedInfo &&
+ currentGroupMembersChangedInfo->actor == contact->handle()[0]) {
+ actorContact = contact;
+ }
+ }
+
+ if (!groupSelfHandle && groupSelfContact) {
+ groupSelfContact.reset();
+ selfContactUpdated = true;
+ }
+
+ pendingGroupMembers.clear();
+ pendingGroupLocalPendingMembers.clear();
+ pendingGroupRemotePendingMembers.clear();
+
+ // FIXME: This shouldn't be needed. Clearer would be to first scan for the actor being present
+ // in the contacts supplied.
+ foreach (ContactPtr contact, contacts) {
+ uint handle = contact->handle()[0];
+ if (groupLocalPendingContactsChangeInfo.contains(handle)) {
+ groupLocalPendingContactsChangeInfo[handle] =
+ GroupMemberChangeDetails(actorContact,
+ currentGroupMembersChangedInfo ? currentGroupMembersChangedInfo->details : QVariantMap());
+ }
+ }
+
+ Contacts groupContactsRemoved;
+ ContactPtr contactToRemove;
+ foreach (uint handle, groupMembersToRemove) {
+ if (groupContacts.contains(handle)) {
+ contactToRemove = groupContacts[handle];
+ groupContacts.remove(handle);
+ } else if (groupLocalPendingContacts.contains(handle)) {
+ contactToRemove = groupLocalPendingContacts[handle];
+ groupLocalPendingContacts.remove(handle);
+ } else if (groupRemotePendingContacts.contains(handle)) {
+ contactToRemove = groupRemotePendingContacts[handle];
+ groupRemotePendingContacts.remove(handle);
+ }
+
+ if (groupLocalPendingContactsChangeInfo.contains(handle)) {
+ groupLocalPendingContactsChangeInfo.remove(handle);
+ }
+
+ if (contactToRemove) {
+ groupContactsRemoved.insert(contactToRemove);
+ }
+ }
+ groupMembersToRemove.clear();
+
+ // FIXME: drop the LPToRemove and RPToRemove sets - they're redundant
+ foreach (uint handle, groupLocalPendingMembersToRemove) {
+ groupLocalPendingContacts.remove(handle);
+ }
+ groupLocalPendingMembersToRemove.clear();
+
+ foreach (uint handle, groupRemotePendingMembersToRemove) {
+ groupRemotePendingContacts.remove(handle);
+ }
+ groupRemotePendingMembersToRemove.clear();
+
+ if (!groupContactsAdded.isEmpty() ||
+ !groupLocalPendingContactsAdded.isEmpty() ||
+ !groupRemotePendingContactsAdded.isEmpty() ||
+ !groupContactsRemoved.isEmpty()) {
+ GroupMemberChangeDetails details(
+ actorContact,
+ currentGroupMembersChangedInfo ? currentGroupMembersChangedInfo->details : QVariantMap());
+
+ if (currentGroupMembersChangedInfo
+ && currentGroupMembersChangedInfo->removed.contains(groupSelfHandle)) {
+ // Update groupSelfContactRemoveInfo with the proper actor in case
+ // the actor was not available by the time onMembersChangedDetailed
+ // was called.
+ groupSelfContactRemoveInfo = details;
+ }
+
+ if (parent->isReady(Channel::FeatureCore)) {
+ // Channel is ready, we can signal membership changes to the outside world without
+ // confusing anyone's fragile logic.
+ emit parent->groupMembersChanged(
+ groupContactsAdded,
+ groupLocalPendingContactsAdded,
+ groupRemotePendingContactsAdded,
+ groupContactsRemoved,
+ details);
+ }
+ }
+ delete currentGroupMembersChangedInfo;
+ currentGroupMembersChangedInfo = 0;
+
+ if (selfContactUpdated && parent->isReady(Channel::FeatureCore)) {
+ emit parent->groupSelfContactChanged();
+ }
+
+ processMembersChanged();
+}
+
+bool Channel::Private::fakeGroupInterfaceIfNeeded()
+{
+ if (parent->interfaces().contains(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_GROUP))) {
+ return false;
+ } else if (targetHandleType != HandleTypeContact) {
+ return false;
+ }
+
+ // fake group interface
+ if (connection->selfHandle() && targetHandle) {
+ // Fake groupSelfHandle and initial members, let the MCD handling take care of the rest
+ // TODO connect to Connection::selfHandleChanged
+ groupSelfHandle = connection->selfHandle();
+ groupInitialMembers = UIntList() << groupSelfHandle << targetHandle;
+
+ debug().nospace() << "Faking a group on channel with self handle=" <<
+ groupSelfHandle << " and other handle=" << targetHandle;
+
+ nowHaveInitialMembers();
+ } else {
+ warning() << "Connection::selfHandle is 0 or targetHandle is 0, "
+ "not faking a group on channel";
+ }
+
+ return true;
+}
+
+void Channel::Private::setReady()
+{
+ Q_ASSERT(!parent->isReady(Channel::FeatureCore));
+
+ debug() << "Channel fully ready";
+ debug() << " Channel type" << channelType;
+ debug() << " Target handle" << targetHandle;
+ debug() << " Target handle type" << targetHandleType;
+
+ if (parent->interfaces().contains(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_GROUP))) {
+ debug() << " Group: flags" << groupFlags;
+ if (groupAreHandleOwnersAvailable) {
+ debug() << " Group: Number of handle owner mappings" <<
+ groupHandleOwners.size();
+ }
+ else {
+ debug() << " Group: No handle owners property present";
+ }
+ debug() << " Group: Number of current members" <<
+ groupContacts.size();
+ debug() << " Group: Number of local pending members" <<
+ groupLocalPendingContacts.size();
+ debug() << " Group: Number of remote pending members" <<
+ groupRemotePendingContacts.size();
+ debug() << " Group: Self handle" << groupSelfHandle <<
+ "tracked:" << (groupIsSelfHandleTracked ? "yes" : "no");
+ }
+
+ readinessHelper->setIntrospectCompleted(FeatureCore, true);
+}
+
+QString Channel::Private::groupMemberChangeDetailsTelepathyError(
+ const GroupMemberChangeDetails &details)
+{
+ QString error;
+ uint reason = details.reason();
+ switch (reason) {
+ case ChannelGroupChangeReasonOffline:
+ error = QLatin1String(TELEPATHY_ERROR_OFFLINE);
+ break;
+ case ChannelGroupChangeReasonKicked:
+ error = QLatin1String(TELEPATHY_ERROR_CHANNEL_KICKED);
+ break;
+ case ChannelGroupChangeReasonBanned:
+ error = QLatin1String(TELEPATHY_ERROR_CHANNEL_BANNED);
+ break;
+ case ChannelGroupChangeReasonBusy:
+ error = QLatin1String(TELEPATHY_ERROR_BUSY);
+ break;
+ case ChannelGroupChangeReasonNoAnswer:
+ error = QLatin1String(TELEPATHY_ERROR_NO_ANSWER);
+ break;
+ case ChannelGroupChangeReasonPermissionDenied:
+ error = QLatin1String(TELEPATHY_ERROR_PERMISSION_DENIED);
+ break;
+ case ChannelGroupChangeReasonInvalidContact:
+ error = QLatin1String(TELEPATHY_ERROR_DOES_NOT_EXIST);
+ break;
+ // The following change reason are being mapped to default
+ // case ChannelGroupChangeReasonNone:
+ // case ChannelGroupChangeReasonInvited: // shouldn't happen
+ // case ChannelGroupChangeReasonError:
+ // case ChannelGroupChangeReasonRenamed:
+ // case ChannelGroupChangeReasonSeparated: // shouldn't happen
+ default:
+ // let's use the actor handle and selfHandle here instead of the
+ // contacts, as the contacts may not be ready.
+ error = ((qdbus_cast<uint>(details.allDetails().value(QLatin1String("actor"))) == groupSelfHandle) ?
+ QLatin1String(TELEPATHY_ERROR_CANCELLED) :
+ QLatin1String(TELEPATHY_ERROR_TERMINATED));
+ break;
+ }
+
+ return error;
+}
+
+void Channel::Private::processConferenceChannelRemoved()
+{
+ if (buildingConferenceChannelRemovedActorContact ||
+ conferenceChannelRemovedQueue.isEmpty()) {
+ return;
+ }
+
+ ConferenceChannelRemovedInfo *info = conferenceChannelRemovedQueue.first();
+ if (!conferenceChannels.contains(info->channelPath.path())) {
+ info = conferenceChannelRemovedQueue.dequeue();
+ delete info;
+ processConferenceChannelRemoved();
+ return;
+ }
+
+ buildingConferenceChannelRemovedActorContact = true;
+
+ if (info->details.contains(keyActor)) {
+ ContactManagerPtr manager = connection->contactManager();
+ PendingContacts *pendingContacts = manager->contactsForHandles(
+ UIntList() << qdbus_cast<uint>(info->details.value(keyActor)));
+ parent->connect(pendingContacts,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(gotConferenceChannelRemovedActorContact(Tp::PendingOperation*)));
+ } else {
+ parent->gotConferenceChannelRemovedActorContact(0);
+ }
+}
+
+struct TP_QT_NO_EXPORT Channel::GroupMemberChangeDetails::Private : public QSharedData
+{
+ Private(const ContactPtr &actor, const QVariantMap &details)
+ : actor(actor), details(details) {}
+
+ ContactPtr actor;
+ QVariantMap details;
+};
+
+/**
+ * \class Channel::GroupMemberChangeDetails
+ * \ingroup clientchannel
+ * \headerfile TelepathyQt/channel.h <TelepathyQt/Channel>
+ *
+ * \brief The Channel::GroupMemberChangeDetails class represents the details of a group membership
+ * change.
+ *
+ * Extended information is not always available; this will be reflected by
+ * the return value of isValid().
+ */
+
+/**
+ * Constructs a new invalid GroupMemberChangeDetails instance.
+ */
+Channel::GroupMemberChangeDetails::GroupMemberChangeDetails()
+{
+}
+
+/**
+ * Copy constructor.
+ */
+Channel::GroupMemberChangeDetails::GroupMemberChangeDetails(const GroupMemberChangeDetails &other)
+ : mPriv(other.mPriv)
+{
+}
+
+/**
+ * Class destructor.
+ */
+Channel::GroupMemberChangeDetails::~GroupMemberChangeDetails()
+{
+}
+
+/**
+ * Assigns all information (validity, details) from other to this.
+ */
+Channel::GroupMemberChangeDetails &Channel::GroupMemberChangeDetails::operator=(
+ const GroupMemberChangeDetails &other)
+{
+ this->mPriv = other.mPriv;
+ return *this;
+}
+
+/**
+ * \fn bool Channel::GroupMemberChangeDetails::isValid() const
+ *
+ * Return whether the details are valid (have actually been received from the service).
+ *
+ * \return \c true if valid, \c false otherwise.
+ */
+
+/**
+ * Return whether the details specify an actor.
+ *
+ * If present, actor() will return the contact object representing the person who made the change.
+ *
+ * \return \c true if the actor is known, \c false otherwise.
+ * \sa actor()
+ */
+bool Channel::GroupMemberChangeDetails::hasActor() const
+{
+ return isValid() && !mPriv->actor.isNull();
+}
+
+/**
+ * Return the contact object representing the person who made the change (actor), if known.
+ *
+ * \return A pointer to the Contact object, or a null ContactPtr if the actor is unknown.
+ * \sa hasActor()
+ */
+ContactPtr Channel::GroupMemberChangeDetails::actor() const
+{
+ return isValid() ? mPriv->actor : ContactPtr();
+}
+
+/**
+ * \fn bool Channel::GroupMemberChangeDetails::hasReason() const
+ *
+ * Return whether the details specify the reason for the change.
+ *
+ * \return \c true if the reason is known, \c false otherwise.
+ * \sa reason()
+ */
+
+/**
+ * \fn ChannelGroupChangeReason Channel::GroupMemberChangeDetails::reason() const
+ *
+ * Return the reason for the change, if known.
+ *
+ * \return The change reason as #ChannelGroupChangeReason, or #ChannelGroupChangeReasonNone
+ * if the reason is unknown.
+ * \sa hasReason()
+ */
+
+/**
+ * \fn bool Channel::GroupMemberChangeDetails::hasMessage() const
+ *
+ * Return whether the details specify a human-readable message from the contact represented by
+ * actor() pertaining to the change.
+ *
+ * \return \c true if the message is known, \c false otherwise.
+ * \sa message()
+ */
+
+/**
+ * \fn QString Channel::GroupMemberChangeDetails::message() const
+ *
+ * Return a human-readable message from the contact represented by actor() pertaining to the change,
+ * if known.
+ *
+ * \return The message, or an empty string if the message is unknown.
+ * \sa hasMessage()
+ */
+
+/**
+ * \fn bool Channel::GroupMemberChangeDetails::hasError() const
+ *
+ * Return whether the details specify a D-Bus error describing the change.
+ *
+ * \return \c true if the error is known, \c false otherwise.
+ * \sa error()
+ */
+
+/**
+ * \fn QString Channel::GroupMemberChangeDetails::error() const
+ *
+ * Return the D-Bus error describing the change, if known.
+ *
+ * The D-Bus error provides more specific information than the reason() and should be used if
+ * applicable.
+ *
+ * \return A D-Bus error describing the change, or an empty string if the error is unknown.
+ * \sa hasError()
+ */
+
+/**
+ * \fn bool Channel::GroupMemberChangeDetails::hasDebugMessage() const
+ *
+ * Return whether the details specify a debug message.
+ *
+ * \return \c true if debug message is present, \c false otherwise.
+ * \sa debugMessage()
+ */
+
+/**
+ * \fn QString Channel::GroupMemberChangeDetails::debugMessage() const
+ *
+ * Return the debug message specified by the details, if any.
+ *
+ * The debug message is purely informational, offered for display for bug reporting purposes, and
+ * should not be attempted to be parsed.
+ *
+ * \return The debug message, or an empty string if there is none.
+ * \sa hasDebugMessage()
+ */
+
+/**
+ * Return a map containing all details of the group members change.
+ *
+ * This is useful for accessing domain-specific additional details.
+ *
+ * \return The details of the group members change as QVariantMap.
+ */
+QVariantMap Channel::GroupMemberChangeDetails::allDetails() const
+{
+ return isValid() ? mPriv->details : QVariantMap();
+}
+
+Channel::GroupMemberChangeDetails::GroupMemberChangeDetails(const ContactPtr &actor,
+ const QVariantMap &details)
+ : mPriv(new Private(actor, details))
+{
+}
+
+/**
+ * \class Channel
+ * \ingroup clientchannel
+ * \headerfile TelepathyQt/channel.h <TelepathyQt/Channel>
+ *
+ * \brief The Channel class represents a Telepathy channel.
+ *
+ * All communication in the Telepathy framework is carried out via channel
+ * objects. Specialized classes for some specific channel types such as
+ * StreamedMediaChannel, TextChannel, FileTransferChannel are provided.
+ *
+ * The remote object accessor functions on this object (channelType(), targetHandleType(),
+ * and so on) don't make any D-Bus calls; instead, they return/use
+ * values cached from a previous introspection run. The introspection process
+ * populates their values in the most efficient way possible based on what the
+ * service implements.
+ *
+ * To avoid unnecessary D-Bus traffic, some accessors only return valid
+ * information after specific features have been enabled.
+ * For instance, to retrieve the initial invitee contacts in a conference channel,
+ * it is necessary to enable the feature Channel::FeatureConferenceInitialInviteeContacts.
+ * See the individual methods descriptions for more details.
+ *
+ * Channel features can be enabled by constructing a ChannelFactory and enabling
+ * the desired features, and passing it to AccountManager, Account or ClientRegistrar
+ * when creating them as appropriate. However, if a particular
+ * feature is only ever used in a specific circumstance, such as an user opening
+ * some settings dialog separate from the general view of the application,
+ * features can be later enabled as needed by calling becomeReady() with the additional
+ * features, and waiting for the resulting PendingOperation to finish.
+ *
+ * Each channel is owned by a connection. If the Connection object becomes invalidated
+ * the Channel object will also get invalidated.
+ *
+ * \section chan_usage_sec Usage
+ *
+ * \subsection chan_create_sec Creating a channel object
+ *
+ * Channel objects can be created in various ways, but the preferred way is
+ * trough Account channel creation methods such as Account::ensureTextChat(),
+ * Account::createFileTransfer(), which uses the channel dispatcher.
+ *
+ * If you already know the object path, you can just call create().
+ * For example:
+ *
+ * \code
+ *
+ * ChannelPtr chan = Channel::create(connection, objectPath,
+ * immutableProperties);
+ *
+ * \endcode
+ *
+ * \subsection chan_ready_sec Making channel ready to use
+ *
+ * A Channel object needs to become ready before usage, meaning that the
+ * introspection process finished and the object accessors can be used.
+ *
+ * To make the object ready, use becomeReady() and wait for the
+ * PendingOperation::finished() signal to be emitted.
+ *
+ * \code
+ *
+ * class MyClass : public QObject
+ * {
+ * QOBJECT
+ *
+ * public:
+ * MyClass(QObject *parent = 0);
+ * ~MyClass() { }
+ *
+ * private Q_SLOTS:
+ * void onChannelReady(Tp::PendingOperation*);
+ *
+ * private:
+ * ChannelPtr chan;
+ * };
+ *
+ * MyClass::MyClass(const ConnectionPtr &connection,
+ * const QString &objectPath, const QVariantMap &immutableProperties)
+ * : QObject(parent)
+ * chan(Channel::create(connection, objectPath, immutableProperties))
+ * {
+ * connect(chan->becomeReady(),
+ * SIGNAL(finished(Tp::PendingOperation*)),
+ * SLOT(onChannelReady(Tp::PendingOperation*)));
+ * }
+ *
+ * void MyClass::onChannelReady(Tp::PendingOperation *op)
+ * {
+ * if (op->isError()) {
+ * qWarning() << "Channel cannot become ready:" <<
+ * op->errorName() << "-" << op->errorMessage();
+ * return;
+ * }
+ *
+ * // Channel is now ready
+ * }
+ *
+ * \endcode
+ *
+ * See \ref async_model, \ref shared_ptr
+ */
+
+/**
+ * Feature representing the core that needs to become ready to make the Channel
+ * object usable.
+ *
+ * Note that this feature must be enabled in order to use most Channel methods.
+ * See specific methods documentation for more details.
+ *
+ * When calling isReady(), becomeReady(), this feature is implicitly added
+ * to the requested features.
+ */
+const Feature Channel::FeatureCore = Feature(QLatin1String(Channel::staticMetaObject.className()), 0, true);
+
+/**
+ * Feature used in order to access the conference initial invitee contacts info.
+ *
+ * \sa conferenceInitialInviteeContacts()
+ */
+const Feature Channel::FeatureConferenceInitialInviteeContacts = Feature(QLatin1String(Channel::staticMetaObject.className()), 1, true);
+
+/**
+ * Create a new Channel object.
+ *
+ * \param connection Connection owning this channel, and specifying the
+ * service.
+ * \param objectPath The channel object path.
+ * \param immutableProperties The channel immutable properties.
+ * \return A ChannelPtr object pointing to the newly created Channel object.
+ *
+ * \todo \a immutableProperties should be used to populate the corresponding accessors (such as
+ * channelType()) already on construction, not only when making FeatureCore ready (fd.o #41654)
+ */
+ChannelPtr Channel::create(const ConnectionPtr &connection,
+ const QString &objectPath, const QVariantMap &immutableProperties)
+{
+ return ChannelPtr(new Channel(connection, objectPath, immutableProperties,
+ Channel::FeatureCore));
+}
+
+/**
+ * Construct a new Channel object.
+ *
+ * \param connection Connection owning this channel, and specifying the
+ * service.
+ * \param objectPath The channel object path.
+ * \param immutableProperties The channel immutable properties.
+ * \param coreFeature The core feature of the channel type. The corresponding introspectable should
+ * depend on Channel::FeatureCore.
+ */
+Channel::Channel(const ConnectionPtr &connection,
+ const QString &objectPath,
+ const QVariantMap &immutableProperties,
+ const Feature &coreFeature)
+ : StatefulDBusProxy(connection->dbusConnection(), connection->busName(),
+ objectPath, coreFeature),
+ OptionalInterfaceFactory<Channel>(this),
+ mPriv(new Private(this, connection, immutableProperties))
+{
+}
+
+/**
+ * Class destructor.
+ */
+Channel::~Channel()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the connection owning this channel.
+ *
+ * \return A pointer to the Connection object.
+ */
+ConnectionPtr Channel::connection() const
+{
+ return mPriv->connection;
+}
+
+/**
+ * Return the immutable properties of the channel.
+ *
+ * If the channel is ready (isReady(Channel::FeatureCore) returns true), the following keys are
+ * guaranteed to be present:
+ * org.freedesktop.Telepathy.Channel.ChannelType,
+ * org.freedesktop.Telepathy.Channel.TargetHandleType,
+ * org.freedesktop.Telepathy.Channel.TargetHandle and
+ * org.freedesktop.Telepathy.Channel.Requested.
+ *
+ * The keys and values in this map are defined by the \telepathy_spec,
+ * or by third-party extensions to that specification.
+ * These are the properties that cannot change over the lifetime of the
+ * channel; they're announced in the result of the request, for efficiency.
+ *
+ * \return The immutable properties as QVariantMap.
+ */
+QVariantMap Channel::immutableProperties() const
+{
+ if (isReady(Channel::FeatureCore)) {
+ QString key;
+
+ key = QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType");
+ if (!mPriv->immutableProperties.contains(key)) {
+ mPriv->immutableProperties.insert(key, mPriv->channelType);
+ }
+
+ key = QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".Interfaces");
+ if (!mPriv->immutableProperties.contains(key)) {
+ mPriv->immutableProperties.insert(key, interfaces());
+ }
+
+ key = QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType");
+ if (!mPriv->immutableProperties.contains(key)) {
+ mPriv->immutableProperties.insert(key, mPriv->targetHandleType);
+ }
+
+ key = QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandle");
+ if (!mPriv->immutableProperties.contains(key)) {
+ mPriv->immutableProperties.insert(key, mPriv->targetHandle);
+ }
+
+ key = QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetID");
+ if (!mPriv->immutableProperties.contains(key)) {
+ mPriv->immutableProperties.insert(key, mPriv->targetId);
+ }
+
+ key = QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".Requested");
+ if (!mPriv->immutableProperties.contains(key)) {
+ mPriv->immutableProperties.insert(key, mPriv->requested);
+ }
+
+ key = QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".InitiatorHandle");
+ if (!mPriv->immutableProperties.contains(key)) {
+ mPriv->immutableProperties.insert(key, mPriv->initiatorHandle);
+ }
+
+ key = QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".InitiatorID");
+ if (!mPriv->immutableProperties.contains(key) && !mPriv->initiatorContact.isNull()) {
+ mPriv->immutableProperties.insert(key, mPriv->initiatorContact->id());
+ }
+ }
+
+ return mPriv->immutableProperties;
+}
+
+/**
+ * Return the D-Bus interface name for the type of this channel.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return The D-Bus interface name for the type of the channel.
+ */
+QString Channel::channelType() const
+{
+ // Similarly, we don't want warnings triggered when using the type interface
+ // proxies internally.
+ if (!isReady(Channel::FeatureCore) && mPriv->channelType.isEmpty()) {
+ warning() << "Channel::channelType() before the channel type has "
+ "been received";
+ }
+ else if (!isValid()) {
+ warning() << "Channel::channelType() used with channel closed";
+ }
+
+ return mPriv->channelType;
+}
+
+/**
+ * Return the type of the handle returned by targetHandle() as specified in
+ * #HandleType.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return The target handle type as #HandleType.
+ * \sa targetHandle(), targetId()
+ */
+HandleType Channel::targetHandleType() const
+{
+ if (!isReady(Channel::FeatureCore)) {
+ warning() << "Channel::targetHandleType() used channel not ready";
+ }
+
+ return (HandleType) mPriv->targetHandleType;
+}
+
+/**
+ * Return the handle of the remote party with which this channel
+ * communicates.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return An integer representing the target handle, which is of the type
+ * targetHandleType() indicates.
+ * \sa targetHandleType(), targetId()
+ */
+uint Channel::targetHandle() const
+{
+ if (!isReady(Channel::FeatureCore)) {
+ warning() << "Channel::targetHandle() used channel not ready";
+ }
+
+ return mPriv->targetHandle;
+}
+
+/**
+ * Return the persistent unique ID of the remote party with which this channel communicates.
+ *
+ * If targetHandleType() is #HandleTypeContact, this will be the ID of the remote contact, and
+ * similarly the unique ID of the room when targetHandleType() is #HandleTypeRoom.
+ *
+ * This is not necessarily the best identifier to display to the user, though. In particular, for
+ * contacts, their alias should be displayed instead. It can be used for matching channels and UI
+ * elements for them across reconnects, though, at which point the old channels and contacts are
+ * invalidated.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return The target identifier.
+ * \sa targetHandle(), targetContact()
+ */
+QString Channel::targetId() const
+{
+ if (!isReady(Channel::FeatureCore)) {
+ warning() << "Channel::targetId() used, but the channel is not ready";
+ }
+
+ return mPriv->targetId;
+}
+
+/**
+ * Return the contact with which this channel communicates for its lifetime, if applicable.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return A pointer to the Contact object, or a null ContactPtr if targetHandleType() is not
+ * #HandleTypeContact.
+ * \sa targetHandle(), targetId()
+ */
+ContactPtr Channel::targetContact() const
+{
+ if (!isReady(Channel::FeatureCore)) {
+ warning() << "Channel::targetContact() used, but the channel is not ready";
+ } else if (targetHandleType() != HandleTypeContact) {
+ warning() << "Channel::targetContact() used with targetHandleType() != Contact";
+ }
+
+ return mPriv->targetContact;
+}
+
+/**
+ * Return whether this channel was created in response to a
+ * local request.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return \c true if the channel was created in response to a local request,
+ * \c false otherwise.
+ */
+bool Channel::isRequested() const
+{
+ if (!isReady(Channel::FeatureCore)) {
+ warning() << "Channel::isRequested() used channel not ready";
+ }
+
+ return mPriv->requested;
+}
+
+/**
+ * Return the contact who initiated this channel.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return A pointer to the Contact object representing the contact who initiated the channel,
+ * or a null ContactPtr if it can't be retrieved.
+ */
+ContactPtr Channel::initiatorContact() const
+{
+ if (!isReady(Channel::FeatureCore)) {
+ warning() << "Channel::initiatorContact() used channel not ready";
+ }
+
+ return mPriv->initiatorContact;
+}
+
+/**
+ * Start an asynchronous request that this channel be closed.
+ *
+ * The returned PendingOperation object will signal the success or failure
+ * of this request; under normal circumstances, it can be expected to
+ * succeed.
+ *
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the call has finished.
+ * \sa requestLeave()
+ */
+PendingOperation *Channel::requestClose()
+{
+ // Closing a channel does not make sense if it is already closed,
+ // just silently Return.
+ if (!isValid()) {
+ return new PendingSuccess(ChannelPtr(this));
+ }
+
+ return new PendingVoid(mPriv->baseInterface->Close(), ChannelPtr(this));
+}
+
+Channel::PendingLeave::PendingLeave(const ChannelPtr &chan, const QString &message,
+ ChannelGroupChangeReason reason)
+ : PendingOperation(chan)
+{
+ Q_ASSERT(chan->mPriv->group != NULL);
+
+ QDBusPendingCall call =
+ chan->mPriv->group->RemoveMembersWithReason(
+ UIntList() << chan->mPriv->groupSelfHandle,
+ message,
+ reason);
+
+ connect(chan.data(),
+ SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
+ this,
+ SLOT(onChanInvalidated(Tp::DBusProxy*)));
+
+ connect(new PendingVoid(call, chan),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ this,
+ SLOT(onRemoveFinished(Tp::PendingOperation*)));
+}
+
+void Channel::PendingLeave::onChanInvalidated(Tp::DBusProxy *proxy)
+{
+ if (isFinished()) {
+ return;
+ }
+
+ debug() << "Finishing PendingLeave successfully as the channel was invalidated";
+
+ setFinished();
+}
+
+void Channel::PendingLeave::onRemoveFinished(Tp::PendingOperation *op)
+{
+ if (isFinished()) {
+ return;
+ }
+
+ ChannelPtr chan = ChannelPtr::staticCast(_object());
+
+ if (op->isValid()) {
+ debug() << "We left the channel" << chan->objectPath();
+
+ ContactPtr c = chan->groupSelfContact();
+
+ if (chan->groupContacts().contains(c)
+ || chan->groupLocalPendingContacts().contains(c)
+ || chan->groupRemotePendingContacts().contains(c)) {
+ debug() << "Waiting for self remove to be picked up";
+ connect(chan.data(),
+ SIGNAL(groupMembersChanged(Tp::Contacts,Tp::Contacts,Tp::Contacts,Tp::Contacts,
+ Tp::Channel::GroupMemberChangeDetails)),
+ this,
+ SLOT(onMembersChanged(Tp::Contacts,Tp::Contacts,Tp::Contacts,Tp::Contacts)));
+ } else {
+ setFinished();
+ }
+
+ return;
+ }
+
+ debug() << "Leave RemoveMembersWithReason failed with " << op->errorName() << op->errorMessage()
+ << "- falling back to Close";
+
+ // If the channel has been closed or otherwise invalidated already in this mainloop iteration,
+ // the requestClose() operation will early-succeed
+ connect(chan->requestClose(),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ this,
+ SLOT(onCloseFinished(Tp::PendingOperation*)));
+}
+
+void Channel::PendingLeave::onMembersChanged(const Tp::Contacts &, const Tp::Contacts &,
+ const Tp::Contacts &, const Tp::Contacts &removed)
+{
+ if (isFinished()) {
+ return;
+ }
+
+ ChannelPtr chan = ChannelPtr::staticCast(_object());
+ ContactPtr c = chan->groupSelfContact();
+
+ if (removed.contains(c)) {
+ debug() << "Leave event picked up for" << chan->objectPath();
+ setFinished();
+ }
+}
+
+void Channel::PendingLeave::onCloseFinished(Tp::PendingOperation *op)
+{
+ if (isFinished()) {
+ return;
+ }
+
+ ChannelPtr chan = ChannelPtr::staticCast(_object());
+
+ if (op->isError()) {
+ warning() << "Closing the channel" << chan->objectPath()
+ << "as a fallback for leaving it failed with"
+ << op->errorName() << op->errorMessage() << "- so didn't leave";
+ setFinishedWithError(op->errorName(), op->errorMessage());
+ } else {
+ debug() << "We left (by closing) the channel" << chan->objectPath();
+ setFinished();
+ }
+}
+
+/**
+ * Start an asynchronous request to leave this channel as gracefully as possible.
+ *
+ * If leaving any more gracefully is not possible, this will revert to the same as requestClose().
+ * In particular, this will be the case for channels with no group interface
+ * (#TP_QT_IFACE_CHANNEL_INTERFACE_GROUP not in the list returned by interfaces()).
+ *
+ * The returned PendingOperation object will signal the success or failure
+ * of this request; under normal circumstances, it can be expected to
+ * succeed.
+ *
+ * A message and a reason may be provided along with the request, which will be sent to the server
+ * if supported, which is indicated by #ChannelGroupFlagMessageDepart and/or
+ * #ChannelGroupFlagMessageReject.
+ *
+ * Attempting to leave again when we have already left, either by our request or forcibly, will be a
+ * no-op, with the returned PendingOperation immediately finishing successfully.
+ *
+ * \param message The message, which can be blank if desired.
+ * \param reason A reason for leaving.
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the call has finished.
+ */
+PendingOperation *Channel::requestLeave(const QString &message, ChannelGroupChangeReason reason)
+{
+ // Leaving a channel does not make sense if it is already closed,
+ // just silently Return.
+ if (!isValid()) {
+ return new PendingSuccess(ChannelPtr(this));
+ }
+
+ if (!isReady(Channel::FeatureCore)) {
+ return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
+ QLatin1String("Channel::FeatureCore must be ready to leave a channel"),
+ ChannelPtr(this));
+ }
+
+ if (!interfaces().contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP)) {
+ return requestClose();
+ }
+
+ ContactPtr self = groupSelfContact();
+
+ if (!groupContacts().contains(self) && !groupLocalPendingContacts().contains(self)
+ && !groupRemotePendingContacts().contains(self)) {
+ debug() << "Channel::requestLeave() called for " << objectPath() <<
+ "which we aren't a member of";
+ return new PendingSuccess(ChannelPtr(this));
+ }
+
+ return new PendingLeave(ChannelPtr(this), message, reason);
+}
+
+/**
+ * \name Group interface
+ *
+ * Cached access to state of the group interface on the associated remote
+ * object, if the interface is present.
+ *
+ * Some methods can be used when targetHandleType() == #HandleTypeContact, such
+ * as groupFlags(), groupCanAddContacts(), groupCanRemoveContacts(),
+ * groupSelfContact() and groupContacts().
+ *
+ * As the group interface state can change freely during the lifetime of the
+ * channel due to events like new contacts joining the group, the cached state
+ * is automatically kept in sync with the remote object's state by hooking
+ * to the change notification signals present in the D-Bus interface.
+ *
+ * As the cached value changes, change notification signals are emitted.
+ *
+ * Signals such as groupMembersChanged(), groupSelfContactChanged(), etc., are emitted to
+ * indicate that properties have changed.
+ *
+ * Check the individual signals' descriptions for details.
+ */
+
+//@{
+
+/**
+ * Return a set of flags indicating the capabilities and behaviour of the
+ * group on this channel.
+ *
+ * Change notification is via the groupFlagsChanged() signal.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return The bitfield combination of flags as #ChannelGroupFlags.
+ * \sa groupFlagsChanged()
+ */
+ChannelGroupFlags Channel::groupFlags() const
+{
+ if (!isReady(Channel::FeatureCore)) {
+ warning() << "Channel::groupFlags() used channel not ready";
+ }
+
+ return (ChannelGroupFlags) mPriv->groupFlags;
+}
+
+/**
+ * Return whether contacts can be added or invited to this channel.
+ *
+ * Change notification is via the groupCanAddContactsChanged() signal.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return \c true if contacts can be added or invited to the channel,
+ * \c false otherwise.
+ * \sa groupFlags(), groupAddContacts()
+ */
+bool Channel::groupCanAddContacts() const
+{
+ if (!isReady(Channel::FeatureCore)) {
+ warning() << "Channel::groupCanAddContacts() used channel not ready";
+ }
+
+ return mPriv->groupFlags & ChannelGroupFlagCanAdd;
+}
+
+/**
+ * Return whether a message is expected when adding/inviting contacts, who
+ * are not already members, to this channel.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return \c true if a message is expected, \c false otherwise.
+ * \sa groupFlags(), groupAddContacts()
+ */
+bool Channel::groupCanAddContactsWithMessage() const
+{
+ if (!isReady(Channel::FeatureCore)) {
+ warning() << "Channel::groupCanAddContactsWithMessage() used when channel not ready";
+ }
+
+ return mPriv->groupFlags & ChannelGroupFlagMessageAdd;
+}
+
+/**
+ * Return whether a message is expected when accepting contacts' requests to
+ * join this channel.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return \c true if a message is expected, \c false otherwise.
+ * \sa groupFlags(), groupAddContacts()
+ */
+bool Channel::groupCanAcceptContactsWithMessage() const
+{
+ if (!isReady(Channel::FeatureCore)) {
+ warning() << "Channel::groupCanAcceptContactsWithMessage() used when channel not ready";
+ }
+
+ return mPriv->groupFlags & ChannelGroupFlagMessageAccept;
+}
+
+/**
+ * Add contacts to this channel.
+ *
+ * Contacts on the local pending list (those waiting for permission to join
+ * the channel) can always be added. If groupCanAcceptContactsWithMessage()
+ * returns \c true, an optional message is expected when doing this; if not,
+ * the message parameter is likely to be ignored (so the user should not be
+ * asked for a message, and the message parameter should be left empty).
+ *
+ * Other contacts can only be added if groupCanAddContacts() returns \c true.
+ * If groupCanAddContactsWithMessage() returns \c true, an optional message is
+ * expected when doing this, and if not, the message parameter is likely to be
+ * ignored.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \param contacts Contacts to be added.
+ * \param message A string message, which can be blank if desired.
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the call has finished.
+ * \sa groupCanAddContacts(), groupCanAddContactsWithMessage(), groupCanAcceptContactsWithMessage()
+ */
+PendingOperation *Channel::groupAddContacts(const QList<ContactPtr> &contacts,
+ const QString &message)
+{
+ if (!isReady(Channel::FeatureCore)) {
+ warning() << "Channel::groupAddContacts() used channel not ready";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Channel not ready"),
+ ChannelPtr(this));
+ } else if (contacts.isEmpty()) {
+ warning() << "Channel::groupAddContacts() used with empty contacts param";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT),
+ QLatin1String("contacts cannot be an empty list"),
+ ChannelPtr(this));
+ }
+
+ foreach (const ContactPtr &contact, contacts) {
+ if (!contact) {
+ warning() << "Channel::groupAddContacts() used but contacts param contains "
+ "invalid contact";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT),
+ QLatin1String("Unable to add invalid contacts"),
+ ChannelPtr(this));
+ }
+ }
+
+ if (!interfaces().contains(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_GROUP))) {
+ warning() << "Channel::groupAddContacts() used with no group interface";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("Channel does not support group interface"),
+ ChannelPtr(this));
+ }
+
+ UIntList handles;
+ foreach (const ContactPtr &contact, contacts) {
+ handles << contact->handle()[0];
+ }
+ return new PendingVoid(mPriv->group->AddMembers(handles, message), ChannelPtr(this));
+}
+
+/**
+ * Return whether contacts in groupRemotePendingContacts() can be removed from
+ * this channel (i.e. whether an invitation can be rescinded).
+ *
+ * Change notification is via the groupCanRescindContactsChanged() signal.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return \c true if contacts can be removed, \c false otherwise.
+ * \sa groupFlags(), groupRemoveContacts()
+ */
+bool Channel::groupCanRescindContacts() const
+{
+ if (!isReady(Channel::FeatureCore)) {
+ warning() << "Channel::groupCanRescindContacts() used channel not ready";
+ }
+
+ return mPriv->groupFlags & ChannelGroupFlagCanRescind;
+}
+
+/**
+ * Return whether a message is expected when removing contacts who are in
+ * groupRemotePendingContacts() from this channel (i.e. rescinding an
+ * invitation).
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return \c true if a message is expected, \c false otherwise.
+ * \sa groupFlags(), groupRemoveContacts()
+ */
+bool Channel::groupCanRescindContactsWithMessage() const
+{
+ if (!isReady(Channel::FeatureCore)) {
+ warning() << "Channel::groupCanRescindContactsWithMessage() used when channel not ready";
+ }
+
+ return mPriv->groupFlags & ChannelGroupFlagMessageRescind;
+}
+
+/**
+ * Return if contacts in groupContacts() can be removed from this channel.
+ *
+ * Note that contacts in local pending lists, and the groupSelfContact(), can
+ * always be removed from the channel.
+ *
+ * Change notification is via the groupCanRemoveContactsChanged() signal.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return \c true if contacts can be removed, \c false otherwise.
+ * \sa groupFlags(), groupRemoveContacts()
+ */
+bool Channel::groupCanRemoveContacts() const
+{
+ if (!isReady(Channel::FeatureCore)) {
+ warning() << "Channel::groupCanRemoveContacts() used channel not ready";
+ }
+
+ return mPriv->groupFlags & ChannelGroupFlagCanRemove;
+}
+
+/**
+ * Return whether a message is expected when removing contacts who are in
+ * groupContacts() from this channel.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return \c true if a message is expected, \c false otherwise.
+ * \sa groupFlags(), groupRemoveContacts()
+ */
+bool Channel::groupCanRemoveContactsWithMessage() const
+{
+ if (!isReady(Channel::FeatureCore)) {
+ warning() << "Channel::groupCanRemoveContactsWithMessage() used when channel not ready";
+ }
+
+ return mPriv->groupFlags & ChannelGroupFlagMessageRemove;
+}
+
+/**
+ * Return whether a message is expected when removing contacts who are in
+ * groupLocalPendingContacts() from this channel (i.e. rejecting a request to
+ * join).
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return \c true if a message is expected, \c false otherwise.
+ * \sa groupFlags(), groupRemoveContacts()
+ */
+bool Channel::groupCanRejectContactsWithMessage() const
+{
+ if (!isReady(Channel::FeatureCore)) {
+ warning() << "Channel::groupCanRejectContactsWithMessage() used when channel not ready";
+ }
+
+ return mPriv->groupFlags & ChannelGroupFlagMessageReject;
+}
+
+/**
+ * Return whether a message is expected when removing the groupSelfContact()
+ * from this channel (i.e. departing from the channel).
+ *
+ * \return \c true if a message is expected, \c false otherwise.
+ * \sa groupFlags(), groupRemoveContacts()
+ */
+bool Channel::groupCanDepartWithMessage() const
+{
+ if (!isReady(Channel::FeatureCore)) {
+ warning() << "Channel::groupCanDepartWithMessage() used when channel not ready";
+ }
+
+ return mPriv->groupFlags & ChannelGroupFlagMessageDepart;
+}
+
+/**
+ * Remove contacts from this channel.
+ *
+ * Contacts on the local pending list (those waiting for permission to join
+ * the channel) can always be removed. If groupCanRejectContactsWithMessage()
+ * returns \c true, an optional message is expected when doing this; if not,
+ * the message parameter is likely to be ignored (so the user should not be
+ * asked for a message, and the message parameter should be left empty).
+ *
+ * The groupSelfContact() can also always be removed, as a way to leave the
+ * group with an optional departure message and/or departure reason indication.
+ * If groupCanDepartWithMessage() returns \c true, an optional message is
+ * expected when doing this, and if not, the message parameter is likely to
+ * be ignored.
+ *
+ * Contacts in the group can only be removed (e.g. kicked) if
+ * groupCanRemoveContacts() returns \c true. If
+ * groupCanRemoveContactsWithMessage() returns \c true, an optional message is
+ * expected when doing this, and if not, the message parameter is likely to be
+ * ignored.
+ *
+ * Contacts in the remote pending list (those who have been invited to the
+ * channel) can only be removed (have their invitations rescinded) if
+ * groupCanRescindContacts() returns \c true. If
+ * groupCanRescindContactsWithMessage() returns \c true, an optional message is
+ * expected when doing this, and if not, the message parameter is likely to be
+ * ignored.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \param contacts Contacts to be removed.
+ * \param message A string message, which can be blank if desired.
+ * \param reason Reason of the change, as specified in
+ * #ChannelGroupChangeReason
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the call has finished.
+ * \sa groupCanRemoveContacts(), groupCanRemoveContactsWithMessage(),
+ * groupCanRejectContactsWithMessage(), groupCanRescindContacts(),
+ * groupCanRescindContacts(), groupCanRescindContactsWithMessage(),
+ * groupCanDepartWithMessage()
+ */
+PendingOperation *Channel::groupRemoveContacts(const QList<ContactPtr> &contacts,
+ const QString &message, ChannelGroupChangeReason reason)
+{
+ if (!isReady(Channel::FeatureCore)) {
+ warning() << "Channel::groupRemoveContacts() used channel not ready";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Channel not ready"),
+ ChannelPtr(this));
+ }
+
+ if (contacts.isEmpty()) {
+ warning() << "Channel::groupRemoveContacts() used with empty contacts param";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT),
+ QLatin1String("contacts param cannot be an empty list"),
+ ChannelPtr(this));
+ }
+
+ foreach (const ContactPtr &contact, contacts) {
+ if (!contact) {
+ warning() << "Channel::groupRemoveContacts() used but contacts param contains "
+ "invalid contact:";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT),
+ QLatin1String("Unable to remove invalid contacts"),
+ ChannelPtr(this));
+ }
+ }
+
+ if (!interfaces().contains(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_GROUP))) {
+ warning() << "Channel::groupRemoveContacts() used with no group interface";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("Channel does not support group interface"),
+ ChannelPtr(this));
+ }
+
+ UIntList handles;
+ foreach (const ContactPtr &contact, contacts) {
+ handles << contact->handle()[0];
+ }
+ return new PendingVoid(
+ mPriv->group->RemoveMembersWithReason(handles, message, reason),
+ ChannelPtr(this));
+}
+
+/**
+ * Return the current contacts of the group.
+ *
+ * Change notification is via the groupMembersChanged() signal.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return A set of pointers to the Contact objects.
+ * \sa groupLocalPendingContacts(), groupRemotePendingContacts()
+ */
+Contacts Channel::groupContacts() const
+{
+ if (!isReady(Channel::FeatureCore)) {
+ warning() << "Channel::groupMembers() used channel not ready";
+ }
+
+ return mPriv->groupContacts.values().toSet();
+}
+
+/**
+ * Return the contacts currently waiting for local approval to join the
+ * group.
+ *
+ * Change notification is via the groupMembersChanged() signal.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return A set of pointers to the Contact objects.
+ * \sa groupContacts(), groupRemotePendingContacts()
+ */
+Contacts Channel::groupLocalPendingContacts() const
+{
+ if (!isReady(Channel::FeatureCore)) {
+ warning() << "Channel::groupLocalPendingContacts() used channel not ready";
+ } else if (!interfaces().contains(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_GROUP))) {
+ warning() << "Channel::groupLocalPendingContacts() used with no group interface";
+ }
+
+ return mPriv->groupLocalPendingContacts.values().toSet();
+}
+
+/**
+ * Return the contacts currently waiting for remote approval to join the
+ * group.
+ *
+ * Change notification is via the groupMembersChanged() signal.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return A set of pointers to the Contact objects.
+ * \sa groupContacts(), groupLocalPendingContacts()
+ */
+Contacts Channel::groupRemotePendingContacts() const
+{
+ if (!isReady(Channel::FeatureCore)) {
+ warning() << "Channel::groupRemotePendingContacts() used channel not ready";
+ } else if (!interfaces().contains(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_GROUP))) {
+ warning() << "Channel::groupRemotePendingContacts() used with no "
+ "group interface";
+ }
+
+ return mPriv->groupRemotePendingContacts.values().toSet();
+}
+
+/**
+ * Return information of a local pending contact change. If
+ * no information is available, an object for which
+ * GroupMemberChangeDetails::isValid() returns <code>false</code> is returned.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \param contact A Contact object that is on the local pending contacts list.
+ * \return The change info as a GroupMemberChangeDetails object.
+ */
+Channel::GroupMemberChangeDetails Channel::groupLocalPendingContactChangeInfo(
+ const ContactPtr &contact) const
+{
+ if (!isReady(Channel::FeatureCore)) {
+ warning() << "Channel::groupLocalPendingContactChangeInfo() used channel not ready";
+ } else if (!interfaces().contains(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_GROUP))) {
+ warning() << "Channel::groupLocalPendingContactChangeInfo() used with no group interface";
+ } else if (!contact) {
+ warning() << "Channel::groupLocalPendingContactChangeInfo() used with null contact param";
+ return GroupMemberChangeDetails();
+ }
+
+ uint handle = contact->handle()[0];
+ return mPriv->groupLocalPendingContactsChangeInfo.value(handle);
+}
+
+/**
+ * Return information on the removal of the local user from the group. If
+ * the user hasn't been removed from the group, an object for which
+ * GroupMemberChangeDetails::isValid() returns <code>false</code> is returned.
+ *
+ * This method should be called only after you've left the channel.
+ * This is useful for getting the remove information after missing the
+ * corresponding groupMembersChanged() signal, as the local user being
+ * removed usually causes the channel to be closed.
+ *
+ * The returned information is not guaranteed to be correct if
+ * groupIsSelfHandleTracked() returns false and a self handle change has
+ * occurred on the remote object.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return The remove info as a GroupMemberChangeDetails object.
+ */
+Channel::GroupMemberChangeDetails Channel::groupSelfContactRemoveInfo() const
+{
+ // Oftentimes, the channel will be closed as a result from being left - so checking a channel's
+ // self remove info when it has been closed and hence invalidated is valid
+ if (isValid() && !isReady(Channel::FeatureCore)) {
+ warning() << "Channel::groupSelfContactRemoveInfo() used before Channel::FeatureCore is ready";
+ } else if (!interfaces().contains(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_GROUP))) {
+ warning() << "Channel::groupSelfContactRemoveInfo() used with "
+ "no group interface";
+ }
+
+ return mPriv->groupSelfContactRemoveInfo;
+}
+
+/**
+ * Return whether globally valid handles can be looked up using the
+ * channel-specific handle on this channel using this object.
+ *
+ * Handle owner lookup is only available if:
+ * <ul>
+ * <li>The object is ready
+ * <li>The list returned by interfaces() contains #TP_QT_IFACE_CHANNEL_INTERFACE_GROUP</li>
+ * <li>The set of flags returned by groupFlags() contains
+ * #GroupFlagProperties and #GroupFlagChannelSpecificHandles</li>
+ * </ul>
+ *
+ * If this function returns \c false, the return value of
+ * groupHandleOwners() is undefined and groupHandleOwnersChanged() will
+ * never be emitted.
+ *
+ * The value returned by this function will stay fixed for the entire time
+ * the object is ready, so no change notification is provided.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return \c true if handle owner lookup functionality is available, \c false otherwise.
+ */
+bool Channel::groupAreHandleOwnersAvailable() const
+{
+ if (!isReady(Channel::FeatureCore)) {
+ warning() << "Channel::groupAreHandleOwnersAvailable() used channel not ready";
+ } else if (!interfaces().contains(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_GROUP))) {
+ warning() << "Channel::groupAreHandleOwnersAvailable() used with "
+ "no group interface";
+ }
+
+ return mPriv->groupAreHandleOwnersAvailable;
+}
+
+/**
+ * Return a mapping of handles specific to this channel to globally valid
+ * handles.
+ *
+ * The mapping includes at least all of the channel-specific handles in this
+ * channel's members, local-pending and remote-pending sets as keys. Any
+ * handle not in the keys of this mapping is not channel-specific in this
+ * channel. Handles which are channel-specific, but for which the owner is
+ * unknown, appear in this mapping with 0 as owner.
+ *
+ * Change notification is via the groupHandleOwnersChanged() signal.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return A mapping from group-specific handles to globally valid handles.
+ */
+HandleOwnerMap Channel::groupHandleOwners() const
+{
+ if (!isReady(Channel::FeatureCore)) {
+ warning() << "Channel::groupHandleOwners() used channel not ready";
+ } else if (!interfaces().contains(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_GROUP))) {
+ warning() << "Channel::groupAreHandleOwnersAvailable() used with no "
+ "group interface";
+ }
+ else if (!groupAreHandleOwnersAvailable()) {
+ warning() << "Channel::groupAreHandleOwnersAvailable() used, but handle "
+ "owners not available";
+ }
+
+ return mPriv->groupHandleOwners;
+}
+
+/**
+ * Return whether the value returned by groupSelfContact() is guaranteed to
+ * accurately represent the local user even after nickname changes, etc.
+ *
+ * This should always be \c true for new services implementing the group interface.
+ *
+ * Older services not providing group properties don't necessarily
+ * emit the SelfHandleChanged signal either, so self contact changes can't be
+ * reliably tracked.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return \c true if changes to the self contact are tracked, \c false otherwise.
+ */
+bool Channel::groupIsSelfContactTracked() const
+{
+ if (!isReady(Channel::FeatureCore)) {
+ warning() << "Channel::groupIsSelfHandleTracked() used channel not ready";
+ } else if (!interfaces().contains(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_GROUP))) {
+ warning() << "Channel::groupIsSelfHandleTracked() used with "
+ "no group interface";
+ }
+
+ return mPriv->groupIsSelfHandleTracked;
+}
+
+/**
+ * Return a Contact object representing the user in the group if at all possible, otherwise a
+ * Contact object representing the user globally.
+ *
+ * Change notification is via the groupSelfContactChanged() signal.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return A pointer to the Contact object.
+ */
+ContactPtr Channel::groupSelfContact() const
+{
+ if (!isReady(Channel::FeatureCore)) {
+ warning() << "Channel::groupSelfContact() used channel not ready";
+ }
+
+ return mPriv->groupSelfContact;
+}
+
+/**
+ * Return whether the local user is in the "local pending" state. This
+ * indicates that the local user needs to take action to accept an invitation,
+ * an incoming call, etc.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return \c true if local user is in the channel's local-pending set, \c false otherwise.
+ */
+bool Channel::groupSelfHandleIsLocalPending() const
+{
+ if (!isReady(Channel::FeatureCore)) {
+ warning() << "Channel::groupSelfHandleIsLocalPending() used when "
+ "channel not ready";
+ return false;
+ }
+
+ return mPriv->groupLocalPendingContacts.contains(mPriv->groupSelfHandle);
+}
+
+/**
+ * Attempt to add the local user to this channel. In some channel types,
+ * such as Text and StreamedMedia, this is used to accept an invitation or an
+ * incoming call.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the call has finished.
+ */
+PendingOperation *Channel::groupAddSelfHandle()
+{
+ if (!isReady(Channel::FeatureCore)) {
+ warning() << "Channel::groupAddSelfHandle() used when channel not "
+ "ready";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT),
+ QLatin1String("Channel object not ready"),
+ ChannelPtr(this));
+ }
+
+ UIntList handles;
+
+ if (mPriv->groupSelfHandle == 0) {
+ handles << mPriv->connection->selfHandle();
+ } else {
+ handles << mPriv->groupSelfHandle;
+ }
+
+ return new PendingVoid(
+ mPriv->group->AddMembers(handles, QLatin1String("")),
+ ChannelPtr(this));
+}
+
+//@}
+
+/**
+ * Return whether this channel implements the conference interface
+ * (#TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE is in the list returned by interfaces()).
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return \c true if the conference interface is supported, \c false otherwise.
+ */
+bool Channel::isConference() const
+{
+ return hasInterface(TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE);
+}
+
+/**
+ * Return a list of contacts invited to this conference when it was created.
+ *
+ * This method requires Channel::FeatureConferenceInitialInviteeContacts to be ready.
+ *
+ * \return A set of pointers to the Contact objects.
+ */
+Contacts Channel::conferenceInitialInviteeContacts() const
+{
+ return mPriv->conferenceInitialInviteeContacts;
+}
+
+/**
+ * Return the individual channels that are part of this conference.
+ *
+ * Change notification is via the conferenceChannelMerged() and
+ * conferenceChannelRemoved() signals.
+ *
+ * Note that the returned channels are not guaranteed to be ready. Calling
+ * Channel::becomeReady() may be needed.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return A list of pointers to Channel objects containing all channels in the conference.
+ * \sa conferenceInitialChannels(), conferenceOriginalChannels()
+ */
+QList<ChannelPtr> Channel::conferenceChannels() const
+{
+ return mPriv->conferenceChannels.values();
+}
+
+/**
+ * Return the initial value of conferenceChannels().
+ *
+ * Note that the returned channels are not guaranteed to be ready. Calling
+ * Channel::becomeReady() may be needed.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return A list of pointers to Channel objects containing all channels that were initially
+ * part of the conference.
+ * \sa conferenceChannels(), conferenceOriginalChannels()
+ */
+QList<ChannelPtr> Channel::conferenceInitialChannels() const
+{
+ return mPriv->conferenceInitialChannels.values();
+}
+
+/**
+ * Return a map between channel specific handles and the corresponding channels of this conference.
+ *
+ * This method is only relevant on GSM conference calls where it is possible to have the same phone
+ * number in a conference twice; for instance, it could be the number of a corporate switchboard.
+ * This is represented using channel-specific handles; whether or not a channel uses
+ * channel-specific handles is reported in groupFlags(). The groupHandleOwners() specifies the
+ * mapping from opaque channel-specific handles to actual numbers; this property specifies the
+ * original 1-1 channel corresponding to each channel-specific handle in the conference.
+ *
+ * In protocols where this situation cannot arise, such as XMPP, this method will return an empty
+ * hash.
+ *
+ * Example, consider this situation:
+ * 1. Place a call (with path /call/to/simon) to the contact +441234567890 (which is assigned the
+ * handle h, say), and ask to be put through to Simon McVittie;
+ * 2. Put that call on hold;
+ * 3. Place another call (with path /call/to/jonny) to +441234567890, and ask to be put through to
+ * Jonny Lamb;
+ * 4. Request a new conference channel with initial channels: ['/call/to/simon', '/call/to/jonny'].
+ *
+ * The new channel will have the following properties, for some handles s and j:
+ *
+ * {
+ * groupFlags(): ChannelGroupFlagChannelSpecificHandles | (other flags),
+ * groupMembers(): [self handle, s, j],
+ * groupHandleOwners(): { s: h, j: h },
+ * conferenceInitialChannels(): ['/call/to/simon', '/call/to/jonny'],
+ * conferenceChannels(): ['/call/to/simon', '/call/to/jonny'],
+ * conferenceOriginalChannels(): { s: '/call/to/simon', j: '/call/to/jonny' },
+ * # ...
+ * }
+ *
+ * Note that the returned channels are not guaranteed to be ready. Calling
+ * Channel::becomeReady() may be needed.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return A map of channel specific handles to pointers to Channel objects.
+ * \sa conferenceChannels(), conferenceInitialChannels()
+ */
+QHash<uint, ChannelPtr> Channel::conferenceOriginalChannels() const
+{
+ return mPriv->conferenceOriginalChannels;
+}
+
+/**
+ * Return whether this channel supports conference merging using conferenceMergeChannel().
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return \c true if the interface is supported, \c false otherwise.
+ * \sa conferenceMergeChannel()
+ */
+bool Channel::supportsConferenceMerging() const
+{
+ return interfaces().contains(QLatin1String(
+ TP_FUTURE_INTERFACE_CHANNEL_INTERFACE_MERGEABLE_CONFERENCE));
+}
+
+/**
+ * Request that the given channel be incorporated into this channel.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the call has finished.
+ * \sa supportsConferenceMerging()
+ */
+PendingOperation *Channel::conferenceMergeChannel(const ChannelPtr &channel)
+{
+ if (!supportsConferenceMerging()) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("Channel does not support MergeableConference interface"),
+ ChannelPtr(this));
+ }
+
+ return new PendingVoid(mPriv->mergeableConferenceInterface()->Merge(
+ QDBusObjectPath(channel->objectPath())),
+ ChannelPtr(this));
+}
+
+/**
+ * Return whether this channel supports splitting using conferenceSplitChannel().
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return \c true if the interface is supported, \c false otherwise.
+ * \sa conferenceSplitChannel()
+ */
+bool Channel::supportsConferenceSplitting() const
+{
+ return interfaces().contains(QLatin1String(
+ TP_FUTURE_INTERFACE_CHANNEL_INTERFACE_SPLITTABLE));
+}
+
+/**
+ * Request that this channel is removed from any conference of which it is
+ * a part.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the call has finished.
+ * \sa supportsConferenceSplitting()
+ */
+PendingOperation *Channel::conferenceSplitChannel()
+{
+ if (!supportsConferenceSplitting()) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("Channel does not support Splittable interface"),
+ ChannelPtr(this));
+ }
+
+ return new PendingVoid(mPriv->splittableInterface()->Split(), ChannelPtr(this));
+}
+
+/**
+ * Return the Client::ChannelInterface interface proxy object for this channel.
+ * This method is protected since the convenience methods provided by this
+ * class should generally be used instead of calling D-Bus methods
+ * directly.
+ *
+ * \return A pointer to the existing Client::ChannelInterface object for this
+ * Channel object.
+ */
+Client::ChannelInterface *Channel::baseInterface() const
+{
+ return mPriv->baseInterface;
+}
+
+void Channel::gotMainProperties(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QVariantMap> reply = *watcher;
+ QVariantMap props;
+
+ if (!reply.isError()) {
+ debug() << "Got reply to Properties::GetAll(Channel)";
+ props = reply.value();
+ } else {
+ warning().nospace() << "Properties::GetAll(Channel) failed with " <<
+ reply.error().name() << ": " << reply.error().message();
+ }
+
+ mPriv->extractMainProps(props);
+
+ mPriv->continueIntrospection();
+}
+
+void Channel::gotChannelType(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QString> reply = *watcher;
+
+ if (reply.isError()) {
+ warning().nospace() << "Channel::GetChannelType() failed with " <<
+ reply.error().name() << ": " << reply.error().message() <<
+ ", Channel officially dead";
+ invalidate(reply.error());
+ return;
+ }
+
+ debug() << "Got reply to fallback Channel::GetChannelType()";
+ mPriv->channelType = reply.value();
+ mPriv->continueIntrospection();
+}
+
+void Channel::gotHandle(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<uint, uint> reply = *watcher;
+
+ if (reply.isError()) {
+ warning().nospace() << "Channel::GetHandle() failed with " <<
+ reply.error().name() << ": " << reply.error().message() <<
+ ", Channel officially dead";
+ invalidate(reply.error());
+ return;
+ }
+
+ debug() << "Got reply to fallback Channel::GetHandle()";
+ mPriv->targetHandleType = reply.argumentAt<0>();
+ mPriv->targetHandle = reply.argumentAt<1>();
+ mPriv->continueIntrospection();
+}
+
+void Channel::gotInterfaces(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QStringList> reply = *watcher;
+
+ if (reply.isError()) {
+ warning().nospace() << "Channel::GetInterfaces() failed with " <<
+ reply.error().name() << ": " << reply.error().message() <<
+ ", Channel officially dead";
+ invalidate(reply.error());
+ return;
+ }
+
+ debug() << "Got reply to fallback Channel::GetInterfaces()";
+ setInterfaces(reply.value());
+ mPriv->readinessHelper->setInterfaces(interfaces());
+ mPriv->nowHaveInterfaces();
+
+ mPriv->fakeGroupInterfaceIfNeeded();
+
+ mPriv->continueIntrospection();
+}
+
+void Channel::onClosed()
+{
+ debug() << "Got Channel::Closed";
+
+ QString error;
+ QString message;
+ if (mPriv->groupSelfContactRemoveInfo.isValid() &&
+ mPriv->groupSelfContactRemoveInfo.hasReason()) {
+ error = mPriv->groupMemberChangeDetailsTelepathyError(
+ mPriv->groupSelfContactRemoveInfo);
+ message = mPriv->groupSelfContactRemoveInfo.message();
+ } else {
+ error = TP_QT_ERROR_CANCELLED;
+ message = QLatin1String("channel closed");
+ }
+
+ invalidate(error, message);
+}
+
+void Channel::onConnectionReady(PendingOperation *op)
+{
+ if (op->isError()) {
+ invalidate(op->errorName(), op->errorMessage());
+ return;
+ }
+
+ // FIXME: should connect to selfHandleChanged and act accordingly, but that is a PITA for
+ // keeping the Contacts built and even if we don't do it, the new code is better than the
+ // old one anyway because earlier on we just wouldn't have had a self contact.
+ //
+ // besides, the only thing which breaks without connecting in the world likely is if you're
+ // using idle and decide to change your nick, which I don't think we necessarily even have API
+ // to do from tp-qt4 anyway (or did I make idle change the nick when setting your alias? can't
+ // remember)
+ //
+ // Simply put, I just don't care ATM.
+
+ // Will be overwritten by the group self handle, if we can discover any.
+ Q_ASSERT(!mPriv->groupSelfHandle);
+ mPriv->groupSelfHandle = mPriv->connection->selfHandle();
+
+ mPriv->introspectMainProperties();
+}
+
+void Channel::onConnectionInvalidated()
+{
+ debug() << "Owning connection died leaving an orphan Channel, "
+ "changing to closed";
+ invalidate(TP_QT_ERROR_ORPHANED,
+ QLatin1String("Connection given as the owner of this channel was invalidated"));
+}
+
+void Channel::gotGroupProperties(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QVariantMap> reply = *watcher;
+ QVariantMap props;
+
+ if (!reply.isError()) {
+ debug() << "Got reply to Properties::GetAll(Channel.Interface.Group)";
+ props = reply.value();
+ }
+ else {
+ warning().nospace() << "Properties::GetAll(Channel.Interface.Group) "
+ "failed with " << reply.error().name() << ": " <<
+ reply.error().message();
+ }
+
+ mPriv->extract0176GroupProps(props);
+ // Add extraction (and possible fallbacks) in similar functions, called from here
+
+ mPriv->continueIntrospection();
+}
+
+void Channel::gotGroupFlags(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<uint> reply = *watcher;
+
+ if (reply.isError()) {
+ warning().nospace() << "Channel.Interface.Group::GetGroupFlags() failed with " <<
+ reply.error().name() << ": " << reply.error().message();
+ }
+ else {
+ debug() << "Got reply to fallback Channel.Interface.Group::GetGroupFlags()";
+ mPriv->setGroupFlags(reply.value());
+
+ if (mPriv->groupFlags & ChannelGroupFlagProperties) {
+ warning() << " Reply included ChannelGroupFlagProperties, even "
+ "though properties specified in 0.17.7 didn't work! - unsetting";
+ mPriv->groupFlags &= ~ChannelGroupFlagProperties;
+ }
+ }
+
+ mPriv->continueIntrospection();
+}
+
+void Channel::gotAllMembers(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<UIntList, UIntList, UIntList> reply = *watcher;
+
+ if (reply.isError()) {
+ warning().nospace() << "Channel.Interface.Group::GetAllMembers() failed with " <<
+ reply.error().name() << ": " << reply.error().message();
+ } else {
+ debug() << "Got reply to fallback Channel.Interface.Group::GetAllMembers()";
+
+ mPriv->groupInitialMembers = reply.argumentAt<0>();
+ mPriv->groupInitialRP = reply.argumentAt<2>();
+
+ foreach (uint handle, reply.argumentAt<1>()) {
+ LocalPendingInfo info = {handle, 0, ChannelGroupChangeReasonNone,
+ QLatin1String("")};
+ mPriv->groupInitialLP.push_back(info);
+ }
+ }
+
+ mPriv->continueIntrospection();
+}
+
+void Channel::gotLocalPendingMembersWithInfo(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<LocalPendingInfoList> reply = *watcher;
+
+ if (reply.isError()) {
+ warning().nospace() << "Channel.Interface.Group::GetLocalPendingMembersWithInfo() "
+ "failed with " << reply.error().name() << ": " << reply.error().message();
+ warning() << " Falling back to what GetAllMembers returned with no extended info";
+ }
+ else {
+ debug() << "Got reply to fallback "
+ "Channel.Interface.Group::GetLocalPendingMembersWithInfo()";
+ // Overrides the previous vague list provided by gotAllMembers
+ mPriv->groupInitialLP = reply.value();
+ }
+
+ mPriv->continueIntrospection();
+}
+
+void Channel::gotSelfHandle(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<uint> reply = *watcher;
+
+ if (reply.isError()) {
+ warning().nospace() << "Channel.Interface.Group::GetSelfHandle() failed with " <<
+ reply.error().name() << ": " << reply.error().message();
+ } else {
+ debug() << "Got reply to fallback Channel.Interface.Group::GetSelfHandle()";
+ // Don't overwrite the self handle we got from the connection with 0
+ if (reply.value()) {
+ mPriv->groupSelfHandle = reply.value();
+ }
+ }
+
+ mPriv->nowHaveInitialMembers();
+
+ mPriv->continueIntrospection();
+}
+
+void Channel::gotContacts(PendingOperation *op)
+{
+ PendingContacts *pending = qobject_cast<PendingContacts *>(op);
+
+ mPriv->buildingContacts = false;
+
+ QList<ContactPtr> contacts;
+ if (pending->isValid()) {
+ contacts = pending->contacts();
+
+ if (!pending->invalidHandles().isEmpty()) {
+ warning() << "Unable to construct Contact objects for handles:" <<
+ pending->invalidHandles();
+
+ if (mPriv->groupSelfHandle &&
+ pending->invalidHandles().contains(mPriv->groupSelfHandle)) {
+ warning() << "Unable to retrieve self contact";
+ mPriv->groupSelfContact.reset();
+ emit groupSelfContactChanged();
+ }
+ }
+ } else {
+ warning().nospace() << "Getting contacts failed with " <<
+ pending->errorName() << ":" << pending->errorMessage();
+ }
+
+ mPriv->updateContacts(contacts);
+}
+
+void Channel::onGroupFlagsChanged(uint added, uint removed)
+{
+ debug().nospace() << "Got Channel.Interface.Group::GroupFlagsChanged(" <<
+ hex << added << ", " << removed << ")";
+
+ added &= ~(mPriv->groupFlags);
+ removed &= mPriv->groupFlags;
+
+ debug().nospace() << "Arguments after filtering (" << hex << added <<
+ ", " << removed << ")";
+
+ uint groupFlags = mPriv->groupFlags;
+ groupFlags |= added;
+ groupFlags &= ~removed;
+ // just emit groupFlagsChanged and related signals if the flags really
+ // changed and we are ready
+ if (mPriv->setGroupFlags(groupFlags) && isReady(Channel::FeatureCore)) {
+ debug() << "Emitting groupFlagsChanged with" << mPriv->groupFlags <<
+ "value" << added << "added" << removed << "removed";
+ emit groupFlagsChanged((ChannelGroupFlags) mPriv->groupFlags,
+ (ChannelGroupFlags) added, (ChannelGroupFlags) removed);
+
+ if (added & ChannelGroupFlagCanAdd ||
+ removed & ChannelGroupFlagCanAdd) {
+ debug() << "Emitting groupCanAddContactsChanged";
+ emit groupCanAddContactsChanged(groupCanAddContacts());
+ }
+
+ if (added & ChannelGroupFlagCanRemove ||
+ removed & ChannelGroupFlagCanRemove) {
+ debug() << "Emitting groupCanRemoveContactsChanged";
+ emit groupCanRemoveContactsChanged(groupCanRemoveContacts());
+ }
+
+ if (added & ChannelGroupFlagCanRescind ||
+ removed & ChannelGroupFlagCanRescind) {
+ debug() << "Emitting groupCanRescindContactsChanged";
+ emit groupCanRescindContactsChanged(groupCanRescindContacts());
+ }
+ }
+}
+
+void Channel::onMembersChanged(const QString &message,
+ const UIntList &added, const UIntList &removed,
+ const UIntList &localPending, const UIntList &remotePending,
+ uint actor, uint reason)
+{
+ // Ignore the signal if we're using the MCD signal to not duplicate events
+ if (mPriv->usingMembersChangedDetailed) {
+ return;
+ }
+
+ debug() << "Got Channel.Interface.Group::MembersChanged with" << added.size() <<
+ "added," << removed.size() << "removed," << localPending.size() <<
+ "moved to LP," << remotePending.size() << "moved to RP," << actor <<
+ "being the actor," << reason << "the reason and" << message << "the message";
+ debug() << " synthesizing a corresponding MembersChangedDetailed signal";
+
+ QVariantMap details;
+
+ if (!message.isEmpty()) {
+ details.insert(QLatin1String("message"), message);
+ }
+
+ if (actor != 0) {
+ details.insert(QLatin1String("actor"), actor);
+ }
+
+ details.insert(QLatin1String("change-reason"), reason);
+
+ mPriv->doMembersChangedDetailed(added, removed, localPending, remotePending, details);
+}
+
+void Channel::onMembersChangedDetailed(
+ const UIntList &added, const UIntList &removed,
+ const UIntList &localPending, const UIntList &remotePending,
+ const QVariantMap &details)
+{
+ // Ignore the signal if we aren't (yet) using MCD to not duplicate events
+ if (!mPriv->usingMembersChangedDetailed) {
+ return;
+ }
+
+ debug() << "Got Channel.Interface.Group::MembersChangedDetailed with" << added.size() <<
+ "added," << removed.size() << "removed," << localPending.size() <<
+ "moved to LP," << remotePending.size() << "moved to RP and with" << details.size() <<
+ "details";
+
+ mPriv->doMembersChangedDetailed(added, removed, localPending, remotePending, details);
+}
+
+void Channel::Private::doMembersChangedDetailed(
+ const UIntList &added, const UIntList &removed,
+ const UIntList &localPending, const UIntList &remotePending,
+ const QVariantMap &details)
+{
+ if (!groupHaveMembers) {
+ debug() << "Still waiting for initial group members, "
+ "so ignoring delta signal...";
+ return;
+ }
+
+ if (added.isEmpty() && removed.isEmpty() &&
+ localPending.isEmpty() && remotePending.isEmpty()) {
+ debug() << "Nothing really changed, so skipping membersChanged";
+ return;
+ }
+
+ // let's store groupSelfContactRemoveInfo here as we may not have time
+ // to build the contacts in case self contact is removed,
+ // as Closed will be emitted right after
+ if (removed.contains(groupSelfHandle)) {
+ if (qdbus_cast<uint>(details.value(QLatin1String("change-reason"))) ==
+ ChannelGroupChangeReasonRenamed) {
+ if (removed.size() != 1 ||
+ (added.size() + localPending.size() + remotePending.size()) != 1) {
+ // spec-incompliant CM, ignoring members changed
+ warning() << "Received MembersChangedDetailed with reason "
+ "Renamed and removed.size != 1 or added.size + "
+ "localPending.size + remotePending.size != 1. Ignoring";
+ return;
+ }
+ uint newHandle = 0;
+ if (!added.isEmpty()) {
+ newHandle = added.first();
+ } else if (!localPending.isEmpty()) {
+ newHandle = localPending.first();
+ } else if (!remotePending.isEmpty()) {
+ newHandle = remotePending.first();
+ }
+ parent->onSelfHandleChanged(newHandle);
+ return;
+ }
+
+ // let's try to get the actor contact from contact manager if available
+ groupSelfContactRemoveInfo = GroupMemberChangeDetails(
+ connection->contactManager()->lookupContactByHandle(
+ qdbus_cast<uint>(details.value(QLatin1String("actor")))),
+ details);
+ }
+
+ HandleIdentifierMap contactIds = qdbus_cast<HandleIdentifierMap>(
+ details.value(GroupMembersChangedInfo::keyContactIds));
+ connection->lowlevel()->injectContactIds(contactIds);
+
+ groupMembersChangedQueue.enqueue(
+ new Private::GroupMembersChangedInfo(
+ added, removed,
+ localPending, remotePending,
+ details));
+
+ if (!buildingContacts) {
+ // if we are building contacts, we should wait it to finish so we don't
+ // present the user with wrong information
+ processMembersChanged();
+ }
+}
+
+void Channel::onHandleOwnersChanged(const HandleOwnerMap &added,
+ const UIntList &removed)
+{
+ debug() << "Got Channel.Interface.Group::HandleOwnersChanged with" <<
+ added.size() << "added," << removed.size() << "removed";
+
+ if (!mPriv->groupAreHandleOwnersAvailable) {
+ debug() << "Still waiting for initial handle owners, so ignoring "
+ "delta signal...";
+ return;
+ }
+
+ UIntList emitAdded;
+ UIntList emitRemoved;
+
+ for (HandleOwnerMap::const_iterator i = added.begin();
+ i != added.end();
+ ++i) {
+ uint handle = i.key();
+ uint global = i.value();
+
+ if (!mPriv->groupHandleOwners.contains(handle)
+ || mPriv->groupHandleOwners[handle] != global) {
+ debug() << " +++/changed" << handle << "->" << global;
+ mPriv->groupHandleOwners[handle] = global;
+ emitAdded.append(handle);
+ }
+ }
+
+ foreach (uint handle, removed) {
+ if (mPriv->groupHandleOwners.contains(handle)) {
+ debug() << " ---" << handle;
+ mPriv->groupHandleOwners.remove(handle);
+ emitRemoved.append(handle);
+ }
+ }
+
+ // just emit groupHandleOwnersChanged if it really changed and
+ // we are ready
+ if ((emitAdded.size() || emitRemoved.size()) && isReady(Channel::FeatureCore)) {
+ debug() << "Emitting groupHandleOwnersChanged with" << emitAdded.size() <<
+ "added" << emitRemoved.size() << "removed";
+ emit groupHandleOwnersChanged(mPriv->groupHandleOwners,
+ emitAdded, emitRemoved);
+ }
+}
+
+void Channel::onSelfHandleChanged(uint selfHandle)
+{
+ debug().nospace() << "Got Channel.Interface.Group::SelfHandleChanged";
+
+ if (selfHandle != mPriv->groupSelfHandle) {
+ mPriv->groupSelfHandle = selfHandle;
+ debug() << " Emitting groupSelfHandleChanged with new self handle" <<
+ selfHandle;
+
+ // FIXME: fix self contact building with no group
+ mPriv->pendingRetrieveGroupSelfContact = true;
+ }
+}
+
+void Channel::gotConferenceProperties(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QVariantMap> reply = *watcher;
+ QVariantMap props;
+
+ mPriv->introspectingConference = false;
+
+ if (!reply.isError()) {
+ debug() << "Got reply to Properties::GetAll(Channel.Interface.Conference)";
+ props = reply.value();
+
+ ConnectionPtr conn = connection();
+ ChannelFactoryConstPtr chanFactory = conn->channelFactory();
+
+ ObjectPathList channels =
+ qdbus_cast<ObjectPathList>(props[QLatin1String("Channels")]);
+ foreach (const QDBusObjectPath &channelPath, channels) {
+ if (mPriv->conferenceChannels.contains(channelPath.path())) {
+ continue;
+ }
+
+ PendingReady *readyOp = chanFactory->proxy(conn,
+ channelPath.path(), QVariantMap());
+ ChannelPtr channel(ChannelPtr::qObjectCast(readyOp->proxy()));
+ Q_ASSERT(!channel.isNull());
+
+ mPriv->conferenceChannels.insert(channelPath.path(), channel);
+ }
+
+ ObjectPathList initialChannels =
+ qdbus_cast<ObjectPathList>(props[QLatin1String("InitialChannels")]);
+ foreach (const QDBusObjectPath &channelPath, initialChannels) {
+ if (mPriv->conferenceInitialChannels.contains(channelPath.path())) {
+ continue;
+ }
+
+ PendingReady *readyOp = chanFactory->proxy(conn,
+ channelPath.path(), QVariantMap());
+ ChannelPtr channel(ChannelPtr::qObjectCast(readyOp->proxy()));
+ Q_ASSERT(!channel.isNull());
+
+ mPriv->conferenceInitialChannels.insert(channelPath.path(), channel);
+ }
+
+ mPriv->conferenceInitialInviteeHandles =
+ qdbus_cast<UIntList>(props[QLatin1String("InitialInviteeHandles")]);
+ QStringList conferenceInitialInviteeIds =
+ qdbus_cast<QStringList>(props[QLatin1String("InitialInviteeIDs")]);
+ if (mPriv->conferenceInitialInviteeHandles.size() == conferenceInitialInviteeIds.size()) {
+ HandleIdentifierMap contactIds;
+ int i = 0;
+ foreach (uint handle, mPriv->conferenceInitialInviteeHandles) {
+ contactIds.insert(handle, conferenceInitialInviteeIds.at(i++));
+ }
+ mPriv->connection->lowlevel()->injectContactIds(contactIds);
+ }
+
+ mPriv->conferenceInvitationMessage =
+ qdbus_cast<QString>(props[QLatin1String("InvitationMessage")]);
+
+ ChannelOriginatorMap originalChannels = qdbus_cast<ChannelOriginatorMap>(
+ props[QLatin1String("OriginalChannels")]);
+ for (ChannelOriginatorMap::const_iterator i = originalChannels.constBegin();
+ i != originalChannels.constEnd(); ++i) {
+ PendingReady *readyOp = chanFactory->proxy(conn,
+ i.value().path(), QVariantMap());
+ ChannelPtr channel(ChannelPtr::qObjectCast(readyOp->proxy()));
+ Q_ASSERT(!channel.isNull());
+
+ mPriv->conferenceOriginalChannels.insert(i.key(), channel);
+ }
+ } else {
+ warning().nospace() << "Properties::GetAll(Channel.Interface.Conference) "
+ "failed with " << reply.error().name() << ": " <<
+ reply.error().message();
+ }
+
+ mPriv->continueIntrospection();
+}
+
+void Channel::gotConferenceInitialInviteeContacts(PendingOperation *op)
+{
+ PendingContacts *pending = qobject_cast<PendingContacts *>(op);
+
+ if (pending->isValid()) {
+ mPriv->conferenceInitialInviteeContacts = pending->contacts().toSet();
+ } else {
+ warning().nospace() << "Getting conference initial invitee contacts "
+ "failed with " << pending->errorName() << ":" <<
+ pending->errorMessage();
+ }
+
+ mPriv->readinessHelper->setIntrospectCompleted(
+ FeatureConferenceInitialInviteeContacts, true);
+}
+
+void Channel::onConferenceChannelMerged(const QDBusObjectPath &channelPath,
+ uint channelSpecificHandle, const QVariantMap &properties)
+{
+ if (mPriv->conferenceChannels.contains(channelPath.path())) {
+ return;
+ }
+
+ ConnectionPtr conn = connection();
+ ChannelFactoryConstPtr chanFactory = conn->channelFactory();
+ PendingReady *readyOp = chanFactory->proxy(conn,
+ channelPath.path(), properties);
+ ChannelPtr channel(ChannelPtr::qObjectCast(readyOp->proxy()));
+ Q_ASSERT(!channel.isNull());
+
+ mPriv->conferenceChannels.insert(channelPath.path(), channel);
+ emit conferenceChannelMerged(channel);
+
+ if (channelSpecificHandle != 0) {
+ mPriv->conferenceOriginalChannels.insert(channelSpecificHandle, channel);
+ }
+}
+
+void Channel::onConferenceChannelMerged(const QDBusObjectPath &channelPath)
+{
+ onConferenceChannelMerged(channelPath, 0, QVariantMap());
+}
+
+void Channel::onConferenceChannelRemoved(const QDBusObjectPath &channelPath,
+ const QVariantMap &details)
+{
+ if (!mPriv->conferenceChannels.contains(channelPath.path())) {
+ return;
+ }
+
+ HandleIdentifierMap contactIds = qdbus_cast<HandleIdentifierMap>(
+ details.value(Private::GroupMembersChangedInfo::keyContactIds));
+ mPriv->connection->lowlevel()->injectContactIds(contactIds);
+
+ mPriv->conferenceChannelRemovedQueue.enqueue(
+ new Private::ConferenceChannelRemovedInfo(channelPath, details));
+ mPriv->processConferenceChannelRemoved();
+}
+
+void Channel::onConferenceChannelRemoved(const QDBusObjectPath &channelPath)
+{
+ onConferenceChannelRemoved(channelPath, QVariantMap());
+}
+
+void Channel::gotConferenceChannelRemovedActorContact(PendingOperation *op)
+{
+ ContactPtr actorContact;
+
+ if (op) {
+ PendingContacts *pc = qobject_cast<PendingContacts *>(op);
+
+ if (pc->isValid()) {
+ Q_ASSERT(pc->contacts().size() == 1);
+ actorContact = pc->contacts().first();
+ } else {
+ warning().nospace() << "Getting conference channel removed actor "
+ "failed with " << pc->errorName() << ":" <<
+ pc->errorMessage();
+ }
+ }
+
+ Private::ConferenceChannelRemovedInfo *info = mPriv->conferenceChannelRemovedQueue.dequeue();
+
+ ChannelPtr channel = mPriv->conferenceChannels[info->channelPath.path()];
+ mPriv->conferenceChannels.remove(info->channelPath.path());
+ emit conferenceChannelRemoved(channel, GroupMemberChangeDetails(actorContact,
+ info->details));
+
+ for (QHash<uint, ChannelPtr>::iterator i = mPriv->conferenceOriginalChannels.begin();
+ i != mPriv->conferenceOriginalChannels.end();) {
+ if (i.value() == channel) {
+ i = mPriv->conferenceOriginalChannels.erase(i);
+ } else {
+ ++i;
+ }
+ }
+
+ delete info;
+
+ mPriv->buildingConferenceChannelRemovedActorContact = false;
+ mPriv->processConferenceChannelRemoved();
+}
+
+/**
+ * \fn void Channel::groupFlagsChanged(uint flags, uint added, uint removed)
+ *
+ * Emitted when the value of groupFlags() changes.
+ *
+ * \param flags The value which would now be returned by groupFlags().
+ * \param added Flags added compared to the previous value.
+ * \param removed Flags removed compared to the previous value.
+ */
+
+/**
+ * \fn void Channel::groupCanAddContactsChanged(bool canAddContacts)
+ *
+ * Emitted when the value of groupCanAddContacts() changes.
+ *
+ * \param canAddContacts Whether a contact can be added to this channel.
+ * \sa groupCanAddContacts()
+ */
+
+/**
+ * \fn void Channel::groupCanRemoveContactsChanged(bool canRemoveContacts)
+ *
+ * Emitted when the value of groupCanRemoveContacts() changes.
+ *
+ * \param canRemoveContacts Whether a contact can be removed from this channel.
+ * \sa groupCanRemoveContacts()
+ */
+
+/**
+ * \fn void Channel::groupCanRescindContactsChanged(bool canRescindContacts)
+ *
+ * Emitted when the value of groupCanRescindContacts() changes.
+ *
+ * \param canRescindContacts Whether contact invitations can be rescinded.
+ * \sa groupCanRescindContacts()
+ */
+
+/**
+ * \fn void Channel::groupMembersChanged(
+ * const Tp::Contacts &groupMembersAdded,
+ * const Tp::Contacts &groupLocalPendingMembersAdded,
+ * const Tp::Contacts &groupRemotePendingMembersAdded,
+ * const Tp::Contacts &groupMembersRemoved,
+ * const Channel::GroupMemberChangeDetails &details)
+ *
+ * Emitted when the value returned by groupContacts(), groupLocalPendingContacts() or
+ * groupRemotePendingContacts() changes.
+ *
+ * \param groupMembersAdded The contacts that were added to this channel.
+ * \param groupLocalPendingMembersAdded The local pending contacts that were
+ * added to this channel.
+ * \param groupRemotePendingMembersAdded The remote pending contacts that were
+ * added to this channel.
+ * \param groupMembersRemoved The contacts removed from this channel.
+ * \param details Additional details such as the contact requesting or causing
+ * the change.
+ */
+
+/**
+ * \fn void Channel::groupHandleOwnersChanged(const HandleOwnerMap &owners,
+ * const Tp::UIntList &added, const Tp::UIntList &removed)
+ *
+ * Emitted when the value returned by groupHandleOwners() changes.
+ *
+ * \param owners The value which would now be returned by
+ * groupHandleOwners().
+ * \param added Handles which have been added to the mapping as keys, or
+ * existing handle keys for which the mapped-to value has changed.
+ * \param removed Handles which have been removed from the mapping.
+ */
+
+/**
+ * \fn void Channel::groupSelfContactChanged()
+ *
+ * Emitted when the value returned by groupSelfContact() changes.
+ */
+
+/**
+ * \fn void Channel::conferenceChannelMerged(const Tp::ChannelPtr &channel)
+ *
+ * Emitted when a new channel is added to the value of conferenceChannels().
+ *
+ * \param channel The channel that was added to conferenceChannels().
+ */
+
+/**
+ * \fn void Channel::conferenceChannelRemoved(const Tp::ChannelPtr &channel,
+ * const Tp::Channel::GroupMemberChangeDetails &details)
+ *
+ * Emitted when a new channel is removed from the value of conferenceChannels().
+ *
+ * \param channel The channel that was removed from conferenceChannels().
+ * \param details The change details.
+ */
+
+} // Tp
diff --git a/TelepathyQt/channel.h b/TelepathyQt/channel.h
new file mode 100644
index 00000000..eb7512aa
--- /dev/null
+++ b/TelepathyQt/channel.h
@@ -0,0 +1,256 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_channel_h_HEADER_GUARD_
+#define _TelepathyQt_channel_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/_gen/cli-channel.h>
+
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/DBus>
+#include <TelepathyQt/DBusProxy>
+#include <TelepathyQt/OptionalInterfaceFactory>
+#include <TelepathyQt/ReadinessHelper>
+#include <TelepathyQt/SharedPtr>
+#include <TelepathyQt/Types>
+
+#include <QSet>
+#include <QSharedDataPointer>
+#include <QVariantMap>
+
+namespace Tp
+{
+
+class Connection;
+class PendingOperation;
+class PendingReady;
+
+class TP_QT_EXPORT Channel : public StatefulDBusProxy,
+ public OptionalInterfaceFactory<Channel>
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(Channel)
+
+public:
+ static const Feature FeatureCore;
+ static const Feature FeatureConferenceInitialInviteeContacts;
+
+ static ChannelPtr create(const ConnectionPtr &connection,
+ const QString &objectPath, const QVariantMap &immutableProperties);
+
+ virtual ~Channel();
+
+ ConnectionPtr connection() const;
+
+ QVariantMap immutableProperties() const;
+
+ QString channelType() const;
+
+ HandleType targetHandleType() const;
+ uint targetHandle() const;
+ QString targetId() const;
+ ContactPtr targetContact() const;
+
+ bool isRequested() const;
+ ContactPtr initiatorContact() const;
+
+ PendingOperation *requestClose();
+ PendingOperation *requestLeave(const QString &message = QString(),
+ ChannelGroupChangeReason reason = ChannelGroupChangeReasonNone);
+
+ ChannelGroupFlags groupFlags() const;
+
+ bool groupCanAddContacts() const;
+ bool groupCanAddContactsWithMessage() const;
+ bool groupCanAcceptContactsWithMessage() const;
+ PendingOperation *groupAddContacts(const QList<ContactPtr> &contacts,
+ const QString &message = QString());
+ bool groupCanRescindContacts() const;
+ bool groupCanRescindContactsWithMessage() const;
+ bool groupCanRemoveContacts() const;
+ bool groupCanRemoveContactsWithMessage() const;
+ bool groupCanRejectContactsWithMessage() const;
+ bool groupCanDepartWithMessage() const;
+ PendingOperation *groupRemoveContacts(const QList<ContactPtr> &contacts,
+ const QString &message = QString(),
+ ChannelGroupChangeReason reason = ChannelGroupChangeReasonNone);
+
+ /**
+ * TODO: have parameters on these like
+ * Contacts groupContacts(bool includeSelfContact = true);
+ */
+ Contacts groupContacts() const;
+ Contacts groupLocalPendingContacts() const;
+ Contacts groupRemotePendingContacts() const;
+
+ class GroupMemberChangeDetails
+ {
+ public:
+ GroupMemberChangeDetails();
+ GroupMemberChangeDetails(const GroupMemberChangeDetails &other);
+ ~GroupMemberChangeDetails();
+
+ GroupMemberChangeDetails &operator=(const GroupMemberChangeDetails &other);
+
+ bool isValid() const { return mPriv.constData() != 0; }
+
+ bool hasActor() const;
+ ContactPtr actor() const;
+
+ bool hasReason() const { return allDetails().contains(QLatin1String("change-reason")); }
+ ChannelGroupChangeReason reason() const { return (ChannelGroupChangeReason) qdbus_cast<uint>(allDetails().value(QLatin1String("change-reason"))); }
+
+ bool hasMessage() const { return allDetails().contains(QLatin1String("message")); }
+ QString message () const { return qdbus_cast<QString>(allDetails().value(QLatin1String("message"))); }
+
+ bool hasError() const { return allDetails().contains(QLatin1String("error")); }
+ QString error() const { return qdbus_cast<QString>(allDetails().value(QLatin1String("error"))); }
+
+ bool hasDebugMessage() const { return allDetails().contains(QLatin1String("debug-message")); }
+ QString debugMessage() const { return qdbus_cast<QString>(allDetails().value(QLatin1String("debug-message"))); }
+
+ QVariantMap allDetails() const;
+
+ private:
+ friend class Channel;
+ friend class Contact;
+ friend class ContactManager;
+
+ TP_QT_NO_EXPORT GroupMemberChangeDetails(const ContactPtr &actor, const QVariantMap &details);
+
+ struct Private;
+ friend struct Private;
+ QSharedDataPointer<Private> mPriv;
+ };
+
+ GroupMemberChangeDetails groupLocalPendingContactChangeInfo(const ContactPtr &contact) const;
+ GroupMemberChangeDetails groupSelfContactRemoveInfo() const;
+
+ bool groupAreHandleOwnersAvailable() const;
+ HandleOwnerMap groupHandleOwners() const;
+
+ bool groupIsSelfContactTracked() const;
+ ContactPtr groupSelfContact() const;
+
+ bool isConference() const;
+ Contacts conferenceInitialInviteeContacts() const;
+ QList<ChannelPtr> conferenceChannels() const;
+ QList<ChannelPtr> conferenceInitialChannels() const;
+ QHash<uint, ChannelPtr> conferenceOriginalChannels() const;
+
+ bool supportsConferenceMerging() const;
+ PendingOperation *conferenceMergeChannel(const ChannelPtr &channel);
+
+ bool supportsConferenceSplitting() const;
+ PendingOperation *conferenceSplitChannel();
+
+Q_SIGNALS:
+ void groupFlagsChanged(Tp::ChannelGroupFlags flags,
+ Tp::ChannelGroupFlags added, Tp::ChannelGroupFlags removed);
+
+ void groupCanAddContactsChanged(bool canAddContacts);
+ void groupCanRemoveContactsChanged(bool canRemoveContacts);
+ void groupCanRescindContactsChanged(bool canRescindContacts);
+
+ void groupMembersChanged(
+ const Tp::Contacts &groupMembersAdded,
+ const Tp::Contacts &groupLocalPendingMembersAdded,
+ const Tp::Contacts &groupRemotePendingMembersAdded,
+ const Tp::Contacts &groupMembersRemoved,
+ const Tp::Channel::GroupMemberChangeDetails &details);
+
+ void groupHandleOwnersChanged(const Tp::HandleOwnerMap &owners,
+ const Tp::UIntList &added, const Tp::UIntList &removed);
+
+ void groupSelfContactChanged();
+
+ void conferenceChannelMerged(const Tp::ChannelPtr &channel);
+ void conferenceChannelRemoved(const Tp::ChannelPtr &channel,
+ const Tp::Channel::GroupMemberChangeDetails &details);
+
+protected:
+ Channel(const ConnectionPtr &connection,const QString &objectPath,
+ const QVariantMap &immutableProperties, const Feature &coreFeature);
+
+ Client::ChannelInterface *baseInterface() const;
+
+ bool groupSelfHandleIsLocalPending() const;
+
+protected Q_SLOTS:
+ PendingOperation *groupAddSelfHandle();
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void gotMainProperties(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void gotChannelType(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void gotHandle(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void gotInterfaces(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void onClosed();
+
+ TP_QT_NO_EXPORT void onConnectionReady(Tp::PendingOperation *op);
+ TP_QT_NO_EXPORT void onConnectionInvalidated();
+
+ TP_QT_NO_EXPORT void gotGroupProperties(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void gotGroupFlags(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void gotAllMembers(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void gotLocalPendingMembersWithInfo(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void gotSelfHandle(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void gotContacts(Tp::PendingOperation *op);
+
+ TP_QT_NO_EXPORT void onGroupFlagsChanged(uint added, uint removed);
+ TP_QT_NO_EXPORT void onMembersChanged(const QString &message,
+ const Tp::UIntList &added, const Tp::UIntList &removed,
+ const Tp::UIntList &localPending, const Tp::UIntList &remotePending,
+ uint actor, uint reason);
+ TP_QT_NO_EXPORT void onMembersChangedDetailed(
+ const Tp::UIntList &added, const Tp::UIntList &removed,
+ const Tp::UIntList &localPending, const Tp::UIntList &remotePending,
+ const QVariantMap &details);
+ TP_QT_NO_EXPORT void onHandleOwnersChanged(const Tp::HandleOwnerMap &added, const Tp::UIntList &removed);
+ TP_QT_NO_EXPORT void onSelfHandleChanged(uint selfHandle);
+
+ TP_QT_NO_EXPORT void gotConferenceProperties(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void gotConferenceInitialInviteeContacts(Tp::PendingOperation *op);
+ TP_QT_NO_EXPORT void onConferenceChannelMerged(const QDBusObjectPath &channel, uint channelSpecificHandle,
+ const QVariantMap &properties);
+ TP_QT_NO_EXPORT void onConferenceChannelMerged(const QDBusObjectPath &channel);
+ TP_QT_NO_EXPORT void onConferenceChannelRemoved(const QDBusObjectPath &channel, const QVariantMap &details);
+ TP_QT_NO_EXPORT void onConferenceChannelRemoved(const QDBusObjectPath &channel);
+ TP_QT_NO_EXPORT void gotConferenceChannelRemovedActorContact(Tp::PendingOperation *op);
+
+private:
+ class PendingLeave;
+ friend class PendingLeave;
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+Q_DECLARE_METATYPE(Tp::Channel::GroupMemberChangeDetails);
+
+#endif
diff --git a/TelepathyQt/channel.xml b/TelepathyQt/channel.xml
new file mode 100644
index 00000000..c850462e
--- /dev/null
+++ b/TelepathyQt/channel.xml
@@ -0,0 +1,37 @@
+<tp:spec
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<tp:title>Channel interfaces</tp:title>
+
+<xi:include href="../spec/Channel.xml"/>
+
+<xi:include href="../spec/Channel_Type_Contact_List.xml"/>
+<xi:include href="../spec/Channel_Type_Contact_Search.xml"/>
+<xi:include href="../spec/Channel_Type_DBus_Tube.xml"/>
+<xi:include href="../spec/Channel_Type_File_Transfer.xml"/>
+<xi:include href="../spec/Channel_Type_Room_List.xml"/>
+<xi:include href="../spec/Channel_Type_Server_Authentication.xml"/>
+<xi:include href="../spec/Channel_Type_Server_TLS_Connection.xml"/>
+<xi:include href="../spec/Channel_Type_Streamed_Media.xml"/>
+<xi:include href="../spec/Channel_Type_Stream_Tube.xml"/>
+<xi:include href="../spec/Channel_Type_Text.xml"/>
+<xi:include href="../spec/Channel_Type_Tubes.xml"/>
+
+<xi:include href="../spec/Channel_Interface_Anonymity.xml"/>
+<xi:include href="../spec/Channel_Interface_Call_State.xml"/>
+<xi:include href="../spec/Channel_Interface_Chat_State.xml"/>
+<xi:include href="../spec/Channel_Interface_Conference.xml"/>
+<xi:include href="../spec/Channel_Interface_Destroyable.xml"/>
+<xi:include href="../spec/Channel_Interface_DTMF.xml"/>
+<xi:include href="../spec/Channel_Interface_Group.xml"/>
+<xi:include href="../spec/Channel_Interface_Hold.xml"/>
+<xi:include href="../spec/Channel_Interface_Media_Signalling.xml"/>
+<xi:include href="../spec/Channel_Interface_Messages.xml"/>
+<xi:include href="../spec/Channel_Interface_Password.xml"/>
+<xi:include href="../spec/Channel_Interface_SASL_Authentication.xml"/>
+<xi:include href="../spec/Channel_Interface_Securable.xml"/>
+<xi:include href="../spec/Channel_Interface_Service_Point.xml"/>
+<xi:include href="../spec/Channel_Interface_Tube.xml"/>
+
+</tp:spec>
diff --git a/TelepathyQt/client-registrar-internal.h b/TelepathyQt/client-registrar-internal.h
new file mode 100644
index 00000000..3f0b4ec3
--- /dev/null
+++ b/TelepathyQt/client-registrar-internal.h
@@ -0,0 +1,365 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_client_registrar_internal_h_HEADER_GUARD_
+#define _TelepathyQt_client_registrar_internal_h_HEADER_GUARD_
+
+#include <QtCore/QObject>
+#include <QtDBus/QtDBus>
+
+#include <TelepathyQt/AbstractClientHandler>
+#include <TelepathyQt/Channel>
+#include <TelepathyQt/ChannelClassSpecList>
+#include <TelepathyQt/Types>
+
+#include "TelepathyQt/fake-handler-manager-internal.h"
+
+namespace Tp
+{
+
+class PendingOperation;
+
+class TP_QT_NO_EXPORT ClientAdaptor : public QDBusAbstractAdaptor
+{
+ Q_OBJECT
+ Q_CLASSINFO("D-Bus Interface", "org.freedesktop.Telepathy.Client")
+ Q_CLASSINFO("D-Bus Introspection", ""
+" <interface name=\"org.freedesktop.Telepathy.Client\" >\n"
+" <property name=\"Interfaces\" type=\"as\" access=\"read\" />\n"
+" </interface>\n"
+ "")
+
+ Q_PROPERTY(QStringList Interfaces READ Interfaces)
+
+public:
+ ClientAdaptor(ClientRegistrar *registrar, const QStringList &interfaces, QObject *parent);
+ virtual ~ClientAdaptor();
+
+ inline const ClientRegistrar *registrar() const
+ {
+ return mRegistrar;
+ }
+
+public: // Properties
+ inline QStringList Interfaces() const
+ {
+ return mInterfaces;
+ }
+
+private:
+ ClientRegistrar *mRegistrar;
+ QStringList mInterfaces;
+};
+
+class TP_QT_NO_EXPORT ClientObserverAdaptor : public QDBusAbstractAdaptor
+{
+ Q_OBJECT
+ Q_CLASSINFO("D-Bus Interface", "org.freedesktop.Telepathy.Client.Observer")
+ Q_CLASSINFO("D-Bus Introspection", ""
+" <interface name=\"org.freedesktop.Telepathy.Client.Observer\" >\n"
+" <property name=\"ObserverChannelFilter\" type=\"aa{sv}\" access=\"read\" />\n"
+" <property name=\"Recover\" type=\"b\" access=\"read\" />\n"
+" <method name=\"ObserveChannels\" >\n"
+" <arg name=\"Account\" type=\"o\" direction=\"in\" />\n"
+" <arg name=\"Connection\" type=\"o\" direction=\"in\" />\n"
+" <arg name=\"Channels\" type=\"a(oa{sv})\" direction=\"in\" />\n"
+" <arg name=\"Dispatch_Operation\" type=\"o\" direction=\"in\" />\n"
+" <arg name=\"Requests_Satisfied\" type=\"ao\" direction=\"in\" />\n"
+" <arg name=\"Observer_Info\" type=\"a{sv}\" direction=\"in\" />\n"
+" </method>\n"
+" </interface>\n"
+ "")
+
+ Q_PROPERTY(Tp::ChannelClassList ObserverChannelFilter READ ObserverChannelFilter)
+ Q_PROPERTY(bool Recover READ Recover)
+
+public:
+ ClientObserverAdaptor(ClientRegistrar *registrar,
+ AbstractClientObserver *client,
+ QObject *parent);
+ virtual ~ClientObserverAdaptor();
+
+ inline const ClientRegistrar *registrar() const
+ {
+ return mRegistrar;
+ }
+
+public: // Properties
+ inline Tp::ChannelClassList ObserverChannelFilter() const
+ {
+ return mClient->observerFilter().bareClasses();
+ }
+
+ inline bool Recover() const
+ {
+ return mClient->shouldRecover();
+ }
+
+public Q_SLOTS: // Methods
+ void ObserveChannels(const QDBusObjectPath &account,
+ const QDBusObjectPath &connection,
+ const Tp::ChannelDetailsList &channels,
+ const QDBusObjectPath &dispatchOperation,
+ const Tp::ObjectPathList &requestsSatisfied,
+ const QVariantMap &observerInfo,
+ const QDBusMessage &message);
+
+private Q_SLOTS:
+ void onReadyOpFinished(Tp::PendingOperation *);
+
+private:
+ struct InvocationData : RefCounted
+ {
+ InvocationData() : readyOp(0) {}
+
+ PendingOperation *readyOp;
+ QString error, message;
+
+ MethodInvocationContextPtr<> ctx;
+ AccountPtr acc;
+ ConnectionPtr conn;
+ QList<ChannelPtr> chans;
+ ChannelDispatchOperationPtr dispatchOp;
+ QList<ChannelRequestPtr> chanReqs;
+ AbstractClientObserver::ObserverInfo observerInfo;
+ };
+ QLinkedList<SharedPtr<InvocationData> > mInvocations;
+
+ ClientRegistrar *mRegistrar;
+ QDBusConnection mBus;
+ AbstractClientObserver *mClient;
+};
+
+class TP_QT_NO_EXPORT ClientApproverAdaptor : public QDBusAbstractAdaptor
+{
+ Q_OBJECT
+ Q_CLASSINFO("D-Bus Interface", "org.freedesktop.Telepathy.Client.Approver")
+ Q_CLASSINFO("D-Bus Introspection", ""
+" <interface name=\"org.freedesktop.Telepathy.Client.Approver\" >\n"
+" <property name=\"ApproverChannelFilter\" type=\"aa{sv}\" access=\"read\" />\n"
+" <method name=\"AddDispatchOperation\" >\n"
+" <arg name=\"Channels\" type=\"a(oa{sv})\" direction=\"in\" />\n"
+" <arg name=\"Dispatch_Operation\" type=\"o\" direction=\"in\" />\n"
+" <arg name=\"Properties\" type=\"a{sv}\" direction=\"in\" />\n"
+" </method>\n"
+" </interface>\n"
+ "")
+
+ Q_PROPERTY(Tp::ChannelClassList ApproverChannelFilter READ ApproverChannelFilter)
+
+public:
+ ClientApproverAdaptor(ClientRegistrar *registrar,
+ AbstractClientApprover *client,
+ QObject *parent);
+ virtual ~ClientApproverAdaptor();
+
+ inline const ClientRegistrar *registrar() const
+ {
+ return mRegistrar;
+ }
+
+public: // Properties
+ inline Tp::ChannelClassList ApproverChannelFilter() const
+ {
+ return mClient->approverFilter().bareClasses();
+ }
+
+public Q_SLOTS: // Methods
+ void AddDispatchOperation(const Tp::ChannelDetailsList &channels,
+ const QDBusObjectPath &dispatchOperation,
+ const QVariantMap &properties,
+ const QDBusMessage &message);
+
+private Q_SLOTS:
+ void onReadyOpFinished(Tp::PendingOperation *);
+
+private:
+ struct InvocationData : RefCounted
+ {
+ InvocationData() : readyOp(0) {}
+
+ PendingOperation *readyOp;
+ QString error, message;
+
+ MethodInvocationContextPtr<> ctx;
+ QList<ChannelPtr> chans;
+ ChannelDispatchOperationPtr dispatchOp;
+ };
+ QLinkedList<SharedPtr<InvocationData> > mInvocations;
+
+private:
+ ClientRegistrar *mRegistrar;
+ QDBusConnection mBus;
+ AbstractClientApprover *mClient;
+};
+
+class TP_QT_NO_EXPORT ClientHandlerAdaptor : public QDBusAbstractAdaptor
+{
+ Q_OBJECT
+ Q_CLASSINFO("D-Bus Interface", "org.freedesktop.Telepathy.Client.Handler")
+ Q_CLASSINFO("D-Bus Introspection", ""
+" <interface name=\"org.freedesktop.Telepathy.Client.Handler\" >\n"
+" <property name=\"HandlerChannelFilter\" type=\"aa{sv}\" access=\"read\" />\n"
+" <property name=\"BypassApproval\" type=\"b\" access=\"read\" />\n"
+" <property name=\"Capabilities\" type=\"as\" access=\"read\" />\n"
+" <property name=\"HandledChannels\" type=\"ao\" access=\"read\" />\n"
+" <method name=\"HandleChannels\" >\n"
+" <arg name=\"Account\" type=\"o\" direction=\"in\" />\n"
+" <arg name=\"Connection\" type=\"o\" direction=\"in\" />\n"
+" <arg name=\"Channels\" type=\"a(oa{sv})\" direction=\"in\" />\n"
+" <arg name=\"Requests_Satisfied\" type=\"ao\" direction=\"in\" />\n"
+" <arg name=\"User_Action_Time\" type=\"t\" direction=\"in\" />\n"
+" <arg name=\"Handler_Info\" type=\"a{sv}\" direction=\"in\" />\n"
+" </method>\n"
+" </interface>\n"
+ "")
+
+ Q_PROPERTY(Tp::ChannelClassList HandlerChannelFilter READ HandlerChannelFilter)
+ Q_PROPERTY(bool BypassApproval READ BypassApproval)
+ Q_PROPERTY(QStringList Capabilities READ Capabilities)
+ Q_PROPERTY(Tp::ObjectPathList HandledChannels READ HandledChannels)
+
+public:
+ ClientHandlerAdaptor(ClientRegistrar *registrar,
+ AbstractClientHandler *client,
+ QObject *parent);
+ virtual ~ClientHandlerAdaptor();
+
+ inline const ClientRegistrar *registrar() const
+ {
+ return mRegistrar;
+ }
+
+public: // Properties
+ inline Tp::ChannelClassList HandlerChannelFilter() const
+ {
+ return mClient->handlerFilter().bareClasses();
+ }
+
+ inline bool BypassApproval() const
+ {
+ return mClient->bypassApproval();
+ }
+
+ inline QStringList Capabilities() const
+ {
+ return mClient->handlerCapabilities().allTokens();
+ }
+
+ inline Tp::ObjectPathList HandledChannels() const
+ {
+ return FakeHandlerManager::instance()->handledChannels(mBus);
+ }
+
+public Q_SLOTS: // Methods
+ void HandleChannels(const QDBusObjectPath &account,
+ const QDBusObjectPath &connection,
+ const Tp::ChannelDetailsList &channels,
+ const Tp::ObjectPathList &requestsSatisfied,
+ qulonglong userActionTime,
+ const QVariantMap &handlerInfo,
+ const QDBusMessage &message);
+
+private Q_SLOTS:
+ void onReadyOpFinished(Tp::PendingOperation *);
+
+private:
+ struct InvocationData : RefCounted
+ {
+ InvocationData() : readyOp(0) {}
+
+ PendingOperation *readyOp;
+ QString error, message;
+
+ MethodInvocationContextPtr<> ctx;
+ AccountPtr acc;
+ ConnectionPtr conn;
+ QList<ChannelPtr> chans;
+ QList<ChannelRequestPtr> chanReqs;
+ QDateTime time;
+ AbstractClientHandler::HandlerInfo handlerInfo;
+ };
+ QLinkedList<SharedPtr<InvocationData> > mInvocations;
+
+private:
+ static void onContextFinished(const MethodInvocationContextPtr<> &context,
+ const QList<ChannelPtr> &channels, ClientHandlerAdaptor *self);
+
+ ClientRegistrar *mRegistrar;
+ QDBusConnection mBus;
+ AbstractClientHandler *mClient;
+
+ static QHash<QPair<QString, QString>, QList<ClientHandlerAdaptor *> > mAdaptorsForConnection;
+};
+
+class TP_QT_NO_EXPORT ClientHandlerRequestsAdaptor : public QDBusAbstractAdaptor
+{
+ Q_OBJECT
+ Q_CLASSINFO("D-Bus Interface", "org.freedesktop.Telepathy.Client.Interface.Requests")
+ Q_CLASSINFO("D-Bus Introspection", ""
+" <interface name=\"org.freedesktop.Telepathy.Client.Interface.Requests\" >\n"
+" <method name=\"AddRequest\" >\n"
+" <arg name=\"Request\" type=\"o\" direction=\"in\" />\n"
+" <arg name=\"Properties\" type=\"a{sv}\" direction=\"in\" />\n"
+" </method>\n"
+" <method name=\"RemoveRequest\" >\n"
+" <arg name=\"Request\" type=\"o\" direction=\"in\" />\n"
+" <arg name=\"Error\" type=\"s\" direction=\"in\" />\n"
+" <arg name=\"Message\" type=\"s\" direction=\"in\" />\n"
+" </method>\n"
+" </interface>\n"
+ "")
+
+public:
+ ClientHandlerRequestsAdaptor(ClientRegistrar *registrar,
+ AbstractClientHandler *client,
+ QObject *parent);
+ virtual ~ClientHandlerRequestsAdaptor();
+
+ inline const ClientRegistrar *registrar() const
+ {
+ return mRegistrar;
+ }
+
+public Q_SLOTS: // Methods
+ void AddRequest(const QDBusObjectPath &request,
+ const QVariantMap &requestProperties,
+ const QDBusMessage &message);
+ void RemoveRequest(const QDBusObjectPath &request,
+ const QString &errorName, const QString &errorMessage,
+ const QDBusMessage &message);
+
+private:
+ ClientRegistrar *mRegistrar;
+ QDBusConnection mBus;
+ AbstractClientHandler *mClient;
+};
+
+} // Tp
+
+Q_DECLARE_METATYPE(Tp::ClientAdaptor*)
+Q_DECLARE_METATYPE(Tp::ClientApproverAdaptor*)
+Q_DECLARE_METATYPE(Tp::ClientHandlerAdaptor*)
+Q_DECLARE_METATYPE(Tp::ClientHandlerRequestsAdaptor*)
+Q_DECLARE_METATYPE(Tp::ClientObserverAdaptor*)
+
+#endif
diff --git a/TelepathyQt/client-registrar.cpp b/TelepathyQt/client-registrar.cpp
new file mode 100644
index 00000000..1c6b7609
--- /dev/null
+++ b/TelepathyQt/client-registrar.cpp
@@ -0,0 +1,1038 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/ClientRegistrar>
+#include "TelepathyQt/client-registrar-internal.h"
+
+#include "TelepathyQt/_gen/client-registrar.moc.hpp"
+#include "TelepathyQt/_gen/client-registrar-internal.moc.hpp"
+
+#include "TelepathyQt/channel-factory.h"
+#include "TelepathyQt/debug-internal.h"
+#include "TelepathyQt/request-temporary-handler-internal.h"
+
+#include <TelepathyQt/Account>
+#include <TelepathyQt/AccountManager>
+#include <TelepathyQt/Channel>
+#include <TelepathyQt/ChannelDispatchOperation>
+#include <TelepathyQt/ChannelRequest>
+#include <TelepathyQt/Connection>
+#include <TelepathyQt/MethodInvocationContext>
+#include <TelepathyQt/PendingComposite>
+#include <TelepathyQt/PendingReady>
+
+namespace Tp
+{
+
+class HandleChannelsInvocationContext : public MethodInvocationContext<>
+{
+ Q_DISABLE_COPY(HandleChannelsInvocationContext)
+
+public:
+ typedef void (*FinishedCb)(const MethodInvocationContextPtr<> &context,
+ const QList<ChannelPtr> &channels,
+ void *data);
+
+ static MethodInvocationContextPtr<> create(const QDBusConnection &bus,
+ const QDBusMessage &message, const QList<ChannelPtr> &channels,
+ FinishedCb finishedCb, void *finishedCbData)
+ {
+ return SharedPtr<MethodInvocationContext<> >(
+ new HandleChannelsInvocationContext(bus, message, channels,
+ finishedCb, finishedCbData));
+ }
+
+private:
+ HandleChannelsInvocationContext(const QDBusConnection &connection,
+ const QDBusMessage &message, const QList<ChannelPtr> &channels,
+ FinishedCb finishedCb, void *finishedCbData)
+ : MethodInvocationContext<>(connection, message),
+ mChannels(channels),
+ mFinishedCb(finishedCb),
+ mFinishedCbData(finishedCbData)
+ {
+ }
+
+ void onFinished()
+ {
+ if (mFinishedCb) {
+ mFinishedCb(MethodInvocationContextPtr<>(this), mChannels, mFinishedCbData);
+ }
+ }
+
+ QList<ChannelPtr> mChannels;
+ FinishedCb mFinishedCb;
+ void *mFinishedCbData;
+};
+
+ClientAdaptor::ClientAdaptor(ClientRegistrar *registrar, const QStringList &interfaces,
+ QObject *parent)
+ : QDBusAbstractAdaptor(parent),
+ mRegistrar(registrar),
+ mInterfaces(interfaces)
+{
+}
+
+ClientAdaptor::~ClientAdaptor()
+{
+}
+
+ClientObserverAdaptor::ClientObserverAdaptor(ClientRegistrar *registrar,
+ AbstractClientObserver *client,
+ QObject *parent)
+ : QDBusAbstractAdaptor(parent),
+ mRegistrar(registrar),
+ mBus(registrar->dbusConnection()),
+ mClient(client)
+{
+}
+
+ClientObserverAdaptor::~ClientObserverAdaptor()
+{
+}
+
+void ClientObserverAdaptor::ObserveChannels(const QDBusObjectPath &accountPath,
+ const QDBusObjectPath &connectionPath,
+ const Tp::ChannelDetailsList &channelDetailsList,
+ const QDBusObjectPath &dispatchOperationPath,
+ const Tp::ObjectPathList &requestsSatisfied,
+ const QVariantMap &observerInfo,
+ const QDBusMessage &message)
+{
+ debug() << "ObserveChannels: account:" << accountPath.path() <<
+ ", connection:" << connectionPath.path();
+
+ AccountFactoryConstPtr accFactory = mRegistrar->accountFactory();
+ ConnectionFactoryConstPtr connFactory = mRegistrar->connectionFactory();
+ ChannelFactoryConstPtr chanFactory = mRegistrar->channelFactory();
+ ContactFactoryConstPtr contactFactory = mRegistrar->contactFactory();
+
+ SharedPtr<InvocationData> invocation(new InvocationData());
+
+ QList<PendingOperation *> readyOps;
+
+ PendingReady *accReady = accFactory->proxy(TP_QT_ACCOUNT_MANAGER_BUS_NAME,
+ accountPath.path(),
+ connFactory,
+ chanFactory,
+ contactFactory);
+ invocation->acc = AccountPtr::qObjectCast(accReady->proxy());
+ readyOps.append(accReady);
+
+ QString connectionBusName = connectionPath.path().mid(1).replace(
+ QLatin1String("/"), QLatin1String("."));
+ PendingReady *connReady = connFactory->proxy(connectionBusName, connectionPath.path(),
+ chanFactory, contactFactory);
+ invocation->conn = ConnectionPtr::qObjectCast(connReady->proxy());
+ readyOps.append(connReady);
+
+ foreach (const ChannelDetails &channelDetails, channelDetailsList) {
+ PendingReady *chanReady = chanFactory->proxy(invocation->conn,
+ channelDetails.channel.path(), channelDetails.properties);
+ ChannelPtr channel = ChannelPtr::qObjectCast(chanReady->proxy());
+ invocation->chans.append(channel);
+ readyOps.append(chanReady);
+ }
+
+ // Yes, we don't give the choice of making CDO and CR ready or not - however, readifying them is
+ // 0-1 D-Bus calls each, for CR mostly 0 - and their constructors start making them ready
+ // automatically, so we wouldn't save any D-Bus traffic anyway
+
+ if (!dispatchOperationPath.path().isEmpty() && dispatchOperationPath.path() != QLatin1String("/")) {
+ QVariantMap props;
+
+ // TODO: push to tp spec having all of the CDO immutable props be contained in observerInfo
+ // so we don't have to introspect the CDO either - then we can pretty much do:
+ //
+ // props = qdbus_cast<QVariantMap>(
+ // observerInfo.value(QLatin1String("dispatch-operation-properties")));
+ //
+ // Currently something like the following can be used for testing the CDO "we've got
+ // everything we need" codepath:
+ //
+ // props.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_DISPATCH_OPERATION ".Account"),
+ // QVariant::fromValue(QDBusObjectPath(accountPath.path())));
+ // props.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_DISPATCH_OPERATION ".Connection"),
+ // QVariant::fromValue(QDBusObjectPath(connectionPath.path())));
+ // props.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_DISPATCH_OPERATION ".Interfaces"),
+ // QVariant::fromValue(QStringList()));
+ // props.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_DISPATCH_OPERATION ".PossibleHandlers"),
+ // QVariant::fromValue(QStringList()));
+
+ invocation->dispatchOp = ChannelDispatchOperation::create(mBus, dispatchOperationPath.path(),
+ props,
+ invocation->chans,
+ accFactory,
+ connFactory,
+ chanFactory,
+ contactFactory);
+ readyOps.append(invocation->dispatchOp->becomeReady());
+ }
+
+ invocation->observerInfo = AbstractClientObserver::ObserverInfo(observerInfo);
+
+ ObjectImmutablePropertiesMap reqPropsMap = qdbus_cast<ObjectImmutablePropertiesMap>(
+ observerInfo.value(QLatin1String("request-properties")));
+ foreach (const QDBusObjectPath &reqPath, requestsSatisfied) {
+ ChannelRequestPtr channelRequest = ChannelRequest::create(invocation->acc,
+ reqPath.path(), reqPropsMap.value(reqPath));
+ invocation->chanReqs.append(channelRequest);
+ readyOps.append(channelRequest->becomeReady());
+ }
+
+ invocation->ctx = MethodInvocationContextPtr<>(new MethodInvocationContext<>(mBus, message));
+
+ invocation->readyOp = new PendingComposite(readyOps, invocation->ctx);
+ connect(invocation->readyOp,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onReadyOpFinished(Tp::PendingOperation*)));
+
+ mInvocations.append(invocation);
+
+ debug() << "Preparing proxies for ObserveChannels of" << channelDetailsList.size() << "channels"
+ << "for client" << mClient;
+}
+
+void ClientObserverAdaptor::onReadyOpFinished(Tp::PendingOperation *op)
+{
+ Q_ASSERT(!mInvocations.isEmpty());
+ Q_ASSERT(op->isFinished());
+
+ for (QLinkedList<SharedPtr<InvocationData> >::iterator i = mInvocations.begin();
+ i != mInvocations.end(); ++i) {
+ if ((*i)->readyOp != op) {
+ continue;
+ }
+
+ (*i)->readyOp = 0;
+
+ if (op->isError()) {
+ warning() << "Preparing proxies for ObserveChannels failed with" << op->errorName()
+ << op->errorMessage();
+ (*i)->error = op->errorName();
+ (*i)->message = op->errorMessage();
+ }
+
+ break;
+ }
+
+ while (!mInvocations.isEmpty() && !mInvocations.first()->readyOp) {
+ SharedPtr<InvocationData> invocation = mInvocations.takeFirst();
+
+ if (!invocation->error.isEmpty()) {
+ // We guarantee that the proxies were ready - so we can't invoke the client if they
+ // weren't made ready successfully. Fix the introspection code if this happens :)
+ invocation->ctx->setFinishedWithError(invocation->error, invocation->message);
+ continue;
+ }
+
+ debug() << "Invoking application observeChannels with" << invocation->chans.size()
+ << "channels on" << mClient;
+
+ mClient->observeChannels(invocation->ctx, invocation->acc, invocation->conn,
+ invocation->chans, invocation->dispatchOp, invocation->chanReqs,
+ invocation->observerInfo);
+ }
+}
+
+ClientApproverAdaptor::ClientApproverAdaptor(ClientRegistrar *registrar,
+ AbstractClientApprover *client,
+ QObject *parent)
+ : QDBusAbstractAdaptor(parent),
+ mRegistrar(registrar),
+ mBus(registrar->dbusConnection()),
+ mClient(client)
+{
+}
+
+ClientApproverAdaptor::~ClientApproverAdaptor()
+{
+}
+
+void ClientApproverAdaptor::AddDispatchOperation(const Tp::ChannelDetailsList &channelDetailsList,
+ const QDBusObjectPath &dispatchOperationPath,
+ const QVariantMap &properties,
+ const QDBusMessage &message)
+{
+ AccountFactoryConstPtr accFactory = mRegistrar->accountFactory();
+ ConnectionFactoryConstPtr connFactory = mRegistrar->connectionFactory();
+ ChannelFactoryConstPtr chanFactory = mRegistrar->channelFactory();
+ ContactFactoryConstPtr contactFactory = mRegistrar->contactFactory();
+
+ QList<PendingOperation *> readyOps;
+
+ QDBusObjectPath connectionPath = qdbus_cast<QDBusObjectPath>(
+ properties.value(
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL_DISPATCH_OPERATION ".Connection")));
+ debug() << "addDispatchOperation: connection:" << connectionPath.path();
+ QString connectionBusName = connectionPath.path().mid(1).replace(
+ QLatin1String("/"), QLatin1String("."));
+ PendingReady *connReady = connFactory->proxy(connectionBusName, connectionPath.path(), chanFactory,
+ contactFactory);
+ ConnectionPtr connection = ConnectionPtr::qObjectCast(connReady->proxy());
+ readyOps.append(connReady);
+
+ SharedPtr<InvocationData> invocation(new InvocationData);
+
+ foreach (const ChannelDetails &channelDetails, channelDetailsList) {
+ PendingReady *chanReady = chanFactory->proxy(connection, channelDetails.channel.path(),
+ channelDetails.properties);
+ invocation->chans.append(ChannelPtr::qObjectCast(chanReady->proxy()));
+ readyOps.append(chanReady);
+ }
+
+ invocation->dispatchOp = ChannelDispatchOperation::create(mBus,
+ dispatchOperationPath.path(), properties, invocation->chans, accFactory, connFactory,
+ chanFactory, contactFactory);
+ readyOps.append(invocation->dispatchOp->becomeReady());
+
+ invocation->ctx = MethodInvocationContextPtr<>(new MethodInvocationContext<>(mBus, message));
+
+ invocation->readyOp = new PendingComposite(readyOps, invocation->ctx);
+ connect(invocation->readyOp,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onReadyOpFinished(Tp::PendingOperation*)));
+
+ mInvocations.append(invocation);
+}
+
+void ClientApproverAdaptor::onReadyOpFinished(Tp::PendingOperation *op)
+{
+ Q_ASSERT(!mInvocations.isEmpty());
+ Q_ASSERT(op->isFinished());
+
+ for (QLinkedList<SharedPtr<InvocationData> >::iterator i = mInvocations.begin();
+ i != mInvocations.end(); ++i) {
+ if ((*i)->readyOp != op) {
+ continue;
+ }
+
+ (*i)->readyOp = 0;
+
+ if (op->isError()) {
+ warning() << "Preparing proxies for AddDispatchOperation failed with" << op->errorName()
+ << op->errorMessage();
+ (*i)->error = op->errorName();
+ (*i)->message = op->errorMessage();
+ }
+
+ break;
+ }
+
+ while (!mInvocations.isEmpty() && !mInvocations.first()->readyOp) {
+ SharedPtr<InvocationData> invocation = mInvocations.takeFirst();
+
+ if (!invocation->error.isEmpty()) {
+ // We guarantee that the proxies were ready - so we can't invoke the client if they
+ // weren't made ready successfully. Fix the introspection code if this happens :)
+ invocation->ctx->setFinishedWithError(invocation->error, invocation->message);
+ continue;
+ }
+
+ debug() << "Invoking application addDispatchOperation with CDO"
+ << invocation->dispatchOp->objectPath() << "on" << mClient;
+
+ mClient->addDispatchOperation(invocation->ctx, invocation->dispatchOp);
+ }
+}
+
+QHash<QPair<QString, QString>, QList<ClientHandlerAdaptor *> > ClientHandlerAdaptor::mAdaptorsForConnection;
+
+ClientHandlerAdaptor::ClientHandlerAdaptor(ClientRegistrar *registrar,
+ AbstractClientHandler *client,
+ QObject *parent)
+ : QDBusAbstractAdaptor(parent),
+ mRegistrar(registrar),
+ mBus(registrar->dbusConnection()),
+ mClient(client)
+{
+ QList<ClientHandlerAdaptor *> &handlerAdaptors =
+ mAdaptorsForConnection[qMakePair(mBus.name(), mBus.baseService())];
+ handlerAdaptors.append(this);
+}
+
+ClientHandlerAdaptor::~ClientHandlerAdaptor()
+{
+ QPair<QString, QString> busId = qMakePair(mBus.name(), mBus.baseService());
+ QList<ClientHandlerAdaptor *> &handlerAdaptors =
+ mAdaptorsForConnection[busId];
+ handlerAdaptors.removeOne(this);
+ if (handlerAdaptors.isEmpty()) {
+ mAdaptorsForConnection.remove(busId);
+ }
+}
+
+void ClientHandlerAdaptor::HandleChannels(const QDBusObjectPath &accountPath,
+ const QDBusObjectPath &connectionPath,
+ const Tp::ChannelDetailsList &channelDetailsList,
+ const Tp::ObjectPathList &requestsSatisfied,
+ qulonglong userActionTime_t,
+ const QVariantMap &handlerInfo,
+ const QDBusMessage &message)
+{
+ debug() << "HandleChannels: account:" << accountPath.path() <<
+ ", connection:" << connectionPath.path();
+
+ AccountFactoryConstPtr accFactory = mRegistrar->accountFactory();
+ ConnectionFactoryConstPtr connFactory = mRegistrar->connectionFactory();
+ ChannelFactoryConstPtr chanFactory = mRegistrar->channelFactory();
+ ContactFactoryConstPtr contactFactory = mRegistrar->contactFactory();
+
+ SharedPtr<InvocationData> invocation(new InvocationData());
+ QList<PendingOperation *> readyOps;
+
+ RequestTemporaryHandler *tempHandler = dynamic_cast<RequestTemporaryHandler *>(mClient);
+ if (tempHandler) {
+ debug() << " This is a temporary handler for the Request & Handle API,"
+ << "giving an early signal of the invocation";
+ tempHandler->setDBusHandlerInvoked();
+ }
+
+ PendingReady *accReady = accFactory->proxy(TP_QT_ACCOUNT_MANAGER_BUS_NAME,
+ accountPath.path(),
+ connFactory,
+ chanFactory,
+ contactFactory);
+ invocation->acc = AccountPtr::qObjectCast(accReady->proxy());
+ readyOps.append(accReady);
+
+ QString connectionBusName = connectionPath.path().mid(1).replace(
+ QLatin1String("/"), QLatin1String("."));
+ PendingReady *connReady = connFactory->proxy(connectionBusName, connectionPath.path(),
+ chanFactory, contactFactory);
+ invocation->conn = ConnectionPtr::qObjectCast(connReady->proxy());
+ readyOps.append(connReady);
+
+ foreach (const ChannelDetails &channelDetails, channelDetailsList) {
+ PendingReady *chanReady = chanFactory->proxy(invocation->conn,
+ channelDetails.channel.path(), channelDetails.properties);
+ ChannelPtr channel = ChannelPtr::qObjectCast(chanReady->proxy());
+ invocation->chans.append(channel);
+ readyOps.append(chanReady);
+ }
+
+ invocation->handlerInfo = AbstractClientHandler::HandlerInfo(handlerInfo);
+
+ ObjectImmutablePropertiesMap reqPropsMap = qdbus_cast<ObjectImmutablePropertiesMap>(
+ handlerInfo.value(QLatin1String("request-properties")));
+ foreach (const QDBusObjectPath &reqPath, requestsSatisfied) {
+ ChannelRequestPtr channelRequest = ChannelRequest::create(invocation->acc,
+ reqPath.path(), reqPropsMap.value(reqPath));
+ invocation->chanReqs.append(channelRequest);
+ readyOps.append(channelRequest->becomeReady());
+ }
+
+ // FIXME See http://bugs.freedesktop.org/show_bug.cgi?id=21690
+ if (userActionTime_t != 0) {
+ invocation->time = QDateTime::fromTime_t((uint) userActionTime_t);
+ }
+
+ invocation->ctx = HandleChannelsInvocationContext::create(mBus, message,
+ invocation->chans,
+ reinterpret_cast<HandleChannelsInvocationContext::FinishedCb>(
+ &ClientHandlerAdaptor::onContextFinished),
+ this);
+
+ invocation->readyOp = new PendingComposite(readyOps, invocation->ctx);
+ connect(invocation->readyOp,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onReadyOpFinished(Tp::PendingOperation*)));
+
+ mInvocations.append(invocation);
+
+ debug() << "Preparing proxies for HandleChannels of" << channelDetailsList.size() << "channels"
+ << "for client" << mClient;
+}
+
+void ClientHandlerAdaptor::onReadyOpFinished(Tp::PendingOperation *op)
+{
+ Q_ASSERT(!mInvocations.isEmpty());
+ Q_ASSERT(op->isFinished());
+
+ for (QLinkedList<SharedPtr<InvocationData> >::iterator i = mInvocations.begin();
+ i != mInvocations.end(); ++i) {
+ if ((*i)->readyOp != op) {
+ continue;
+ }
+
+ (*i)->readyOp = 0;
+
+ if (op->isError()) {
+ warning() << "Preparing proxies for HandleChannels failed with" << op->errorName()
+ << op->errorMessage();
+ (*i)->error = op->errorName();
+ (*i)->message = op->errorMessage();
+ }
+
+ break;
+ }
+
+ while (!mInvocations.isEmpty() && !mInvocations.first()->readyOp) {
+ SharedPtr<InvocationData> invocation = mInvocations.takeFirst();
+
+ if (!invocation->error.isEmpty()) {
+ RequestTemporaryHandler *tempHandler = dynamic_cast<RequestTemporaryHandler *>(mClient);
+ if (tempHandler) {
+ debug() << " This is a temporary handler for the Request & Handle API, indicating failure";
+ tempHandler->setDBusHandlerErrored(invocation->error, invocation->message);
+ }
+
+ // We guarantee that the proxies were ready - so we can't invoke the client if they
+ // weren't made ready successfully. Fix the introspection code if this happens :)
+ invocation->ctx->setFinishedWithError(invocation->error, invocation->message);
+ continue;
+ }
+
+ debug() << "Invoking application handleChannels with" << invocation->chans.size()
+ << "channels on" << mClient;
+
+ mClient->handleChannels(invocation->ctx, invocation->acc, invocation->conn,
+ invocation->chans, invocation->chanReqs, invocation->time, invocation->handlerInfo);
+ }
+}
+
+void ClientHandlerAdaptor::onContextFinished(
+ const MethodInvocationContextPtr<> &context,
+ const QList<ChannelPtr> &channels, ClientHandlerAdaptor *self)
+{
+ if (!context->isError()) {
+ debug() << "HandleChannels context finished successfully, "
+ "updating handled channels";
+
+ // register the channels in FakeHandlerManager so we report HandledChannels correctly
+ FakeHandlerManager::instance()->registerChannels(channels);
+ }
+}
+
+ClientHandlerRequestsAdaptor::ClientHandlerRequestsAdaptor(
+ ClientRegistrar *registrar,
+ AbstractClientHandler *client,
+ QObject *parent)
+ : QDBusAbstractAdaptor(parent),
+ mRegistrar(registrar),
+ mBus(registrar->dbusConnection()),
+ mClient(client)
+{
+}
+
+ClientHandlerRequestsAdaptor::~ClientHandlerRequestsAdaptor()
+{
+}
+
+void ClientHandlerRequestsAdaptor::AddRequest(
+ const QDBusObjectPath &request,
+ const QVariantMap &requestProperties,
+ const QDBusMessage &message)
+{
+ debug() << "AddRequest:" << request.path();
+ message.setDelayedReply(true);
+ mBus.send(message.createReply());
+ mClient->addRequest(ChannelRequest::create(mBus,
+ request.path(), requestProperties,
+ mRegistrar->accountFactory(),
+ mRegistrar->connectionFactory(),
+ mRegistrar->channelFactory(),
+ mRegistrar->contactFactory()));
+}
+
+void ClientHandlerRequestsAdaptor::RemoveRequest(
+ const QDBusObjectPath &request,
+ const QString &errorName, const QString &errorMessage,
+ const QDBusMessage &message)
+{
+ debug() << "RemoveRequest:" << request.path() << "-" << errorName
+ << "-" << errorMessage;
+ message.setDelayedReply(true);
+ mBus.send(message.createReply());
+ mClient->removeRequest(ChannelRequest::create(mBus,
+ request.path(), QVariantMap(),
+ mRegistrar->accountFactory(),
+ mRegistrar->connectionFactory(),
+ mRegistrar->channelFactory(),
+ mRegistrar->contactFactory()), errorName, errorMessage);
+}
+
+struct TP_QT_NO_EXPORT ClientRegistrar::Private
+{
+ Private(const QDBusConnection &bus, const AccountFactoryConstPtr &accFactory,
+ const ConnectionFactoryConstPtr &connFactory, const ChannelFactoryConstPtr &chanFactory,
+ const ContactFactoryConstPtr &contactFactory)
+ : bus(bus), accFactory(accFactory), connFactory(connFactory), chanFactory(chanFactory),
+ contactFactory(contactFactory)
+ {
+ if (accFactory->dbusConnection().name() != bus.name()) {
+ warning() << " The D-Bus connection in the account factory is not the proxy connection";
+ }
+
+ if (connFactory->dbusConnection().name() != bus.name()) {
+ warning() << " The D-Bus connection in the connection factory is not the proxy connection";
+ }
+
+ if (chanFactory->dbusConnection().name() != bus.name()) {
+ warning() << " The D-Bus connection in the channel factory is not the proxy connection";
+ }
+ }
+
+ QDBusConnection bus;
+
+ AccountFactoryConstPtr accFactory;
+ ConnectionFactoryConstPtr connFactory;
+ ChannelFactoryConstPtr chanFactory;
+ ContactFactoryConstPtr contactFactory;
+
+ QHash<AbstractClientPtr, QString> clients;
+ QHash<AbstractClientPtr, QObject*> clientObjects;
+ QSet<QString> services;
+};
+
+/**
+ * \class ClientRegistrar
+ * \ingroup serverclient
+ * \headerfile TelepathyQt/client-registrar.h <TelepathyQt/ClientRegistrar>
+ *
+ * \brief The ClientRegistrar class is responsible for registering Telepathy
+ * clients (Observer, Approver, Handler).
+ *
+ * Clients should inherit AbstractClientObserver, AbstractClientApprover,
+ * AbstractClientHandler or some combination of these, by using multiple
+ * inheritance, and register themselves using registerClient().
+ *
+ * See the individual classes descriptions for more details.
+ *
+ * \section cr_usage_sec Usage
+ *
+ * \subsection cr_create_sec Creating a client registrar object
+ *
+ * One way to create a ClientRegistrar object is to just call the create method.
+ * For example:
+ *
+ * \code ClientRegistrarPtr cr = ClientRegistrar::create(); \endcode
+ *
+ * You can also provide a D-Bus connection as a QDBusConnection:
+ *
+ * \code ClientRegistrarPtr cr = ClientRegistrar::create(QDBusConnection::systemBus()); \endcode
+ *
+ * \subsection cr_registering_sec Registering a client
+ *
+ * To register a client, just call registerClient() with a given AbstractClientPtr
+ * pointing to a valid AbstractClient instance.
+ *
+ * \code
+ *
+ * class MyClient : public AbstractClientObserver, public AbstractClientHandler
+ * {
+ * ...
+ * };
+ *
+ * ...
+ *
+ * ClientRegistrarPtr cr = ClientRegistrar::create();
+ * SharedPtr<MyClient> client = SharedPtr<MyClient>(new MyClient(...));
+ * cr->registerClient(AbstractClientPtr::dynamicCast(client), "myclient");
+ *
+ * \endcode
+ *
+ * \sa AbstractClientObserver, AbstractClientApprover, AbstractClientHandler
+ *
+ * See \ref async_model, \ref shared_ptr
+ */
+
+/**
+ * Create a new client registrar object using the given \a bus.
+ *
+ * The instance will use an account factory creating Tp::Account objects with no features
+ * ready, a connection factory creating Tp::Connection objects with no features ready, and a channel
+ * factory creating stock Telepathy-Qt4 channel subclasses, as appropriate, with no features ready.
+ *
+ * \param bus QDBusConnection to use.
+ * \return A ClientRegistrarPtr object pointing to the newly created ClientRegistrar object.
+ */
+ClientRegistrarPtr ClientRegistrar::create(const QDBusConnection &bus)
+{
+ return create(bus, AccountFactory::create(bus), ConnectionFactory::create(bus),
+ ChannelFactory::create(bus), ContactFactory::create());
+}
+
+/**
+ * Create a new client registrar object using QDBusConnection::sessionBus() and the given factories.
+ *
+ * \param accountFactory The account factory to use.
+ * \param connectionFactory The connection factory to use.
+ * \param channelFactory The channel factory to use.
+ * \param contactFactory The contact factory to use.
+ * \return A ClientRegistrarPtr object pointing to the newly created ClientRegistrar object.
+ */
+ClientRegistrarPtr ClientRegistrar::create(
+ const AccountFactoryConstPtr &accountFactory,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory)
+{
+ return create(QDBusConnection::sessionBus(), accountFactory, connectionFactory, channelFactory,
+ contactFactory);
+}
+
+/**
+ * Create a new client registrar object using the given \a bus and the given factories.
+ *
+ * \param bus QDBusConnection to use.
+ * \param accountFactory The account factory to use.
+ * \param connectionFactory The connection factory to use.
+ * \param channelFactory The channel factory to use.
+ * \param contactFactory The contact factory to use.
+ * \return A ClientRegistrarPtr object pointing to the newly created ClientRegistrar object.
+ */
+ClientRegistrarPtr ClientRegistrar::create(const QDBusConnection &bus,
+ const AccountFactoryConstPtr &accountFactory,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory)
+{
+ return ClientRegistrarPtr(new ClientRegistrar(bus, accountFactory, connectionFactory,
+ channelFactory, contactFactory));
+}
+
+/**
+ * Create a new client registrar object using the bus and factories of the given Account \a manager.
+ *
+ * Using this create method will enable (like any other way of passing the same factories to an AM
+ * and a registrar) getting the same Account/Connection etc. proxy instances from both
+ * AccountManager and AbstractClient implementations.
+ *
+ * \param manager The AccountManager the bus and factories of which should be used.
+ * \return A ClientRegistrarPtr object pointing to the newly ClientRegistrar object.
+ */
+ClientRegistrarPtr ClientRegistrar::create(const AccountManagerPtr &manager)
+{
+ if (!manager) {
+ return ClientRegistrarPtr();
+ }
+
+ return create(manager->dbusConnection(), manager->accountFactory(),
+ manager->connectionFactory(), manager->channelFactory(), manager->contactFactory());
+}
+
+/**
+ * Construct a new client registrar object using the given \a bus and the given factories.
+ *
+ * \param bus QDBusConnection to use.
+ * \param accountFactory The account factory to use.
+ * \param connectionFactory The connection factory to use.
+ * \param channelFactory The channel factory to use.
+ * \param contactFactory The contact factory to use.
+ */
+ClientRegistrar::ClientRegistrar(const QDBusConnection &bus,
+ const AccountFactoryConstPtr &accountFactory,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory)
+ : Object(),
+ mPriv(new Private(bus, accountFactory, connectionFactory, channelFactory, contactFactory))
+{
+}
+
+/**
+ * Class destructor.
+ */
+ClientRegistrar::~ClientRegistrar()
+{
+ unregisterClients();
+ delete mPriv;
+}
+
+/**
+ * Return the D-Bus connection being used by this client registrar.
+ *
+ * \return A QDBusConnection object.
+ */
+QDBusConnection ClientRegistrar::dbusConnection() const
+{
+ return mPriv->bus;
+}
+
+/**
+ * Get the account factory used by this client registrar.
+ *
+ * Only read access is provided. This allows constructing object instances and examining the object
+ * construction settings, but not changing settings. Allowing changes would lead to tricky
+ * situations where objects constructed at different times by the registrar would have unpredictably
+ * different construction settings (eg. subclass).
+ *
+ * \return A read-only pointer to the AccountFactory object.
+ */
+AccountFactoryConstPtr ClientRegistrar::accountFactory() const
+{
+ return mPriv->accFactory;
+}
+
+/**
+ * Get the connection factory used by this client registrar.
+ *
+ * Only read access is provided. This allows constructing object instances and examining the object
+ * construction settings, but not changing settings. Allowing changes would lead to tricky
+ * situations where objects constructed at different times by the registrar would have unpredictably
+ * different construction settings (eg. subclass).
+ *
+ * \return A read-only pointer to the ConnectionFactory object.
+ */
+ConnectionFactoryConstPtr ClientRegistrar::connectionFactory() const
+{
+ return mPriv->connFactory;
+}
+
+/**
+ * Get the channel factory used by this client registrar.
+ *
+ * Only read access is provided. This allows constructing object instances and examining the object
+ * construction settings, but not changing settings. Allowing changes would lead to tricky
+ * situations where objects constructed at different times by the registrar would have unpredictably
+ * different construction settings (eg. subclass).
+ *
+ * \return A read-only pointer to the ChannelFactory object.
+ */
+ChannelFactoryConstPtr ClientRegistrar::channelFactory() const
+{
+ return mPriv->chanFactory;
+}
+
+/**
+ * Get the contact factory used by this client registrar.
+ *
+ * Only read access is provided. This allows constructing object instances and examining the object
+ * construction settings, but not changing settings. Allowing changes would lead to tricky
+ * situations where objects constructed at different times by the registrar would have unpredictably
+ * different construction settings (eg. subclass).
+ *
+ * \return A read-only pointer to the ContactFactory object.
+ */
+ContactFactoryConstPtr ClientRegistrar::contactFactory() const
+{
+ return mPriv->contactFactory;
+}
+
+/**
+ * Return the list of clients registered using registerClient() on this client
+ * registrar.
+ *
+ * \return A list of pointers to AbstractClient objects.
+ * \sa registerClient(), unregisterClient()
+ */
+QList<AbstractClientPtr> ClientRegistrar::registeredClients() const
+{
+ return mPriv->clients.keys();
+}
+
+/**
+ * Register a client on D-Bus.
+ *
+ * The client registrar will export the appropriate D-Bus interfaces,
+ * based on the abstract classes subclassed by \param client.
+ *
+ * If each of a client instance should be able to manipulate channels
+ * separately, set unique to true.
+ *
+ * The client name MUST be a non-empty string of ASCII digits, letters, dots
+ * and/or underscores, starting with a letter, and without sets of
+ * two consecutive dots or a dot followed by a digit.
+ *
+ * This method will do nothing if the client is already registered, and \c true
+ * will be returned.
+ *
+ * To unregister a client use unregisterClient().
+ *
+ * \param client The client to register.
+ * \param clientName The client name used to register.
+ * \param unique Whether each of a client instance is able to manipulate
+ * channels separately.
+ * \return \c true if \a client was successfully registered, \c false otherwise.
+ * \sa registeredClients(), unregisterClient()
+ */
+bool ClientRegistrar::registerClient(const AbstractClientPtr &client,
+ const QString &clientName, bool unique)
+{
+ if (!client) {
+ warning() << "Unable to register a null client";
+ return false;
+ }
+
+ if (mPriv->clients.contains(client)) {
+ debug() << "Client already registered";
+ return true;
+ }
+
+ QString busName = QLatin1String("org.freedesktop.Telepathy.Client.");
+ busName.append(clientName);
+ if (unique) {
+ // o.f.T.Client.clientName.<unique_bus_name>_<pointer> should be enough to identify
+ // an unique identifier
+ busName.append(QString(QLatin1String(".%1_%2"))
+ .arg(mPriv->bus.baseService()
+ .replace(QLatin1String(":"), QLatin1String("_"))
+ .replace(QLatin1String("."), QLatin1String("_")))
+ .arg((intptr_t) client.data(), 0, 16));
+ }
+
+ if (mPriv->services.contains(busName) ||
+ !mPriv->bus.registerService(busName)) {
+ warning() << "Unable to register client: busName" <<
+ busName << "already registered";
+ return false;
+ }
+
+ QObject *object = new QObject(this);
+ QStringList interfaces;
+
+ AbstractClientHandler *handler =
+ dynamic_cast<AbstractClientHandler*>(client.data());
+ if (handler) {
+ // export o.f.T.Client.Handler
+ new ClientHandlerAdaptor(this, handler, object);
+ interfaces.append(
+ QLatin1String("org.freedesktop.Telepathy.Client.Handler"));
+ if (handler->wantsRequestNotification()) {
+ // export o.f.T.Client.Interface.Requests
+ new ClientHandlerRequestsAdaptor(this, handler, object);
+ interfaces.append(
+ QLatin1String(
+ "org.freedesktop.Telepathy.Client.Interface.Requests"));
+ }
+ }
+
+ AbstractClientObserver *observer =
+ dynamic_cast<AbstractClientObserver*>(client.data());
+ if (observer) {
+ // export o.f.T.Client.Observer
+ new ClientObserverAdaptor(this, observer, object);
+ interfaces.append(
+ QLatin1String("org.freedesktop.Telepathy.Client.Observer"));
+ }
+
+ AbstractClientApprover *approver =
+ dynamic_cast<AbstractClientApprover*>(client.data());
+ if (approver) {
+ // export o.f.T.Client.Approver
+ new ClientApproverAdaptor(this, approver, object);
+ interfaces.append(
+ QLatin1String("org.freedesktop.Telepathy.Client.Approver"));
+ }
+
+ if (interfaces.isEmpty()) {
+ warning() << "Client does not implement any known interface";
+ // cleanup
+ mPriv->bus.unregisterService(busName);
+ return false;
+ }
+
+ // export o.f.T.Client interface
+ new ClientAdaptor(this, interfaces, object);
+
+ QString objectPath = QString(QLatin1String("/%1")).arg(busName);
+ objectPath.replace(QLatin1String("."), QLatin1String("/"));
+ if (!mPriv->bus.registerObject(objectPath, object)) {
+ // this shouldn't happen, but let's make sure
+ warning() << "Unable to register client: objectPath" <<
+ objectPath << "already registered";
+ // cleanup
+ delete object;
+ mPriv->bus.unregisterService(busName);
+ return false;
+ }
+
+ if (handler) {
+ handler->setRegistered(true);
+ }
+
+ debug() << "Client registered - busName:" << busName <<
+ "objectPath:" << objectPath << "interfaces:" << interfaces;
+
+ mPriv->services.insert(busName);
+ mPriv->clients.insert(client, objectPath);
+ mPriv->clientObjects.insert(client, object);
+
+ return true;
+}
+
+/**
+ * Unregister a client registered using registerClient() on this client
+ * registrar.
+ *
+ * If \a client was not registered previously, \c false will be returned.
+ *
+ * \param client The client to unregister.
+ * \return \c true if \a client was successfully unregistered, \c false otherwise.
+ * \sa registeredClients(), registerClient()
+ */
+bool ClientRegistrar::unregisterClient(const AbstractClientPtr &client)
+{
+ if (!mPriv->clients.contains(client)) {
+ warning() << "Trying to unregister an unregistered client";
+ return false;
+ }
+
+ AbstractClientHandler *handler =
+ dynamic_cast<AbstractClientHandler*>(client.data());
+ if (handler) {
+ handler->setRegistered(false);
+ }
+
+ QString objectPath = mPriv->clients.value(client);
+ mPriv->bus.unregisterObject(objectPath);
+ mPriv->clients.remove(client);
+ QObject *object = mPriv->clientObjects.value(client);
+ // delete object here and it's children (adaptors), to make sure if adaptor
+ // is keeping a static list of adaptors per connection, the list is updated.
+ delete object;
+ mPriv->clientObjects.remove(client);
+
+ QString busName = objectPath.mid(1).replace(QLatin1String("/"),
+ QLatin1String("."));
+ mPriv->bus.unregisterService(busName);
+ mPriv->services.remove(busName);
+
+ debug() << "Client unregistered - busName:" << busName <<
+ "objectPath:" << objectPath;
+
+ return true;
+}
+
+/**
+ * Unregister all clients registered using registerClient() on this client
+ * registrar.
+ *
+ * \sa registeredClients(), registerClient(), unregisterClient()
+ */
+void ClientRegistrar::unregisterClients()
+{
+ // copy the hash as it will be modified
+ QHash<AbstractClientPtr, QString> clients = mPriv->clients;
+
+ QHash<AbstractClientPtr, QString>::const_iterator end =
+ clients.constEnd();
+ QHash<AbstractClientPtr, QString>::const_iterator it =
+ clients.constBegin();
+ while (it != end) {
+ unregisterClient(it.key());
+ ++it;
+ }
+}
+
+} // Tp
diff --git a/TelepathyQt/client-registrar.h b/TelepathyQt/client-registrar.h
new file mode 100644
index 00000000..631dd454
--- /dev/null
+++ b/TelepathyQt/client-registrar.h
@@ -0,0 +1,97 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_client_registrar_h_HEADER_GUARD_
+#define _TelepathyQt_client_registrar_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/AccountFactory>
+#include <TelepathyQt/ChannelFactory>
+#include <TelepathyQt/ConnectionFactory>
+#include <TelepathyQt/ContactFactory>
+#include <TelepathyQt/Object>
+#include <TelepathyQt/Types>
+
+#include <QDBusConnection>
+#include <QString>
+
+namespace Tp
+{
+
+class TP_QT_EXPORT ClientRegistrar : public Object
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(ClientRegistrar)
+
+public:
+ static ClientRegistrarPtr create(const QDBusConnection &bus);
+ static ClientRegistrarPtr create(
+ const AccountFactoryConstPtr &accountFactory =
+ AccountFactory::create(QDBusConnection::sessionBus()),
+ const ConnectionFactoryConstPtr &connectionFactory =
+ ConnectionFactory::create(QDBusConnection::sessionBus()),
+ const ChannelFactoryConstPtr &channelFactory =
+ ChannelFactory::create(QDBusConnection::sessionBus()),
+ const ContactFactoryConstPtr &contactFactory =
+ ContactFactory::create());
+ static ClientRegistrarPtr create(const QDBusConnection &bus,
+ const AccountFactoryConstPtr &accountFactory,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory);
+ static ClientRegistrarPtr create(const AccountManagerPtr &accountManager);
+
+ ~ClientRegistrar();
+
+ QDBusConnection dbusConnection() const;
+
+ AccountFactoryConstPtr accountFactory() const;
+ ConnectionFactoryConstPtr connectionFactory() const;
+ ChannelFactoryConstPtr channelFactory() const;
+ ContactFactoryConstPtr contactFactory() const;
+
+ QList<AbstractClientPtr> registeredClients() const;
+ bool registerClient(const AbstractClientPtr &client,
+ const QString &clientName, bool unique = false);
+ bool unregisterClient(const AbstractClientPtr &client);
+ void unregisterClients();
+
+private:
+ ClientRegistrar(const QDBusConnection &bus,
+ const AccountFactoryConstPtr &accountFactory,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory);
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+
+ static QHash<QPair<QString, QString>, ClientRegistrar *> registrarForConnection;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/client.cpp b/TelepathyQt/client.cpp
new file mode 100644
index 00000000..27c1a749
--- /dev/null
+++ b/TelepathyQt/client.cpp
@@ -0,0 +1,26 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/Client>
+
+#include "TelepathyQt/_gen/cli-client-body.hpp"
+#include "TelepathyQt/_gen/cli-client.moc.hpp"
diff --git a/TelepathyQt/client.h b/TelepathyQt/client.h
new file mode 100644
index 00000000..0835a8bd
--- /dev/null
+++ b/TelepathyQt/client.h
@@ -0,0 +1,32 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_client_h_HEADER_GUARD_
+#define _TelepathyQt_client_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/_gen/cli-client.h>
+
+#endif
diff --git a/TelepathyQt/client.xml b/TelepathyQt/client.xml
new file mode 100644
index 00000000..d4d0f6de
--- /dev/null
+++ b/TelepathyQt/client.xml
@@ -0,0 +1,14 @@
+<tp:spec
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<tp:title>Client interfaces</tp:title>
+
+<xi:include href="../spec/Client.xml"/>
+<xi:include href="../spec/Client_Observer.xml"/>
+<xi:include href="../spec/Client_Approver.xml"/>
+<xi:include href="../spec/Client_Handler.xml"/>
+
+<xi:include href="../spec/Client_Interface_Requests.xml"/>
+
+</tp:spec>
diff --git a/TelepathyQt/connection-capabilities.cpp b/TelepathyQt/connection-capabilities.cpp
new file mode 100644
index 00000000..c6e2526a
--- /dev/null
+++ b/TelepathyQt/connection-capabilities.cpp
@@ -0,0 +1,303 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/ConnectionCapabilities>
+
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+/**
+ * \class ConnectionCapabilities
+ * \ingroup clientconn
+ * \headerfile TelepathyQt/connection-capabilities.h <TelepathyQt/ConnectionCapabilities>
+ *
+ * \brief The ConnectionCapabilities class represents the capabilities of a
+ * Connection.
+ */
+
+/**
+ * Construct a new ConnectionCapabilities object.
+ */
+ConnectionCapabilities::ConnectionCapabilities()
+ : CapabilitiesBase()
+{
+}
+
+/**
+ * Construct a new ConnectionCapabilities object using the give \a rccs.
+ *
+ * \param rccs RequestableChannelClassList representing the capabilities of a
+ * Connection.
+ */
+ConnectionCapabilities::ConnectionCapabilities(const RequestableChannelClassList &rccs)
+ : CapabilitiesBase(rccs, false)
+{
+}
+
+/**
+ * Construct a new ConnectionCapabilities object using the give \a rccSpecs.
+ *
+ * \param rccSpecs RequestableChannelClassSpecList representing the capabilities of a
+ * Connection.
+ */
+ConnectionCapabilities::ConnectionCapabilities(const RequestableChannelClassSpecList &rccSpecs)
+ : CapabilitiesBase(rccSpecs, false)
+{
+}
+
+/**
+ * Class destructor.
+ */
+ConnectionCapabilities::~ConnectionCapabilities()
+{
+}
+
+/**
+ * Return true if named text chatrooms can be joined by providing a
+ * chatroom identifier.
+ *
+ * If the protocol is such that chatrooms can be joined, but only via
+ * a more elaborate D-Bus API than normal (because more information is needed),
+ * then this method will return false.
+ *
+ * \return \c true if Account::ensureTextChatroom() can be expected to work.
+ */
+bool ConnectionCapabilities::textChatrooms() const
+{
+ RequestableChannelClassSpecList rccSpecs = allClassSpecs();
+ foreach (const RequestableChannelClassSpec &rccSpec, rccSpecs) {
+ if (rccSpec.supports(RequestableChannelClassSpec::textChatroom())) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Return whether creating conference media calls is supported.
+ *
+ * \return \c true if supported, \c false otherwise.
+ */
+bool ConnectionCapabilities::conferenceStreamedMediaCalls() const
+{
+ RequestableChannelClassSpecList rccSpecs = allClassSpecs();
+ foreach (const RequestableChannelClassSpec &rccSpec, rccSpecs) {
+ if (rccSpec.supports(RequestableChannelClassSpec::conferenceStreamedMediaCall())) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Return whether creating conference media calls is supported.
+ *
+ * This method will also check whether inviting new contacts when creating a conference media call
+ * channel by providing additional members to initial invitees (as opposed to merging several
+ * channels into one new conference channel) is supported.
+ *
+ * If providing additional members is supported, it is also possible to request conference media
+ * calls with fewer than two (even zero) already established media calls.
+ *
+ * \return \c true if supported, \c false otherwise.
+ */
+bool ConnectionCapabilities::conferenceStreamedMediaCallsWithInvitees() const
+{
+ RequestableChannelClassSpecList rccSpecs = allClassSpecs();
+ foreach (const RequestableChannelClassSpec &rccSpec, rccSpecs) {
+ if (rccSpec.supports(RequestableChannelClassSpec::conferenceStreamedMediaCallWithInvitees())) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Return whether creating conference text chats is supported.
+ *
+ * \return \c true if supported, \c false otherwise.
+ */
+bool ConnectionCapabilities::conferenceTextChats() const
+{
+ RequestableChannelClassSpecList rccSpecs = allClassSpecs();
+ foreach (const RequestableChannelClassSpec &rccSpec, rccSpecs) {
+ if (rccSpec.supports(RequestableChannelClassSpec::conferenceTextChat())) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Return whether creating conference text chats is supported.
+ *
+ * This method will also check whether inviting new contacts when creating a conference text chat
+ * channel by providing additional members to initial invitees (as opposed to merging several
+ * channels into one new conference channel) is supported.
+ *
+ * If providing additional members is supported, it is also possible to request conference text
+ * chats with fewer than two (even zero) already established text chats.
+ *
+ * \return \c true if supported, \c false otherwise.
+ */
+bool ConnectionCapabilities::conferenceTextChatsWithInvitees() const
+{
+ RequestableChannelClassSpecList rccSpecs = allClassSpecs();
+ foreach (const RequestableChannelClassSpec &rccSpec, rccSpecs) {
+ if (rccSpec.supports(RequestableChannelClassSpec::conferenceTextChatWithInvitees())) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Return whether creating conference text chat rooms is supported.
+ *
+ * \return \c true if supported, \c false otherwise.
+ */
+bool ConnectionCapabilities::conferenceTextChatrooms() const
+{
+ RequestableChannelClassSpecList rccSpecs = allClassSpecs();
+ foreach (const RequestableChannelClassSpec &rccSpec, rccSpecs) {
+ if (rccSpec.supports(RequestableChannelClassSpec::conferenceTextChatroom())) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Return whether creating conference text chat rooms is supported.
+ *
+ * This method will also check whether inviting new contacts when creating a conference text chat
+ * room channel by providing additional members to initial invitees (as opposed to merging several
+ * channels into one new conference channel) is supported.
+ *
+ * If providing additional members is supported, it is also possible to request conference text
+ * chat rooms with fewer than two (even zero) already established text chat rooms.
+ *
+ * \return \c true if supported, \c false otherwise.
+ */
+bool ConnectionCapabilities::conferenceTextChatroomsWithInvitees() const
+{
+ RequestableChannelClassSpecList rccSpecs = allClassSpecs();
+ foreach (const RequestableChannelClassSpec &rccSpec, rccSpecs) {
+ if (rccSpec.supports(RequestableChannelClassSpec::conferenceTextChatroomWithInvitees())) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * \deprecated Use contactSearches() instead.
+ */
+bool ConnectionCapabilities::contactSearch()
+{
+ return contactSearches();
+}
+
+/**
+ * \deprecated Use contactSearchesWithSpecificServer() instead.
+ */
+bool ConnectionCapabilities::contactSearchWithSpecificServer() const
+{
+ return contactSearchesWithSpecificServer();
+}
+
+/**
+ * \deprecated Use contactSearchesWithLimit() instead.
+ */
+bool ConnectionCapabilities::contactSearchWithLimit() const
+{
+ return contactSearchesWithLimit();
+}
+
+/**
+ * Return whether creating a ContactSearch channel is supported.
+ *
+ * \return \c true if supported, \c false otherwise.
+ */
+bool ConnectionCapabilities::contactSearches() const
+{
+ RequestableChannelClassSpecList rccSpecs = allClassSpecs();
+ foreach (const RequestableChannelClassSpec &rccSpec, rccSpecs) {
+ if (rccSpec.supports(RequestableChannelClassSpec::contactSearch())) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Return whether creating a ContactSearch channel specifying a server is supported.
+ *
+ * \return \c true if supported, \c false otherwise.
+ */
+bool ConnectionCapabilities::contactSearchesWithSpecificServer() const
+{
+ RequestableChannelClassSpecList rccSpecs = allClassSpecs();
+ foreach (const RequestableChannelClassSpec &rccSpec, rccSpecs) {
+ if (rccSpec.supports(RequestableChannelClassSpec::contactSearchWithSpecificServer())) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Return whether creating a ContactSearch channel specifying a limit is supported.
+ *
+ * \return \c true if supported, \c false otherwise.
+ */
+bool ConnectionCapabilities::contactSearchesWithLimit() const
+{
+ RequestableChannelClassSpecList rccSpecs = allClassSpecs();
+ foreach (const RequestableChannelClassSpec &rccSpec, rccSpecs) {
+ if (rccSpec.supports(RequestableChannelClassSpec::contactSearchWithLimit())) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Return whether creating a StreamTube channel by providing a contact identifier is supported.
+ *
+ * \return \c true if supported, \c false otherwise.
+ */
+bool ConnectionCapabilities::streamTubes() const
+{
+ RequestableChannelClassSpecList rccSpecs = allClassSpecs();
+ foreach (const RequestableChannelClassSpec &rccSpec, rccSpecs) {
+ if (rccSpec.supports(RequestableChannelClassSpec::streamTube())) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // Tp
diff --git a/TelepathyQt/connection-capabilities.h b/TelepathyQt/connection-capabilities.h
new file mode 100644
index 00000000..3ffcc288
--- /dev/null
+++ b/TelepathyQt/connection-capabilities.h
@@ -0,0 +1,77 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_connection_capabilities_h_HEADER_GUARD_
+#define _TelepathyQt_connection_capabilities_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/CapabilitiesBase>
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+class TestBackdoors;
+
+class TP_QT_EXPORT ConnectionCapabilities : public CapabilitiesBase
+{
+public:
+ ConnectionCapabilities();
+ virtual ~ConnectionCapabilities();
+
+ bool textChatrooms() const;
+
+ bool conferenceStreamedMediaCalls() const;
+ bool conferenceStreamedMediaCallsWithInvitees() const;
+ bool conferenceTextChats() const;
+ bool conferenceTextChatsWithInvitees() const;
+ bool conferenceTextChatrooms() const;
+ bool conferenceTextChatroomsWithInvitees() const;
+
+ bool contactSearches() const;
+ bool contactSearchesWithSpecificServer() const;
+ bool contactSearchesWithLimit() const;
+
+ TP_QT_DEPRECATED bool contactSearch();
+ TP_QT_DEPRECATED bool contactSearchWithSpecificServer() const;
+ TP_QT_DEPRECATED bool contactSearchWithLimit() const;
+
+ bool streamTubes() const;
+
+protected:
+ friend class Account;
+ friend class Connection;
+ friend class ProtocolInfo;
+ friend class TestBackdoors;
+
+ ConnectionCapabilities(const RequestableChannelClassList &rccs);
+ ConnectionCapabilities(const RequestableChannelClassSpecList &rccSpecs);
+};
+
+} // Tp
+
+Q_DECLARE_METATYPE(Tp::ConnectionCapabilities);
+
+#endif
diff --git a/TelepathyQt/connection-factory.cpp b/TelepathyQt/connection-factory.cpp
new file mode 100644
index 00000000..02867846
--- /dev/null
+++ b/TelepathyQt/connection-factory.cpp
@@ -0,0 +1,150 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/ConnectionFactory>
+
+#include <TelepathyQt/Connection>
+#include <TelepathyQt/DBusProxy>
+
+namespace Tp
+{
+
+/**
+ * \class ConnectionFactory
+ * \ingroup utils
+ * \headerfile TelepathyQt/connection-factory.h <TelepathyQt/ConnectionFactory>
+ *
+ * \brief The ConnectionFactory class is responsible for constructing Connection
+ * objects according to application-defined settings.
+ *
+ * The class is used by Account and other classes which construct Connection proxy
+ * instances to enable sharing instances of application-defined Connection
+ * subclasses with certain features always ready.
+ */
+
+/**
+ * Create a new ConnectionFactory object.
+ *
+ * Optionally, the \a features to make ready on all constructed proxies can be specified. The
+ * default is to make no features ready. It should be noted that unlike Connection::becomeReady(),
+ * FeatureCore isn't assumed. If no features are specified, which is the default behavior, no
+ * Connection::becomeReady() call is made at all and the proxy won't be Connection::isReady().
+ *
+ * \param bus The QDBusConnection for proxies constructed using this factory to use.
+ * \param features The features to make ready on constructed Connections.
+ * \return A ConnectionFactoryPtr object pointing to the newly created
+ * ConnectionFactory object.
+ */
+ConnectionFactoryPtr ConnectionFactory::create(const QDBusConnection &bus,
+ const Features &features)
+{
+ return ConnectionFactoryPtr(new ConnectionFactory(bus, features));
+}
+
+/**
+ * Construct a new ConnectionFactory object.
+ *
+ * As in create(), it should be noted that unlike Connection::becomeReady(), FeatureCore isn't
+ * assumed. If no \a features are specified, no Connection::becomeReady() call is made at all and
+ * the proxy won't be Connection::isReady().
+ *
+ * \param bus The QDBusConnection for proxies constructed using this factory to use.
+ * \param features The features to make ready on constructed Connections.
+ */
+ConnectionFactory::ConnectionFactory(const QDBusConnection &bus, const Features &features)
+ : FixedFeatureFactory(bus)
+{
+ addFeatures(features);
+}
+
+/**
+ * Class destructor.
+ */
+ConnectionFactory::~ConnectionFactory()
+{
+}
+
+/**
+ * Constructs a Connection proxy and begins making it ready.
+ *
+ * If a valid proxy already exists in the factory cache for the given combination of \a busName and
+ * \a objectPath, it is returned instead. All newly created proxies are automatically cached until
+ * they're either DBusProxy::invalidated() or the last reference to them outside the factory has
+ * been dropped.
+ *
+ * The proxy can be accessed immediately after this function returns using PendingReady::proxy().
+ * The ready operation only finishes, however, when the features specified by features(), if any,
+ * are made ready as much as possible. If the service doesn't support a given feature, they won't
+ * obviously be ready even if the operation finished successfully, as is the case for
+ * Connection::becomeReady().
+ *
+ * \param busName The bus/service name of the D-Bus connection object the proxy is constructed for.
+ * \param objectPath The object path of the connection.
+ * \param chanFactory The channel factory to use for the Connection.
+ * \param contactFactory The channel factory to use for the Connection.
+ * \return A PendingReady operation with the proxy in PendingReady::proxy().
+ */
+PendingReady *ConnectionFactory::proxy(const QString &busName, const QString &objectPath,
+ const ChannelFactoryConstPtr &chanFactory,
+ const ContactFactoryConstPtr &contactFactory) const
+{
+ DBusProxyPtr proxy = cachedProxy(busName, objectPath);
+ if (proxy.isNull()) {
+ proxy = construct(busName, objectPath, chanFactory, contactFactory);
+ }
+
+ return nowHaveProxy(proxy);
+}
+
+/**
+ * Can be used by subclasses to override the Connection subclass constructed by the factory.
+ *
+ * This is automatically called by proxy() to construct proxy instances if no valid cached proxy is
+ * found.
+ *
+ * The default implementation constructs Tp::Connection objects.
+ *
+ * \param busName The bus/service name of the D-Bus Connection object the proxy is constructed for.
+ * \param objectPath The object path of the connection.
+ * \param chanFactory The channel factory to use for the Connection.
+ * \param contactFactory The channel factory to use for the Connection.
+ * \return A pointer to the constructed Connection object.
+ */
+ConnectionPtr ConnectionFactory::construct(const QString &busName, const QString &objectPath,
+ const ChannelFactoryConstPtr &chanFactory,
+ const ContactFactoryConstPtr &contactFactory) const
+{
+ return Connection::create(dbusConnection(), busName, objectPath, chanFactory, contactFactory);
+}
+
+/**
+ * Transforms well-known names to the corresponding unique names, as is appropriate for Connection
+ *
+ * \param uniqueOrWellKnown The name to transform.
+ * \return The unique name corresponding to \a uniqueOrWellKnown (which may be it itself).
+ */
+QString ConnectionFactory::finalBusNameFrom(const QString &uniqueOrWellKnown) const
+{
+ return StatefulDBusProxy::uniqueNameFrom(dbusConnection(), uniqueOrWellKnown);
+}
+
+}
diff --git a/TelepathyQt/connection-factory.h b/TelepathyQt/connection-factory.h
new file mode 100644
index 00000000..70f3b503
--- /dev/null
+++ b/TelepathyQt/connection-factory.h
@@ -0,0 +1,78 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_connection_factory_h_HEADER_GUARD_
+#define _TelepathyQt_connection_factory_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Global>
+#include <TelepathyQt/SharedPtr>
+#include <TelepathyQt/Types>
+
+#include <TelepathyQt/Feature>
+#include <TelepathyQt/FixedFeatureFactory>
+
+// For Q_DISABLE_COPY
+#include <QtGlobal>
+
+#include <QString>
+
+class QDBusConnection;
+
+namespace Tp
+{
+
+class PendingReady;
+
+class TP_QT_EXPORT ConnectionFactory : public FixedFeatureFactory
+{
+public:
+ static ConnectionFactoryPtr create(const QDBusConnection &bus,
+ const Features &features = Features());
+
+ virtual ~ConnectionFactory();
+
+ PendingReady *proxy(const QString &busName, const QString &objectPath,
+ const ChannelFactoryConstPtr &chanFactory,
+ const ContactFactoryConstPtr &contactFactory) const;
+
+protected:
+ ConnectionFactory(const QDBusConnection &bus, const Features &features);
+
+ virtual ConnectionPtr construct(const QString &busName, const QString &objectPath,
+ const ChannelFactoryConstPtr &chanFactory,
+ const ContactFactoryConstPtr &contactFactory) const;
+ virtual QString finalBusNameFrom(const QString &uniqueOrWellKnown) const;
+ // Nothing we'd like to prepare()
+ // Fixed features
+
+private:
+ struct Private;
+ Private *mPriv; // Currently unused, just for future-proofing
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/connection-internal.h b/TelepathyQt/connection-internal.h
new file mode 100644
index 00000000..2d210a5c
--- /dev/null
+++ b/TelepathyQt/connection-internal.h
@@ -0,0 +1,61 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_connection_internal_h_HEADER_GUARD_
+#define _TelepathyQt_connection_internal_h_HEADER_GUARD_
+
+#include <TelepathyQt/Connection>
+
+#include <TelepathyQt/PendingReady>
+
+#include <QSet>
+
+namespace Tp
+{
+
+class TP_QT_NO_EXPORT Connection::PendingConnect : public PendingReady
+{
+ Q_OBJECT
+
+public:
+ PendingConnect(const ConnectionPtr &connection, const Features &requestedFeatures);
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onConnectReply(QDBusPendingCallWatcher *);
+ TP_QT_NO_EXPORT void onStatusChanged(Tp::ConnectionStatus newStatus);
+ TP_QT_NO_EXPORT void onBecomeReadyReply(Tp::PendingOperation *);
+ TP_QT_NO_EXPORT void onConnInvalidated(Tp::DBusProxy *proxy, const QString &error, const QString &message);
+
+private:
+ friend class ConnectionLowlevel;
+};
+
+class ConnectionHelper
+{
+public:
+ static QString statusReasonToErrorName(Tp::ConnectionStatusReason reason,
+ Tp::ConnectionStatus oldStatus);
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/connection-lowlevel.h b/TelepathyQt/connection-lowlevel.h
new file mode 100644
index 00000000..63a3429f
--- /dev/null
+++ b/TelepathyQt/connection-lowlevel.h
@@ -0,0 +1,96 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_connection_lowlevel_h_HEADER_GUARD_
+#define _TelepathyQt_connection_lowlevel_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+class Connection;
+class PendingChannel;
+class PendingContactAttributes;
+class PendingHandles;
+class PendingOperation;
+class PendingReady;
+
+class TP_QT_EXPORT ConnectionLowlevel : public QObject, public RefCounted
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(ConnectionLowlevel)
+
+public:
+ ~ConnectionLowlevel();
+
+ bool isValid() const;
+ ConnectionPtr connection() const;
+
+ PendingReady *requestConnect(const Features &requestedFeatures = Features());
+ PendingOperation *requestDisconnect();
+
+ SimpleStatusSpecMap allowedPresenceStatuses() const;
+ uint maxPresenceStatusMessageLength() const;
+
+ PendingOperation *setSelfPresence(const QString &status, const QString &statusMessage);
+
+ PendingChannel *createChannel(const QVariantMap &request);
+ PendingChannel *createChannel(const QVariantMap &request, int timeout);
+ PendingChannel *ensureChannel(const QVariantMap &request);
+ PendingChannel *ensureChannel(const QVariantMap &request, int timeout);
+
+ PendingHandles *requestHandles(HandleType handleType, const QStringList &names);
+ PendingHandles *referenceHandles(HandleType handleType, const UIntList &handles);
+
+ PendingContactAttributes *contactAttributes(const UIntList &handles,
+ const QStringList &interfaces, bool reference = true);
+ QStringList contactAttributeInterfaces() const;
+
+ void injectContactIds(const HandleIdentifierMap &contactIds);
+ void injectContactId(uint handle, const QString &contactId);
+
+private:
+ friend class Connection;
+ friend class ContactManager;
+ friend class PendingContacts;
+
+ TP_QT_NO_EXPORT ConnectionLowlevel(Connection *parent);
+
+ TP_QT_NO_EXPORT bool hasImmortalHandles() const;
+
+ TP_QT_NO_EXPORT bool hasContactId(uint handle) const;
+ TP_QT_NO_EXPORT QString contactId(uint handle) const;
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/connection-manager-internal.h b/TelepathyQt/connection-manager-internal.h
new file mode 100644
index 00000000..65083237
--- /dev/null
+++ b/TelepathyQt/connection-manager-internal.h
@@ -0,0 +1,159 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_connection_manager_internal_h_HEADER_GUARD_
+#define _TelepathyQt_connection_manager_internal_h_HEADER_GUARD_
+
+#include <TelepathyQt/PendingStringList>
+
+#include <QDBusConnection>
+#include <QLatin1String>
+#include <QQueue>
+#include <QSet>
+#include <QString>
+#include <QWeakPointer>
+
+namespace Tp
+{
+
+class ConnectionManager;
+class ReadinessHelper;
+
+struct TP_QT_NO_EXPORT ConnectionManager::Private
+{
+ Private(ConnectionManager *parent, const QString &name,
+ const ConnectionFactoryConstPtr &connFactory,
+ const ChannelFactoryConstPtr &chanFactory,
+ const ContactFactoryConstPtr &contactFactory);
+ ~Private();
+
+ bool parseConfigFile();
+
+ static void introspectMain(Private *self);
+ void introspectProtocolsLegacy();
+ void introspectParametersLegacy();
+
+ static QString makeBusName(const QString &name);
+ static QString makeObjectPath(const QString &name);
+
+ class PendingNames;
+ class ProtocolWrapper;
+
+ // Public object
+ ConnectionManager *parent;
+ ConnectionManagerLowlevelPtr lowlevel;
+
+ QString name;
+
+ // Instance of generated interface class
+ Client::ConnectionManagerInterface *baseInterface;
+
+ // Mandatory properties interface proxy
+ Client::DBus::PropertiesInterface *properties;
+
+ ReadinessHelper *readinessHelper;
+
+ ConnectionFactoryConstPtr connFactory;
+ ChannelFactoryConstPtr chanFactory;
+ ContactFactoryConstPtr contactFactory;
+
+ // Introspection
+ QQueue<QString> parametersQueue;
+ ProtocolInfoList protocols;
+ QSet<SharedPtr<ProtocolWrapper> > wrappers;
+};
+
+struct TP_QT_NO_EXPORT ConnectionManagerLowlevel::Private
+{
+ Private(ConnectionManager *cm)
+ : cm(QWeakPointer<ConnectionManager>(cm))
+ {
+ }
+
+ QWeakPointer<ConnectionManager> cm;
+};
+
+class TP_QT_NO_EXPORT ConnectionManager::Private::PendingNames : public PendingStringList
+{
+ Q_OBJECT
+
+public:
+ PendingNames(const QDBusConnection &bus);
+ ~PendingNames() {};
+
+private Q_SLOTS:
+ void onCallFinished(QDBusPendingCallWatcher *);
+ void continueProcessing();
+
+private:
+ void invokeMethod(const QLatin1String &method);
+ void parseResult(const QStringList &names);
+
+ QQueue<QLatin1String> mMethodsQueue;
+ QSet<QString> mResult;
+ QDBusConnection mBus;
+};
+
+class TP_QT_NO_EXPORT ConnectionManager::Private::ProtocolWrapper :
+ public StatelessDBusProxy, public OptionalInterfaceFactory<ProtocolWrapper>
+{
+ Q_OBJECT
+
+public:
+ static const Feature FeatureCore;
+
+ ProtocolWrapper(const QDBusConnection &bus, const QString &busName,
+ const QString &objectPath,
+ const QString &cmName, const QString &name, const QVariantMap &props);
+ ~ProtocolWrapper();
+
+ ProtocolInfo info() const { return mInfo; }
+
+ inline Client::DBus::PropertiesInterface *propertiesInterface() const
+ {
+ return optionalInterface<Client::DBus::PropertiesInterface>(BypassInterfaceCheck);
+ }
+
+private Q_SLOTS:
+ void gotMainProperties(QDBusPendingCallWatcher *watcher);
+ void gotAvatarsProperties(QDBusPendingCallWatcher *watcher);
+ void gotPresenceProperties(QDBusPendingCallWatcher *watcher);
+
+private:
+ static void introspectMain(ProtocolWrapper *self);
+ void introspectAvatars();
+ void introspectPresence();
+
+ void continueIntrospection();
+
+ void fillRCCs();
+ bool receiveProperties(const QVariantMap &props);
+
+ ReadinessHelper *mReadinessHelper;
+ ProtocolInfo mInfo;
+ QVariantMap mImmutableProps;
+ QQueue<void (ProtocolWrapper::*)()> introspectQueue;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/connection-manager-lowlevel.h b/TelepathyQt/connection-manager-lowlevel.h
new file mode 100644
index 00000000..69c67330
--- /dev/null
+++ b/TelepathyQt/connection-manager-lowlevel.h
@@ -0,0 +1,64 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_connection_manager_lowlevel_h_HEADER_GUARD_
+#define _TelepathyQt_connection_manager_lowlevel_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+class PendingConnection;
+
+class TP_QT_EXPORT ConnectionManagerLowlevel : public QObject, public RefCounted
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(ConnectionManagerLowlevel)
+
+public:
+ ~ConnectionManagerLowlevel();
+
+ bool isValid() const;
+ ConnectionManagerPtr connectionManager() const;
+
+ PendingConnection *requestConnection(const QString &protocolName,
+ const QVariantMap &parameters);
+
+private:
+ friend class ConnectionManager;
+
+ TP_QT_NO_EXPORT ConnectionManagerLowlevel(ConnectionManager *parent);
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/connection-manager.cpp b/TelepathyQt/connection-manager.cpp
new file mode 100644
index 00000000..f5d2817d
--- /dev/null
+++ b/TelepathyQt/connection-manager.cpp
@@ -0,0 +1,1076 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/ConnectionManager>
+#include <TelepathyQt/ConnectionManagerLowlevel>
+#include "TelepathyQt/connection-manager-internal.h"
+
+#include "TelepathyQt/_gen/cli-connection-manager-body.hpp"
+#include "TelepathyQt/_gen/cli-connection-manager.moc.hpp"
+#include "TelepathyQt/_gen/connection-manager.moc.hpp"
+#include "TelepathyQt/_gen/connection-manager-internal.moc.hpp"
+#include "TelepathyQt/_gen/connection-manager-lowlevel.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/ConnectionCapabilities>
+#include <TelepathyQt/DBus>
+#include <TelepathyQt/PendingConnection>
+#include <TelepathyQt/PendingReady>
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/ManagerFile>
+#include <TelepathyQt/Types>
+
+#include <QDBusConnectionInterface>
+#include <QQueue>
+#include <QStringList>
+#include <QTimer>
+
+namespace
+{
+
+bool checkValidProtocolName(const QString &protocolName)
+{
+ if (!protocolName[0].isLetter()) {
+ return false;
+ }
+
+ int length = protocolName.length();
+ if (length <= 1) {
+ return true;
+ }
+
+ QChar ch;
+ for (int i = 1; i < length; ++i) {
+ ch = protocolName[i];
+ if (!ch.isLetterOrNumber() && ch != QLatin1Char('-')) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+}
+
+namespace Tp
+{
+
+ConnectionManager::Private::PendingNames::PendingNames(const QDBusConnection &bus)
+ : PendingStringList(SharedPtr<RefCounted>()),
+ mBus(bus)
+{
+ mMethodsQueue.enqueue(QLatin1String("ListNames"));
+ mMethodsQueue.enqueue(QLatin1String("ListActivatableNames"));
+ QTimer::singleShot(0, this, SLOT(continueProcessing()));
+}
+
+void ConnectionManager::Private::PendingNames::onCallFinished(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QStringList> reply = *watcher;
+
+ if (!reply.isError()) {
+ parseResult(reply.value());
+ continueProcessing();
+ } else {
+ warning() << "Failure: error " << reply.error().name() <<
+ ": " << reply.error().message();
+ setFinishedWithError(reply.error());
+ }
+
+ watcher->deleteLater();
+}
+
+void ConnectionManager::Private::PendingNames::continueProcessing()
+{
+ if (!mMethodsQueue.isEmpty()) {
+ QLatin1String method = mMethodsQueue.dequeue();
+ invokeMethod(method);
+ }
+ else {
+ debug() << "Success: list" << mResult;
+ setResult(mResult.toList());
+ setFinished();
+ }
+}
+
+void ConnectionManager::Private::PendingNames::invokeMethod(const QLatin1String &method)
+{
+ QDBusPendingCall call = mBus.interface()->asyncCallWithArgumentList(
+ method, QList<QVariant>());
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this);
+ connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(onCallFinished(QDBusPendingCallWatcher*)));
+}
+
+void ConnectionManager::Private::PendingNames::parseResult(const QStringList &names)
+{
+ foreach (const QString name, names) {
+ if (name.startsWith(QLatin1String(TELEPATHY_INTERFACE_CONNECTION_MANAGER "."))) {
+ mResult << name.right(name.length() - 44);
+ }
+ }
+}
+
+const Feature ConnectionManager::Private::ProtocolWrapper::FeatureCore =
+ Feature(QLatin1String(ConnectionManager::Private::ProtocolWrapper::staticMetaObject.className()),
+ 0, true);
+
+ConnectionManager::Private::ProtocolWrapper::ProtocolWrapper(
+ const QDBusConnection &bus,
+ const QString &busName, const QString &objectPath,
+ const QString &cmName, const QString &name, const QVariantMap &props)
+ : StatelessDBusProxy(bus, busName, objectPath, FeatureCore),
+ OptionalInterfaceFactory<ProtocolWrapper>(this),
+ mReadinessHelper(readinessHelper()),
+ mInfo(ProtocolInfo(cmName, name)),
+ mImmutableProps(props)
+{
+ fillRCCs();
+
+ ReadinessHelper::Introspectables introspectables;
+
+ // As Protocol does not have predefined statuses let's simulate one (0)
+ ReadinessHelper::Introspectable introspectableCore(
+ QSet<uint>() << 0, // makesSenseForStatuses
+ Features(), // dependsOnFeatures
+ QStringList(), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &ProtocolWrapper::introspectMain,
+ this);
+ introspectables[FeatureCore] = introspectableCore;
+
+ mReadinessHelper->addIntrospectables(introspectables);
+}
+
+ConnectionManager::Private::ProtocolWrapper::~ProtocolWrapper()
+{
+}
+
+void ConnectionManager::Private::ProtocolWrapper::introspectMain(
+ ConnectionManager::Private::ProtocolWrapper *self)
+{
+ Client::DBus::PropertiesInterface *properties = self->propertiesInterface();
+ Q_ASSERT(properties != 0);
+
+ if (self->receiveProperties(self->mImmutableProps)) {
+ debug() << "Got everything we want from the immutable props for" <<
+ self->info().name();
+
+ if (self->hasInterface(TP_QT_IFACE_PROTOCOL_INTERFACE_AVATARS)) {
+ self->introspectQueue.enqueue(&ProtocolWrapper::introspectAvatars);
+ } else {
+ debug() << "Full functionality requires CM support for the Protocol.Avatars interface";
+ }
+
+ if (self->hasInterface(TP_QT_IFACE_PROTOCOL_INTERFACE_PRESENCE)) {
+ self->introspectQueue.enqueue(&ProtocolWrapper::introspectPresence);
+ } else {
+ debug() << "Full functionality requires CM support for the Protocol.Presence interface";
+ }
+
+ self->continueIntrospection();
+ return;
+ }
+
+ debug() << "Not enough immutable properties, calling Properties::GetAll(Protocol) for" <<
+ self->info().name();
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+ properties->GetAll(TP_QT_IFACE_PROTOCOL),
+ self);
+ self->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotMainProperties(QDBusPendingCallWatcher*)));
+}
+
+void ConnectionManager::Private::ProtocolWrapper::introspectAvatars()
+{
+ Client::DBus::PropertiesInterface *properties = propertiesInterface();
+ Q_ASSERT(properties != 0);
+
+ debug() << "Calling Properties::GetAll(Protocol.Avatars)";
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+ properties->GetAll(TP_QT_IFACE_PROTOCOL_INTERFACE_AVATARS),
+ this);
+ connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotAvatarsProperties(QDBusPendingCallWatcher*)));
+}
+
+void ConnectionManager::Private::ProtocolWrapper::introspectPresence()
+{
+ Client::DBus::PropertiesInterface *properties = propertiesInterface();
+ Q_ASSERT(properties != 0);
+
+ debug() << "Calling Properties::GetAll(Protocol.Presence)";
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+ properties->GetAll(TP_QT_IFACE_PROTOCOL_INTERFACE_PRESENCE),
+ this);
+ connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotPresenceProperties(QDBusPendingCallWatcher*)));
+}
+
+void ConnectionManager::Private::ProtocolWrapper::continueIntrospection()
+{
+ if (introspectQueue.isEmpty()) {
+ mReadinessHelper->setIntrospectCompleted(FeatureCore, true);
+ } else {
+ (this->*(introspectQueue.dequeue()))();
+ }
+}
+
+void ConnectionManager::Private::ProtocolWrapper::gotMainProperties(
+ QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QVariantMap> reply = *watcher;
+ QVariantMap props;
+
+ if (!reply.isError()) {
+ debug() << "Got reply to Properties.GetAll(Protocol)";
+
+ QVariantMap unqualifiedProps = reply.value();
+ QVariantMap qualifiedProps;
+ foreach (QString unqualified, unqualifiedProps.keys()) {
+ qualifiedProps.insert(
+ QString(QLatin1String("%1.%2")).
+ arg(TP_QT_IFACE_PROTOCOL).
+ arg(unqualified),
+ unqualifiedProps.value(unqualified));
+ }
+ receiveProperties(qualifiedProps);
+ } else {
+ warning().nospace() <<
+ "Properties.GetAll(Protocol) failed: " <<
+ reply.error().name() << ": " << reply.error().message();
+ warning() << " Full functionality requires CM support for the Protocol interface";
+ }
+
+ if (hasInterface(TP_QT_IFACE_PROTOCOL_INTERFACE_AVATARS)) {
+ introspectQueue.enqueue(&ProtocolWrapper::introspectAvatars);
+ } else {
+ debug() << "Full functionality requires CM support for the Protocol.Avatars interface";
+ }
+
+ if (hasInterface(TP_QT_IFACE_PROTOCOL_INTERFACE_PRESENCE)) {
+ introspectQueue.enqueue(&ProtocolWrapper::introspectPresence);
+ } else {
+ debug() << "Full functionality requires CM support for the Protocol.Presence interface";
+ }
+
+ continueIntrospection();
+
+ watcher->deleteLater();
+}
+
+void ConnectionManager::Private::ProtocolWrapper::gotAvatarsProperties(
+ QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QVariantMap> reply = *watcher;
+ QVariantMap props;
+
+ if (!reply.isError()) {
+ debug() << "Got reply to Properties.GetAll(Protocol.Avatars)";
+ props = reply.value();
+
+ QStringList supportedMimeTypes = qdbus_cast<QStringList>(
+ props[QLatin1String("SupportedAvatarMIMETypes")]);
+ uint minHeight = qdbus_cast<uint>(props[QLatin1String("MinimumAvatarHeight")]);
+ uint maxHeight = qdbus_cast<uint>(props[QLatin1String("MaximumAvatarHeight")]);
+ uint recommendedHeight = qdbus_cast<uint>(
+ props[QLatin1String("RecommendedAvatarHeight")]);
+ uint minWidth = qdbus_cast<uint>(props[QLatin1String("MinimumAvatarWidth")]);
+ uint maxWidth = qdbus_cast<uint>(props[QLatin1String("MaximumAvatarWidth")]);
+ uint recommendedWidth = qdbus_cast<uint>(
+ props[QLatin1String("RecommendedAvatarWidth")]);
+ uint maxBytes = qdbus_cast<uint>(props[QLatin1String("MaximumAvatarBytes")]);
+ mInfo.setAvatarRequirements(AvatarSpec(supportedMimeTypes,
+ minHeight, maxHeight, recommendedHeight,
+ minWidth, maxWidth, recommendedWidth,
+ maxBytes));
+ } else {
+ warning().nospace() <<
+ "Properties.GetAll(Protocol.Avatars) failed: " <<
+ reply.error().name() << ": " << reply.error().message();
+ warning() << " Full functionality requires CM support for the Protocol.Avatars interface";
+ }
+
+ continueIntrospection();
+
+ watcher->deleteLater();
+}
+
+void ConnectionManager::Private::ProtocolWrapper::gotPresenceProperties(
+ QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QVariantMap> reply = *watcher;
+ QVariantMap props;
+
+ if (!reply.isError()) {
+ debug() << "Got reply to Properties.GetAll(Protocol.Presence)";
+ props = reply.value();
+ mInfo.setAllowedPresenceStatuses(PresenceSpecList(qdbus_cast<SimpleStatusSpecMap>(
+ props[QLatin1String("Statuses")])));
+ } else {
+ warning().nospace() <<
+ "Properties.GetAll(Protocol.Presence) failed: " <<
+ reply.error().name() << ": " << reply.error().message();
+ warning() << " Full functionality requires CM support for the Protocol.Presence interface";
+ }
+
+ continueIntrospection();
+
+ watcher->deleteLater();
+}
+
+void ConnectionManager::Private::ProtocolWrapper::fillRCCs()
+{
+ RequestableChannelClassList classes;
+
+ QVariantMap fixedProps;
+ QStringList allowedProps;
+
+ // Text chatrooms
+ fixedProps.insert(
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType"),
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_TEXT));
+ fixedProps.insert(
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType"),
+ static_cast<uint>(HandleTypeRoom));
+
+ RequestableChannelClass textChatroom = {fixedProps, allowedProps};
+ classes.append(textChatroom);
+
+ // 1-1 text chats
+ fixedProps[QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType")] =
+ static_cast<uint>(HandleTypeContact);
+
+ RequestableChannelClass text = {fixedProps, allowedProps};
+ classes.append(text);
+
+ // Media calls
+ fixedProps[QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType")] =
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA);
+
+ RequestableChannelClass media = {fixedProps, allowedProps};
+ classes.append(media);
+
+ // Initially audio-only calls
+ allowedProps.push_back(
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialAudio"));
+
+ RequestableChannelClass initialAudio = {fixedProps, allowedProps};
+ classes.append(initialAudio);
+
+ // Initially AV calls
+ allowedProps.push_back(
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialVideo"));
+
+ RequestableChannelClass initialAV = {fixedProps, allowedProps};
+ classes.append(initialAV);
+
+ // Initially video-only calls
+ allowedProps.removeAll(
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialAudio"));
+
+ RequestableChannelClass initialVideo = {fixedProps, allowedProps};
+ classes.append(initialVideo);
+
+ // That also settles upgrading calls, because the media classes don't have ImmutableStreams
+
+ mInfo.setRequestableChannelClasses(classes);
+}
+
+bool ConnectionManager::Private::ProtocolWrapper::receiveProperties(const QVariantMap &props)
+{
+ bool gotEverything =
+ props.contains(QLatin1String(TELEPATHY_INTERFACE_PROTOCOL ".Interfaces")) &&
+ props.contains(QLatin1String(TELEPATHY_INTERFACE_PROTOCOL ".Parameters")) &&
+ props.contains(QLatin1String(TELEPATHY_INTERFACE_PROTOCOL ".ConnectionInterfaces")) &&
+ props.contains(QLatin1String(TELEPATHY_INTERFACE_PROTOCOL ".RequestableChannelClasses")) &&
+ props.contains(QLatin1String(TELEPATHY_INTERFACE_PROTOCOL ".VCardField")) &&
+ props.contains(QLatin1String(TELEPATHY_INTERFACE_PROTOCOL ".EnglishName")) &&
+ props.contains(QLatin1String(TELEPATHY_INTERFACE_PROTOCOL ".Icon"));
+
+ setInterfaces(qdbus_cast<QStringList>(
+ props[QLatin1String(TELEPATHY_INTERFACE_PROTOCOL ".Interfaces")]));
+ mReadinessHelper->setInterfaces(interfaces());
+
+ ParamSpecList parameters = qdbus_cast<ParamSpecList>(
+ props[QLatin1String(TELEPATHY_INTERFACE_PROTOCOL ".Parameters")]);
+ foreach (const ParamSpec &spec, parameters) {
+ mInfo.addParameter(spec);
+ }
+
+ mInfo.setVCardField(qdbus_cast<QString>(
+ props[QLatin1String(TELEPATHY_INTERFACE_PROTOCOL ".VCardField")]));
+ QString englishName = qdbus_cast<QString>(
+ props[QLatin1String(TELEPATHY_INTERFACE_PROTOCOL ".EnglishName")]);
+ if (englishName.isEmpty()) {
+ QStringList words = mInfo.name().split(QLatin1Char('-'));
+ for (int i = 0; i < words.size(); ++i) {
+ words[i][0] = words[i].at(0).toUpper();
+ }
+ englishName = words.join(QLatin1String(" "));
+ }
+ mInfo.setEnglishName(englishName);
+ QString iconName = qdbus_cast<QString>(
+ props[QLatin1String(TELEPATHY_INTERFACE_PROTOCOL ".Icon")]);
+ if (iconName.isEmpty()) {
+ iconName = QString(QLatin1String("im-%1")).arg(mInfo.name());
+ }
+ mInfo.setIconName(iconName);
+
+ // Don't overwrite the everything-is-possible RCCs with an empty list if there is no RCCs key
+ if (props.contains(QLatin1String(TELEPATHY_INTERFACE_PROTOCOL ".RequestableChannelClasses"))) {
+ mInfo.setRequestableChannelClasses(qdbus_cast<RequestableChannelClassList>(
+ props[QLatin1String(TELEPATHY_INTERFACE_PROTOCOL ".RequestableChannelClasses")]));
+ }
+
+ return gotEverything;
+}
+
+ConnectionManager::Private::Private(ConnectionManager *parent, const QString &name,
+ const ConnectionFactoryConstPtr &connFactory,
+ const ChannelFactoryConstPtr &chanFactory,
+ const ContactFactoryConstPtr &contactFactory)
+ : parent(parent),
+ lowlevel(ConnectionManagerLowlevelPtr(new ConnectionManagerLowlevel(parent))),
+ name(name),
+ baseInterface(new Client::ConnectionManagerInterface(parent)),
+ properties(parent->interface<Client::DBus::PropertiesInterface>()),
+ readinessHelper(parent->readinessHelper()),
+ connFactory(connFactory),
+ chanFactory(chanFactory),
+ contactFactory(contactFactory)
+{
+ debug() << "Creating new ConnectionManager:" << parent->busName();
+
+ if (connFactory->dbusConnection().name() != parent->dbusConnection().name()) {
+ warning() << " The D-Bus connection in the connection factory is not the proxy connection";
+ }
+
+ if (chanFactory->dbusConnection().name() != parent->dbusConnection().name()) {
+ warning() << " The D-Bus connection in the channel factory is not the proxy connection";
+ }
+
+ ReadinessHelper::Introspectables introspectables;
+
+ // As ConnectionManager does not have predefined statuses let's simulate one (0)
+ ReadinessHelper::Introspectable introspectableCore(
+ QSet<uint>() << 0, // makesSenseForStatuses
+ Features(), // dependsOnFeatures
+ QStringList(), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &Private::introspectMain,
+ this);
+ introspectables[FeatureCore] = introspectableCore;
+
+ readinessHelper->addIntrospectables(introspectables);
+}
+
+ConnectionManager::Private::~Private()
+{
+ delete baseInterface;
+}
+
+bool ConnectionManager::Private::parseConfigFile()
+{
+ ManagerFile f(name);
+ if (!f.isValid()) {
+ return false;
+ }
+
+ foreach (const QString &protocol, f.protocols()) {
+ ProtocolInfo info(name, protocol);
+
+ foreach (const ParamSpec &spec, f.parameters(protocol)) {
+ info.addParameter(spec);
+ }
+ info.setRequestableChannelClasses(
+ f.requestableChannelClasses(protocol));
+ info.setVCardField(f.vcardField(protocol));
+ info.setEnglishName(f.englishName(protocol));
+ info.setIconName(f.iconName(protocol));
+ info.setAllowedPresenceStatuses(f.allowedPresenceStatuses(protocol));
+ info.setAvatarRequirements(f.avatarRequirements(protocol));
+
+ protocols.append(info);
+ }
+
+ return true;
+}
+
+void ConnectionManager::Private::introspectMain(ConnectionManager::Private *self)
+{
+ if (self->parseConfigFile()) {
+ self->readinessHelper->setIntrospectCompleted(FeatureCore, true);
+ return;
+ }
+
+ warning() << "Error parsing config file for connection manager"
+ << self->name << "- introspecting";
+
+ debug() << "Calling Properties::GetAll(ConnectionManager)";
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+ self->properties->GetAll(
+ QLatin1String(TELEPATHY_INTERFACE_CONNECTION_MANAGER)),
+ self->parent);
+ self->parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotMainProperties(QDBusPendingCallWatcher*)));
+}
+
+void ConnectionManager::Private::introspectProtocolsLegacy()
+{
+ debug() << "Calling ConnectionManager::ListProtocols";
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+ baseInterface->ListProtocols(), parent);
+ parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotProtocolsLegacy(QDBusPendingCallWatcher*)));
+}
+
+void ConnectionManager::Private::introspectParametersLegacy()
+{
+ foreach (const QString &protocolName, parametersQueue) {
+ debug() << "Calling ConnectionManager::GetParameters(" << protocolName << ")";
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+ baseInterface->GetParameters(protocolName), parent);
+ parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotParametersLegacy(QDBusPendingCallWatcher*)));
+ }
+}
+
+QString ConnectionManager::Private::makeBusName(const QString &name)
+{
+ return QString(TP_QT_CONNECTION_MANAGER_BUS_NAME_BASE).append(name);
+}
+
+QString ConnectionManager::Private::makeObjectPath(const QString &name)
+{
+ return QString(TP_QT_CONNECTION_MANAGER_OBJECT_PATH_BASE).append(name);
+}
+
+/**
+ * \class ConnectionManagerLowlevel
+ * \ingroup clientcm
+ * \headerfile TelepathyQt/connection-manager-lowlevel.h <TelepathyQt/ConnectionManagerLowlevel>
+ *
+ * \brief The ConnectionManagerLowlevel class extends ConnectionManager with
+ * support to low-level features.
+ */
+
+ConnectionManagerLowlevel::ConnectionManagerLowlevel(ConnectionManager *cm)
+ : mPriv(new Private(cm))
+{
+}
+
+ConnectionManagerLowlevel::~ConnectionManagerLowlevel()
+{
+ delete mPriv;
+}
+
+bool ConnectionManagerLowlevel::isValid() const
+{
+ return !mPriv->cm.isNull();
+}
+
+ConnectionManagerPtr ConnectionManagerLowlevel::connectionManager() const
+{
+ return ConnectionManagerPtr(mPriv->cm);
+}
+
+/**
+ * \class ConnectionManager
+ * \ingroup clientcm
+ * \headerfile TelepathyQt/connection-manager.h <TelepathyQt/ConnectionManager>
+ *
+ * \brief The ConnectionManager class represents a Telepathy connection manager.
+ *
+ * Connection managers allow connections to be made on one or more protocols.
+ *
+ * Most client applications should use this functionality via the
+ * AccountManager, to allow connections to be shared between client
+ * applications.
+ */
+
+/**
+ * Feature representing the core that needs to become ready to make the
+ * ConnectionManager object usable.
+ *
+ * Note that this feature must be enabled in order to use most ConnectionManager
+ * methods.
+ * See specific methods documentation for more details.
+ *
+ * When calling isReady(), becomeReady(), this feature is implicitly added
+ * to the requested features.
+ */
+const Feature ConnectionManager::FeatureCore = Feature(QLatin1String(ConnectionManager::staticMetaObject.className()), 0, true);
+
+/**
+ * Create a new ConnectionManager object.
+ *
+ * The instance will use a connection factory creating Tp::Connection objects with no features
+ * ready, and a channel factory creating stock Telepathy-Qt4 channel subclasses, as appropriate,
+ * with no features ready.
+ *
+ * \param bus QDBusConnection to use.
+ * \param name Name of the connection manager.
+ * \return A ConnectionManagerPtr object pointing to the newly created
+ * ConnectionManager object.
+ */
+ConnectionManagerPtr ConnectionManager::create(const QDBusConnection &bus, const QString &name)
+{
+ return ConnectionManagerPtr(new ConnectionManager(QDBusConnection::sessionBus(), name,
+ ConnectionFactory::create(bus), ChannelFactory::create(bus),
+ ContactFactory::create()));
+}
+
+/**
+ * Create a new ConnectionManager using QDBusConnection::sessionBus() and the given factories.
+ *
+ * The channel factory is passed to any Connection objects created by this manager
+ * object. In fact, they're not used directly by ConnectionManager at all.
+ *
+ * A warning is printed if the factories are for a bus different from QDBusConnection::sessionBus().
+ *
+ * \param name Name of the connection manager.
+ * \param connectionFactory The connection factory to use.
+ * \param channelFactory The channel factory to use.
+ * \param contactFactory The contact factory to use.
+ * \return A ConnectionManagerPtr object pointing to the newly created
+ * ConnectionManager object.
+ */
+ConnectionManagerPtr ConnectionManager::create(const QString &name,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory)
+{
+ return ConnectionManagerPtr(new ConnectionManager(QDBusConnection::sessionBus(), name,
+ connectionFactory, channelFactory, contactFactory));
+}
+
+/**
+ * Create a new ConnectionManager using the given \a bus and the given factories.
+ *
+ * The channel factory is passed to any Connection objects created by this manager
+ * object. In fact, they're not used directly by ConnectionManager at all.
+ *
+ * A warning is printed if the factories are for a bus different from QDBusConnection::sessionBus().
+ *
+ * \param bus QDBusConnection to use.
+ * \param name Name of the connection manager.
+ * \param connectionFactory The connection factory to use.
+ * \param channelFactory The channel factory to use.
+ * \param contactFactory The contact factory to use.
+ * \return A ConnectionManagerPtr object pointing to the newly created
+ * ConnectionManager object.
+ */
+ConnectionManagerPtr ConnectionManager::create(const QDBusConnection &bus,
+ const QString &name,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory)
+{
+ return ConnectionManagerPtr(new ConnectionManager(bus, name,
+ connectionFactory, channelFactory, contactFactory));
+}
+
+/**
+ * Construct a new ConnectionManager object using the given \a bus.
+ *
+ * \param bus QDBusConnection to use.
+ * \param name Name of the connection manager.
+ */
+ConnectionManager::ConnectionManager(const QDBusConnection &bus,
+ const QString &name,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory)
+ : StatelessDBusProxy(bus, Private::makeBusName(name),
+ Private::makeObjectPath(name), FeatureCore),
+ OptionalInterfaceFactory<ConnectionManager>(this),
+ mPriv(new Private(this, name, connectionFactory, channelFactory, contactFactory))
+{
+}
+
+/**
+ * Class destructor.
+ */
+ConnectionManager::~ConnectionManager()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the short name of the connection manager (e.g. "gabble").
+ *
+ * \return The name of the connection manager.
+ */
+QString ConnectionManager::name() const
+{
+ return mPriv->name;
+}
+
+/**
+ * Return the connection factory used by this manager.
+ *
+ * Only read access is provided. This allows constructing object instances and examining the object
+ * construction settings, but not changing settings. Allowing changes would lead to tricky
+ * situations where objects constructed at different times by the manager would have unpredictably
+ * different construction settings (eg. subclass).
+ *
+ * \return A read-only pointer to the ConnectionFactory object.
+ */
+ConnectionFactoryConstPtr ConnectionManager::connectionFactory() const
+{
+ return mPriv->connFactory;
+}
+
+/**
+ * Return the channel factory used by this manager.
+ *
+ * Only read access is provided. This allows constructing object instances and examining the object
+ * construction settings, but not changing settings. Allowing changes would lead to tricky
+ * situations where objects constructed at different times by the manager would have unpredictably
+ * different construction settings (eg. subclass).
+ *
+ * \return A read-only pointer to the ChannelFactory object.
+ */
+ChannelFactoryConstPtr ConnectionManager::channelFactory() const
+{
+ return mPriv->chanFactory;
+}
+
+/**
+ * Return the contact factory used by this manager.
+ *
+ * Only read access is provided. This allows constructing object instances and examining the object
+ * construction settings, but not changing settings. Allowing changes would lead to tricky
+ * situations where objects constructed at different times by the manager would have unpredictably
+ * different construction settings (eg. subclass).
+ *
+ * \return A read-only pointer to the ContactFactory object.
+ */
+ContactFactoryConstPtr ConnectionManager::contactFactory() const
+{
+ return mPriv->contactFactory;
+}
+
+/**
+ * Return a list of strings identifying the protocols supported by this
+ * connection manager, as described in the \telepathy_spec (e.g. "jabber").
+ *
+ * These identifiers are not intended to be displayed to users directly; user
+ * interfaces are responsible for mapping them to localized strings.
+ *
+ * This method requires ConnectionManager::FeatureCore to be ready.
+ *
+ * \return A list of supported protocols.
+ */
+QStringList ConnectionManager::supportedProtocols() const
+{
+ QStringList protocols;
+ foreach (const ProtocolInfo &info, mPriv->protocols) {
+ protocols.append(info.name());
+ }
+ return protocols;
+}
+
+/**
+ * Return a list of protocols info for this connection manager.
+ *
+ * Note that the returned ProtocolInfoList contents should not be freed.
+ *
+ * This method requires ConnectionManager::FeatureCore to be ready.
+ *
+ * \return A list of á¹”rotocolInfo.
+ */
+const ProtocolInfoList &ConnectionManager::protocols() const
+{
+ return mPriv->protocols;
+}
+
+/**
+ * Return whether this connection manager implements the protocol specified by
+ * \a protocolName.
+ *
+ * This method requires ConnectionManager::FeatureCore to be ready.
+ *
+ * \return \c true if the protocol is supported, \c false otherwise.
+ * \sa protocol(), protocols()
+ */
+bool ConnectionManager::hasProtocol(const QString &protocolName) const
+{
+ foreach (const ProtocolInfo &info, mPriv->protocols) {
+ if (info.name() == protocolName) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Return the ProtocolInfo object for the protocol specified by
+ * \a protocolName.
+ *
+ * This method requires ConnectionManager::FeatureCore to be ready.
+ *
+ * \param protocolName The name of the protocol.
+ * \return A ProtocolInfo object which will return \c for ProtocolInfo::isValid() if the protocol
+ * specified by \a protocolName is not supported.
+ * \sa hasProtocol()
+ */
+ProtocolInfo ConnectionManager::protocol(const QString &protocolName) const
+{
+ foreach (const ProtocolInfo &info, mPriv->protocols) {
+ if (info.name() == protocolName) {
+ return info;
+ }
+ }
+ return ProtocolInfo();
+}
+
+/**
+ * Request a Connection object representing a given account on a given protocol
+ * with the given parameters.
+ *
+ * Return a pending operation representing the Connection object which will
+ * succeed when the connection has been created or fail if an error occurred.
+ *
+ * \param protocol Name of the protocol to create the account for.
+ * \param parameters Account parameters.
+ * \return A PendingOperation which will emit PendingConnection::finished when
+ * the account has been created of failed its creation process.
+ */
+PendingConnection *ConnectionManagerLowlevel::requestConnection(const QString &protocol,
+ const QVariantMap &parameters)
+{
+ if (!isValid()) {
+ return new PendingConnection(TP_QT_ERROR_NOT_AVAILABLE,
+ QLatin1String("The connection manager has been destroyed already"));
+ }
+
+ return new PendingConnection(ConnectionManagerPtr(mPriv->cm),
+ protocol, parameters);
+}
+
+/**
+ * Return a pending operation from which a list of all installed connection
+ * manager short names (such as "gabble" or "haze") can be retrieved if it
+ * succeeds.
+ *
+ * \return A PendingStringList which will emit PendingStringList::finished
+ * when this object has finished or failed getting the connection
+ * manager names.
+ */
+PendingStringList *ConnectionManager::listNames(const QDBusConnection &bus)
+{
+ return new ConnectionManager::Private::PendingNames(bus);
+}
+
+ConnectionManagerLowlevelPtr ConnectionManager::lowlevel()
+{
+ return mPriv->lowlevel;
+}
+
+ConnectionManagerLowlevelConstPtr ConnectionManager::lowlevel() const
+{
+ return mPriv->lowlevel;
+}
+
+/**
+ * Return the Client::ConnectionManagerInterface for this ConnectionManager.
+ * This method is protected since the convenience methods provided by this
+ * class should generally be used instead of calling D-Bus methods
+ * directly.
+ *
+ * \return A pointer to the existing Client::ConnectionManagerInterface for this
+ * ConnectionManager object.
+ */
+Client::ConnectionManagerInterface *ConnectionManager::baseInterface() const
+{
+ return mPriv->baseInterface;
+}
+
+/**** Private ****/
+void ConnectionManager::gotMainProperties(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QVariantMap> reply = *watcher;
+ QVariantMap props;
+
+ if (!reply.isError()) {
+ debug() << "Got reply to Properties.GetAll(ConnectionManager)";
+ props = reply.value();
+
+ // If Interfaces is not supported, the spec says to assume it's
+ // empty, so keep the empty list mPriv was initialized with
+ if (props.contains(QLatin1String("Interfaces"))) {
+ setInterfaces(qdbus_cast<QStringList>(props[QLatin1String("Interfaces")]));
+ mPriv->readinessHelper->setInterfaces(interfaces());
+ }
+ } else {
+ warning().nospace() <<
+ "Properties.GetAll(ConnectionManager) failed: " <<
+ reply.error().name() << ": " << reply.error().message();
+
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureCore, false, reply.error());
+ watcher->deleteLater();
+ return;
+ }
+
+ ProtocolPropertiesMap protocolsMap =
+ qdbus_cast<ProtocolPropertiesMap>(props[QLatin1String("Protocols")]);
+ if (!protocolsMap.isEmpty()) {
+ ProtocolPropertiesMap::const_iterator i = protocolsMap.constBegin();
+ ProtocolPropertiesMap::const_iterator end = protocolsMap.constEnd();
+ while (i != end) {
+ QString protocolName = i.key();
+ if (!checkValidProtocolName(protocolName)) {
+ warning() << "Protocol has an invalid name" << protocolName << "- ignoring";
+ continue;
+ }
+
+ QString escapedProtocolName = protocolName;
+ escapedProtocolName.replace(QLatin1Char('-'), QLatin1Char('_'));
+ QString protocolPath = QString(
+ QLatin1String("%1/%2")).arg(objectPath()).arg(escapedProtocolName);
+ SharedPtr<Private::ProtocolWrapper> wrapper = SharedPtr<Private::ProtocolWrapper>(
+ new Private::ProtocolWrapper(
+ dbusConnection(), busName(), protocolPath,
+ mPriv->name, protocolName, i.value()));
+ connect(wrapper->becomeReady(),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onProtocolReady(Tp::PendingOperation*)));
+ mPriv->wrappers.insert(wrapper);
+ ++i;
+ }
+ } else {
+ mPriv->introspectProtocolsLegacy();
+ }
+
+ watcher->deleteLater();
+}
+
+void ConnectionManager::gotProtocolsLegacy(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QStringList> reply = *watcher;
+ QStringList protocolsNames;
+
+ if (!reply.isError()) {
+ debug() << "Got reply to ConnectionManager.ListProtocols";
+ protocolsNames = reply.value();
+
+ foreach (const QString &protocolName, protocolsNames) {
+ mPriv->protocols.append(ProtocolInfo(mPriv->name, protocolName));
+ mPriv->parametersQueue.enqueue(protocolName);
+ }
+
+ mPriv->introspectParametersLegacy();
+ } else {
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureCore, false, reply.error());
+
+ warning().nospace() <<
+ "ConnectionManager.ListProtocols failed: " <<
+ reply.error().name() << ": " << reply.error().message();
+
+ // FIXME shouldn't this invalidate the CM?
+ }
+
+ watcher->deleteLater();
+}
+
+void ConnectionManager::gotParametersLegacy(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<ParamSpecList> reply = *watcher;
+ QString protocolName = mPriv->parametersQueue.dequeue();
+ bool found = false;
+ int pos = 0;
+ foreach (const ProtocolInfo &info, mPriv->protocols) {
+ if (info.name() == protocolName) {
+ found = true;
+ break;
+ }
+ ++pos;
+ }
+ Q_ASSERT(found);
+
+ if (!reply.isError()) {
+ debug() << QString(QLatin1String("Got reply to ConnectionManager.GetParameters(%1)")).arg(protocolName);
+ ParamSpecList parameters = reply.value();
+ ProtocolInfo &info = mPriv->protocols[pos];
+ foreach (const ParamSpec &spec, parameters) {
+ debug() << "Parameter" << spec.name << "has flags" << spec.flags
+ << "and signature" << spec.signature;
+
+ info.addParameter(spec);
+ }
+ } else {
+ // let's remove this protocol as we can't get the params
+ mPriv->protocols.removeAt(pos);
+
+ warning().nospace() <<
+ QString(QLatin1String("ConnectionManager.GetParameters(%1) failed: ")).arg(protocolName) <<
+ reply.error().name() << ": " << reply.error().message();
+ }
+
+ if (mPriv->parametersQueue.isEmpty()) {
+ if (!mPriv->protocols.isEmpty()) {
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureCore, true);
+ } else {
+ // we could not retrieve the params for any protocol, fail core.
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureCore, false, reply.error());
+ }
+ }
+
+ watcher->deleteLater();
+}
+
+void ConnectionManager::onProtocolReady(Tp::PendingOperation *op)
+{
+ PendingReady *pr = qobject_cast<PendingReady*>(op);
+ SharedPtr<Private::ProtocolWrapper> wrapper =
+ SharedPtr<Private::ProtocolWrapper>::qObjectCast(pr->proxy());
+ ProtocolInfo info = wrapper->info();
+
+ mPriv->wrappers.remove(wrapper);
+
+ if (!op->isError()) {
+ mPriv->protocols.append(info);
+ } else {
+ warning().nospace() << "Protocol(" << info.name() << ")::becomeReady "
+ "failed: " << op->errorName() << ": " << op->errorMessage();
+ }
+
+ if (mPriv->wrappers.isEmpty()) {
+ if (!mPriv->protocols.isEmpty()) {
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureCore, true);
+ } else {
+ // we could not make any Protocol objects ready, fail core.
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureCore, false,
+ op->errorName(), op->errorMessage());
+ }
+ }
+}
+
+} // Tp
diff --git a/TelepathyQt/connection-manager.h b/TelepathyQt/connection-manager.h
new file mode 100644
index 00000000..6e7ca00b
--- /dev/null
+++ b/TelepathyQt/connection-manager.h
@@ -0,0 +1,125 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_connection_manager_h_HEADER_GUARD_
+#define _TelepathyQt_connection_manager_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/_gen/cli-connection-manager.h>
+
+#include <TelepathyQt/ChannelFactory>
+#include <TelepathyQt/ConnectionFactory>
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/ContactFactory>
+#include <TelepathyQt/DBus>
+#include <TelepathyQt/DBusProxy>
+#include <TelepathyQt/OptionalInterfaceFactory>
+#include <TelepathyQt/ProtocolInfo>
+#include <TelepathyQt/ProtocolParameter>
+#include <TelepathyQt/ReadinessHelper>
+#include <TelepathyQt/SharedPtr>
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+class ConnectionManagerLowlevel;
+class PendingConnection;
+class PendingStringList;
+
+class TP_QT_EXPORT ConnectionManager : public StatelessDBusProxy,
+ public OptionalInterfaceFactory<ConnectionManager>
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(ConnectionManager)
+ Q_PROPERTY(QString name READ name)
+ Q_PROPERTY(QStringList supportedProtocols READ supportedProtocols)
+ Q_PROPERTY(ProtocolInfoList protocols READ protocols)
+
+public:
+ static const Feature FeatureCore;
+
+ static ConnectionManagerPtr create(const QDBusConnection &bus,
+ const QString &name);
+ static ConnectionManagerPtr create(const QString &name,
+ const ConnectionFactoryConstPtr &connectionFactory =
+ ConnectionFactory::create(QDBusConnection::sessionBus()),
+ const ChannelFactoryConstPtr &channelFactory =
+ ChannelFactory::create(QDBusConnection::sessionBus()),
+ const ContactFactoryConstPtr &contactFactory =
+ ContactFactory::create());
+ static ConnectionManagerPtr create(const QDBusConnection &bus,
+ const QString &name,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory =
+ ContactFactory::create());
+
+ virtual ~ConnectionManager();
+
+ QString name() const;
+
+ ConnectionFactoryConstPtr connectionFactory() const;
+ ChannelFactoryConstPtr channelFactory() const;
+ ContactFactoryConstPtr contactFactory() const;
+
+ QStringList supportedProtocols() const;
+ const ProtocolInfoList &protocols() const;
+ bool hasProtocol(const QString &protocolName) const;
+ ProtocolInfo protocol(const QString &protocolName) const;
+
+ static PendingStringList *listNames(
+ const QDBusConnection &bus = QDBusConnection::sessionBus());
+
+#if defined(BUILDING_TP_QT) || defined(TP_QT_ENABLE_LOWLEVEL_API)
+ ConnectionManagerLowlevelPtr lowlevel();
+ ConnectionManagerLowlevelConstPtr lowlevel() const;
+#endif
+
+protected:
+ ConnectionManager(const QDBusConnection &bus, const QString &name,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory);
+
+ Client::ConnectionManagerInterface *baseInterface() const;
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void gotMainProperties(QDBusPendingCallWatcher *);
+ TP_QT_NO_EXPORT void gotProtocolsLegacy(QDBusPendingCallWatcher *);
+ TP_QT_NO_EXPORT void gotParametersLegacy(QDBusPendingCallWatcher *);
+ TP_QT_NO_EXPORT void onProtocolReady(Tp::PendingOperation *);
+
+private:
+ friend class PendingConnection;
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/connection-manager.xml b/TelepathyQt/connection-manager.xml
new file mode 100644
index 00000000..597a78df
--- /dev/null
+++ b/TelepathyQt/connection-manager.xml
@@ -0,0 +1,12 @@
+<tp:spec
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<tp:title>Connection Manager interfaces</tp:title>
+
+<xi:include href="../spec/Connection_Manager.xml"/>
+<xi:include href="../spec/Protocol.xml"/>
+<xi:include href="../spec/Protocol_Interface_Avatars.xml"/>
+<xi:include href="../spec/Protocol_Interface_Presence.xml"/>
+
+</tp:spec>
diff --git a/TelepathyQt/connection.cpp b/TelepathyQt/connection.cpp
new file mode 100644
index 00000000..e7a69a8d
--- /dev/null
+++ b/TelepathyQt/connection.cpp
@@ -0,0 +1,2578 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/Connection>
+#include <TelepathyQt/ConnectionLowlevel>
+#include "TelepathyQt/connection-internal.h"
+
+#include "TelepathyQt/_gen/cli-connection.moc.hpp"
+#include "TelepathyQt/_gen/cli-connection-body.hpp"
+#include "TelepathyQt/_gen/connection.moc.hpp"
+#include "TelepathyQt/_gen/connection-internal.moc.hpp"
+#include "TelepathyQt/_gen/connection-lowlevel.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/ChannelFactory>
+#include <TelepathyQt/ConnectionCapabilities>
+#include <TelepathyQt/ContactFactory>
+#include <TelepathyQt/ContactManager>
+#include <TelepathyQt/PendingChannel>
+#include <TelepathyQt/PendingContactAttributes>
+#include <TelepathyQt/PendingContacts>
+#include <TelepathyQt/PendingFailure>
+#include <TelepathyQt/PendingHandles>
+#include <TelepathyQt/PendingReady>
+#include <TelepathyQt/PendingVariantMap>
+#include <TelepathyQt/PendingVoid>
+#include <TelepathyQt/ReferencedHandles>
+
+#include <QMap>
+#include <QMetaObject>
+#include <QMutex>
+#include <QMutexLocker>
+#include <QPair>
+#include <QQueue>
+#include <QString>
+#include <QTimer>
+#include <QtGlobal>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT Connection::Private
+{
+ Private(Connection *parent,
+ const ChannelFactoryConstPtr &chanFactory,
+ const ContactFactoryConstPtr &contactFactory);
+ ~Private();
+
+ void init();
+
+ static void introspectMain(Private *self);
+ void introspectMainFallbackStatus();
+ void introspectMainFallbackInterfaces();
+ void introspectMainFallbackSelfHandle();
+ void introspectCapabilities();
+ void introspectContactAttributeInterfaces();
+ static void introspectSelfContact(Private *self);
+ static void introspectSimplePresence(Private *self);
+ static void introspectRoster(Private *self);
+ static void introspectRosterGroups(Private *self);
+ static void introspectBalance(Private *self);
+ static void introspectConnected(Private *self);
+
+ void continueMainIntrospection();
+ void setCurrentStatus(uint status);
+ void forceCurrentStatus(uint status);
+ void setInterfaces(const QStringList &interfaces);
+
+ // Should always be used instead of directly using baseclass invalidate()
+ void invalidateResetCaps(const QString &errorName, const QString &errorMessage);
+
+ struct HandleContext;
+
+ // Public object
+ Connection *parent;
+ ConnectionLowlevelPtr lowlevel;
+
+ // Factories
+ ChannelFactoryConstPtr chanFactory;
+ ContactFactoryConstPtr contactFactory;
+
+ // Instance of generated interface class
+ Client::ConnectionInterface *baseInterface;
+
+ // Mandatory properties interface proxy
+ Client::DBus::PropertiesInterface *properties;
+
+ // Optional interface proxies
+ Client::ConnectionInterfaceSimplePresenceInterface *simplePresence;
+
+ ReadinessHelper *readinessHelper;
+
+ // Introspection
+ QQueue<void (Private::*)()> introspectMainQueue;
+
+ // FeatureCore
+ // keep pendingStatus and pendingStatusReason until we emit statusChanged
+ // so Connection::status() and Connection::statusReason() are consistent
+ bool introspectingConnected;
+
+ uint pendingStatus;
+ uint pendingStatusReason;
+ uint status;
+ uint statusReason;
+ ErrorDetails errorDetails;
+
+ uint selfHandle;
+
+ bool immortalHandles;
+
+ ConnectionCapabilities caps;
+
+ ContactManagerPtr contactManager;
+
+ // FeatureSelfContact
+ bool introspectingSelfContact;
+ bool reintrospectSelfContactRequired;
+ ContactPtr selfContact;
+ QStringList contactAttributeInterfaces;
+
+ // FeatureSimplePresence
+ SimpleStatusSpecMap simplePresenceStatuses;
+ uint maxPresenceStatusMessageLength;
+
+ // FeatureAccountBalance
+ CurrencyAmount accountBalance;
+
+ // misc
+ // (Bus connection name, service name) -> HandleContext
+ static QMap<QPair<QString, QString>, HandleContext *> handleContexts;
+ static QMutex handleContextsLock;
+ HandleContext *handleContext;
+
+ QString cmName;
+ QString protocolName;
+};
+
+struct TP_QT_NO_EXPORT ConnectionLowlevel::Private
+{
+ Private(Connection *conn)
+ : conn(QWeakPointer<Connection>(conn))
+ {
+ }
+
+ QWeakPointer<Connection> conn;
+ HandleIdentifierMap contactsIds;
+};
+
+// Handle tracking
+struct TP_QT_NO_EXPORT Connection::Private::HandleContext
+{
+ struct Type
+ {
+ QMap<uint, uint> refcounts;
+ QSet<uint> toRelease;
+ uint requestsInFlight;
+ bool releaseScheduled;
+
+ Type()
+ : requestsInFlight(0),
+ releaseScheduled(false)
+ {
+ }
+ };
+
+ HandleContext()
+ : refcount(0)
+ {
+ }
+
+ int refcount;
+ QMutex lock;
+ QMap<uint, Type> types;
+};
+
+Connection::Private::Private(Connection *parent,
+ const ChannelFactoryConstPtr &chanFactory,
+ const ContactFactoryConstPtr &contactFactory)
+ : parent(parent),
+ lowlevel(ConnectionLowlevelPtr(new ConnectionLowlevel(parent))),
+ chanFactory(chanFactory),
+ contactFactory(contactFactory),
+ baseInterface(new Client::ConnectionInterface(parent)),
+ properties(parent->interface<Client::DBus::PropertiesInterface>()),
+ simplePresence(0),
+ readinessHelper(parent->readinessHelper()),
+ introspectingConnected(false),
+ pendingStatus((uint) -1),
+ pendingStatusReason(ConnectionStatusReasonNoneSpecified),
+ status((uint) -1),
+ statusReason(ConnectionStatusReasonNoneSpecified),
+ selfHandle(0),
+ immortalHandles(false),
+ contactManager(ContactManagerPtr(new ContactManager(parent))),
+ introspectingSelfContact(false),
+ reintrospectSelfContactRequired(false),
+ maxPresenceStatusMessageLength(0),
+ handleContext(0)
+{
+ accountBalance.amount = 0;
+ accountBalance.scale = 0;
+
+ Q_ASSERT(properties != 0);
+
+ if (chanFactory->dbusConnection().name() != parent->dbusConnection().name()) {
+ warning() << " The D-Bus connection in the channel factory is not the proxy connection";
+ }
+
+ init();
+
+ ReadinessHelper::Introspectables introspectables;
+
+ ReadinessHelper::Introspectable introspectableCore(
+ QSet<uint>() << (uint) -1 <<
+ ConnectionStatusDisconnected <<
+ ConnectionStatusConnected, // makesSenseForStatuses
+ Features(), // dependsOnFeatures (none)
+ QStringList(), // dependsOnInterfaces (none)
+ (ReadinessHelper::IntrospectFunc) &Private::introspectMain,
+ this);
+ introspectables[FeatureCore] = introspectableCore;
+
+ ReadinessHelper::Introspectable introspectableSelfContact(
+ QSet<uint>() << ConnectionStatusConnected, // makesSenseForStatuses
+ Features() << FeatureCore, // dependsOnFeatures (core)
+ QStringList(), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &Private::introspectSelfContact,
+ this);
+ introspectables[FeatureSelfContact] = introspectableSelfContact;
+
+ ReadinessHelper::Introspectable introspectableSimplePresence(
+ QSet<uint>() << ConnectionStatusDisconnected <<
+ ConnectionStatusConnected, // makesSenseForStatuses
+ Features() << FeatureCore, // dependsOnFeatures (core)
+ QStringList() << QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &Private::introspectSimplePresence,
+ this);
+ introspectables[FeatureSimplePresence] = introspectableSimplePresence;
+
+ ReadinessHelper::Introspectable introspectableRoster(
+ QSet<uint>() << ConnectionStatusConnected, // makesSenseForStatuses
+ Features() << FeatureCore, // dependsOnFeatures (core)
+ QStringList() << QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_CONTACTS), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &Private::introspectRoster,
+ this);
+ introspectables[FeatureRoster] = introspectableRoster;
+
+ ReadinessHelper::Introspectable introspectableRosterGroups(
+ QSet<uint>() << ConnectionStatusConnected, // makesSenseForStatuses
+ Features() << FeatureRoster, // dependsOnFeatures (core)
+ QStringList() << QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_REQUESTS), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &Private::introspectRosterGroups,
+ this);
+ introspectables[FeatureRosterGroups] = introspectableRosterGroups;
+
+ ReadinessHelper::Introspectable introspectableBalance(
+ QSet<uint>() << ConnectionStatusConnected, // makesSenseForStatuses
+ Features() << FeatureCore, // dependsOnFeatures (core)
+ QStringList() << QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_BALANCE), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &Private::introspectBalance,
+ this);
+ introspectables[FeatureAccountBalance] = introspectableBalance;
+
+ ReadinessHelper::Introspectable introspectableConnected(
+ QSet<uint>() << (uint) -1 <<
+ ConnectionStatusDisconnected <<
+ ConnectionStatusConnecting <<
+ ConnectionStatusConnected, // makesSenseForStatuses
+ Features() << FeatureCore, // dependsOnFeatures (none)
+ QStringList(), // dependsOnInterfaces (none)
+ (ReadinessHelper::IntrospectFunc) &Private::introspectConnected,
+ this);
+ introspectables[FeatureConnected] = introspectableConnected;
+
+ readinessHelper->addIntrospectables(introspectables);
+ readinessHelper->setCurrentStatus(status);
+ parent->connect(readinessHelper,
+ SIGNAL(statusReady(uint)),
+ SLOT(onStatusReady(uint)));
+
+ // FIXME: QRegExp probably isn't the most efficient possible way to parse
+ // this :-)
+ QRegExp rx(QLatin1String("^" TP_QT_CONNECTION_OBJECT_PATH_BASE
+ "([_A-Za-z][_A-Za-z0-9]*)" // cap(1) is the CM
+ "/([_A-Za-z][_A-Za-z0-9]*)" // cap(2) is the protocol
+ "/([_A-Za-z][_A-Za-z0-9]*)" // account-specific part
+ ));
+
+ if (rx.exactMatch(parent->objectPath())) {
+ cmName = rx.cap(1);
+ protocolName = rx.cap(2);
+ } else {
+ warning() << "Connection object path is not spec-compliant, "
+ "trying again with a different account-specific part check";
+
+ rx = QRegExp(QLatin1String("^" TP_QT_CONNECTION_OBJECT_PATH_BASE
+ "([_A-Za-z][_A-Za-z0-9]*)" // cap(1) is the CM
+ "/([_A-Za-z][_A-Za-z0-9]*)" // cap(2) is the protocol
+ "/([_A-Za-z0-9]*)" // account-specific part
+ ));
+ if (rx.exactMatch(parent->objectPath())) {
+ cmName = rx.cap(1);
+ protocolName = rx.cap(2);
+ } else {
+ warning() << "Not a valid Connection object path:" <<
+ parent->objectPath();
+ }
+ }
+}
+
+Connection::Private::~Private()
+{
+ contactManager->resetRoster();
+
+ // Clear selfContact so its handle will be released cleanly before the
+ // handleContext
+ selfContact.reset();
+
+ QMutexLocker locker(&handleContextsLock);
+ // All handle contexts locked, so safe
+ if (!--handleContext->refcount) {
+ if (!immortalHandles) {
+ debug() << "Destroying HandleContext";
+
+ foreach (uint handleType, handleContext->types.keys()) {
+ HandleContext::Type type = handleContext->types[handleType];
+
+ if (!type.refcounts.empty()) {
+ debug() << " Still had references to" <<
+ type.refcounts.size() << "handles, releasing now";
+ baseInterface->ReleaseHandles(handleType, type.refcounts.keys());
+ }
+
+ if (!type.toRelease.empty()) {
+ debug() << " Was going to release" <<
+ type.toRelease.size() << "handles, doing that now";
+ baseInterface->ReleaseHandles(handleType, type.toRelease.toList());
+ }
+ }
+
+ }
+
+ handleContexts.remove(qMakePair(baseInterface->connection().name(),
+ parent->objectPath()));
+ delete handleContext;
+ } else {
+ Q_ASSERT(handleContext->refcount > 0);
+ }
+}
+
+void Connection::Private::init()
+{
+ debug() << "Connecting to ConnectionError()";
+ parent->connect(baseInterface,
+ SIGNAL(ConnectionError(QString,QVariantMap)),
+ SLOT(onConnectionError(QString,QVariantMap)));
+ debug() << "Connecting to StatusChanged()";
+ parent->connect(baseInterface,
+ SIGNAL(StatusChanged(uint,uint)),
+ SLOT(onStatusChanged(uint,uint)));
+ debug() << "Connecting to SelfHandleChanged()";
+ parent->connect(baseInterface,
+ SIGNAL(SelfHandleChanged(uint)),
+ SLOT(onSelfHandleChanged(uint)));
+
+ QMutexLocker locker(&handleContextsLock);
+ QString busConnectionName = baseInterface->connection().name();
+
+ if (handleContexts.contains(qMakePair(busConnectionName, parent->objectPath()))) {
+ debug() << "Reusing existing HandleContext for" << parent->objectPath();
+ handleContext = handleContexts[
+ qMakePair(busConnectionName, parent->objectPath())];
+ } else {
+ debug() << "Creating new HandleContext for" << parent->objectPath();
+ handleContext = new HandleContext;
+ handleContexts[
+ qMakePair(busConnectionName, parent->objectPath())] = handleContext;
+ }
+
+ // All handle contexts locked, so safe
+ ++handleContext->refcount;
+}
+
+void Connection::Private::introspectMain(Connection::Private *self)
+{
+ debug() << "Calling Properties::GetAll(Connection)";
+ QDBusPendingCallWatcher *watcher =
+ new QDBusPendingCallWatcher(
+ self->properties->GetAll(QLatin1String(TELEPATHY_INTERFACE_CONNECTION)),
+ self->parent);
+ self->parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotMainProperties(QDBusPendingCallWatcher*)));
+}
+
+void Connection::Private::introspectMainFallbackStatus()
+{
+ debug() << "Calling GetStatus()";
+ QDBusPendingCallWatcher *watcher =
+ new QDBusPendingCallWatcher(baseInterface->GetStatus(),
+ parent);
+ parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotStatus(QDBusPendingCallWatcher*)));
+}
+
+void Connection::Private::introspectMainFallbackInterfaces()
+{
+ debug() << "Calling GetInterfaces()";
+ QDBusPendingCallWatcher *watcher =
+ new QDBusPendingCallWatcher(baseInterface->GetInterfaces(),
+ parent);
+ parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotInterfaces(QDBusPendingCallWatcher*)));
+}
+
+void Connection::Private::introspectMainFallbackSelfHandle()
+{
+ debug() << "Calling GetSelfHandle()";
+ QDBusPendingCallWatcher *watcher =
+ new QDBusPendingCallWatcher(baseInterface->GetSelfHandle(),
+ parent);
+ parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotSelfHandle(QDBusPendingCallWatcher*)));
+}
+
+void Connection::Private::introspectCapabilities()
+{
+ debug() << "Retrieving capabilities";
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+ properties->Get(
+ QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_REQUESTS),
+ QLatin1String("RequestableChannelClasses")), parent);
+ parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotCapabilities(QDBusPendingCallWatcher*)));
+}
+
+void Connection::Private::introspectContactAttributeInterfaces()
+{
+ debug() << "Retrieving contact attribute interfaces";
+ QDBusPendingCall call =
+ properties->Get(
+ QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_CONTACTS),
+ QLatin1String("ContactAttributeInterfaces"));
+ QDBusPendingCallWatcher *watcher =
+ new QDBusPendingCallWatcher(call, parent);
+ parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotContactAttributeInterfaces(QDBusPendingCallWatcher*)));
+}
+
+void Connection::Private::introspectSelfContact(Connection::Private *self)
+{
+ debug() << "Building self contact";
+
+ Q_ASSERT(!self->introspectingSelfContact);
+
+ self->introspectingSelfContact = true;
+ self->reintrospectSelfContactRequired = false;
+
+ PendingContacts *contacts = self->contactManager->contactsForHandles(
+ UIntList() << self->selfHandle);
+ self->parent->connect(contacts,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(gotSelfContact(Tp::PendingOperation*)));
+}
+
+void Connection::Private::introspectSimplePresence(Connection::Private *self)
+{
+ Q_ASSERT(self->properties != 0);
+
+ debug() << "Calling Properties::Get("
+ "Connection.I.SimplePresence.Statuses)";
+ QDBusPendingCall call =
+ self->properties->GetAll(
+ QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE));
+ QDBusPendingCallWatcher *watcher =
+ new QDBusPendingCallWatcher(call, self->parent);
+ self->parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotSimpleStatuses(QDBusPendingCallWatcher*)));
+}
+
+void Connection::Private::introspectRoster(Connection::Private *self)
+{
+ debug() << "Introspecting roster";
+
+ PendingOperation *op = self->contactManager->introspectRoster();
+ self->parent->connect(op,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onIntrospectRosterFinished(Tp::PendingOperation*)));
+}
+
+void Connection::Private::introspectRosterGroups(Connection::Private *self)
+{
+ debug() << "Introspecting roster groups";
+
+ PendingOperation *op = self->contactManager->introspectRosterGroups();
+ self->parent->connect(op,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onIntrospectRosterGroupsFinished(Tp::PendingOperation*)));
+}
+
+void Connection::Private::introspectBalance(Connection::Private *self)
+{
+ debug() << "Introspecting balance";
+
+ // we already checked if balance interface exists, so bypass requests
+ // interface checking
+ Client::ConnectionInterfaceBalanceInterface *iface =
+ self->parent->interface<Client::ConnectionInterfaceBalanceInterface>();
+
+ debug() << "Connecting to Balance.BalanceChanged";
+ self->parent->connect(iface,
+ SIGNAL(BalanceChanged(Tp::CurrencyAmount)),
+ SLOT(onBalanceChanged(Tp::CurrencyAmount)));
+
+ debug() << "Retrieving balance";
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+ self->properties->Get(
+ QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_BALANCE),
+ QLatin1String("AccountBalance")), self->parent);
+ self->parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotBalance(QDBusPendingCallWatcher*)));
+}
+
+void Connection::Private::introspectConnected(Connection::Private *self)
+{
+ Q_ASSERT(!self->introspectingConnected);
+ self->introspectingConnected = true;
+
+ if (self->pendingStatus == ConnectionStatusConnected) {
+ self->readinessHelper->setIntrospectCompleted(FeatureConnected, true);
+ self->introspectingConnected = false;
+ }
+}
+
+void Connection::Private::continueMainIntrospection()
+{
+ if (!parent->isValid()) {
+ debug() << parent << "stopping main introspection, as it has been invalidated";
+ return;
+ }
+
+ if (introspectMainQueue.isEmpty()) {
+ readinessHelper->setIntrospectCompleted(FeatureCore, true);
+ } else {
+ (this->*(introspectMainQueue.dequeue()))();
+ }
+}
+
+void Connection::Private::setCurrentStatus(uint status)
+{
+ // ReadinessHelper waits for all in-flight introspection ops to finish for the current status
+ // before proceeding to a new one.
+ //
+ // Therefore we don't need any safeguarding here to prevent finishing introspection when there
+ // is a pending status change. However, we can speed up the process slightly by canceling any
+ // pending introspect ops from our local introspection queue when it's waiting for us.
+
+ introspectMainQueue.clear();
+
+ if (introspectingConnected) {
+ // On the other hand, we have to finish the Connected introspection for now, as
+ // ReadinessHelper would otherwise wait indefinitely for it to land
+ debug() << "Finishing FeatureConnected for status" << this->status <<
+ "to allow ReadinessHelper to introspect new status" << status;
+ readinessHelper->setIntrospectCompleted(FeatureConnected, true);
+ introspectingConnected = false;
+ }
+
+ readinessHelper->setCurrentStatus(status);
+}
+
+void Connection::Private::forceCurrentStatus(uint status)
+{
+ // only update the status if we did not get it from StatusChanged
+ if (pendingStatus == (uint) -1) {
+ debug() << "Got status:" << status;
+ pendingStatus = status;
+ // No need to re-run introspection as we just received the status. Let
+ // the introspection continue normally but update readinessHelper with
+ // the correct status.
+ readinessHelper->forceCurrentStatus(status);
+ }
+}
+
+void Connection::Private::setInterfaces(const QStringList &interfaces)
+{
+ debug() << "Got interfaces:" << interfaces;
+ parent->setInterfaces(interfaces);
+ readinessHelper->setInterfaces(interfaces);
+}
+
+void Connection::Private::invalidateResetCaps(const QString &error, const QString &message)
+{
+ caps.updateRequestableChannelClasses(RequestableChannelClassList());
+ parent->invalidate(error, message);
+}
+
+/**
+ * \class ConnectionLowlevel
+ * \ingroup clientconn
+ * \headerfile TelepathyQt/connection-lowlevel.h <TelepathyQt/ConnectionLowlevel>
+ *
+ * \brief The ConnectionLowlevel class extends Connection with support to
+ * low-level features.
+ */
+
+ConnectionLowlevel::ConnectionLowlevel(Connection *conn)
+ : mPriv(new Private(conn))
+{
+}
+
+ConnectionLowlevel::~ConnectionLowlevel()
+{
+ delete mPriv;
+}
+
+bool ConnectionLowlevel::isValid() const
+{
+ return !mPriv->conn.isNull();
+}
+
+ConnectionPtr ConnectionLowlevel::connection() const
+{
+ return ConnectionPtr(mPriv->conn);
+}
+
+Connection::PendingConnect::PendingConnect(const ConnectionPtr &connection,
+ const Features &requestedFeatures)
+ : PendingReady(connection, requestedFeatures)
+{
+ if (!connection) {
+ // Called when the connection had already been destroyed
+ return;
+ }
+
+ QDBusPendingCall call = connection->baseInterface()->Connect();
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this);
+
+ connect(connection.data(),
+ SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
+ this,
+ SLOT(onConnInvalidated(Tp::DBusProxy*,QString,QString)));
+
+ connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ this,
+ SLOT(onConnectReply(QDBusPendingCallWatcher*)));
+}
+
+void Connection::PendingConnect::onConnectReply(QDBusPendingCallWatcher *watcher)
+{
+ ConnectionPtr connection = ConnectionPtr::qObjectCast(proxy());
+
+ if (watcher->isError()) {
+ debug() << "Connect failed with" <<
+ watcher->error().name() << ": " << watcher->error().message();
+ setFinishedWithError(watcher->error());
+ connection->disconnect(
+ this,
+ SLOT(onConnInvalidated(Tp::DBusProxy*,QString,QString)));
+ } else {
+ if (connection->status() == ConnectionStatusConnected) {
+ onStatusChanged(ConnectionStatusConnected);
+ } else {
+ // Wait for statusChanged()! Connect returning just means that the connection has
+ // started to connect - spec quoted for truth:
+ //
+ // Connect () -> nothing
+ // Request that the connection be established. This will be done asynchronously and
+ // errors will be returned by emitting StatusChanged signals.
+ //
+ // Which should actually say progress and/or errors IMO, but anyway...
+ connect(connection.data(),
+ SIGNAL(statusChanged(Tp::ConnectionStatus)),
+ SLOT(onStatusChanged(Tp::ConnectionStatus)));
+ }
+ }
+
+ watcher->deleteLater();
+}
+
+void Connection::PendingConnect::onStatusChanged(ConnectionStatus newStatus)
+{
+ ConnectionPtr connection = ConnectionPtr::qObjectCast(proxy());
+
+ if (newStatus == ConnectionStatusDisconnected) {
+ debug() << "Connection became disconnected while a PendingConnect was underway";
+ setFinishedWithError(connection->invalidationReason(), connection->invalidationMessage());
+
+ connection->disconnect(this,
+ SLOT(onConnInvalidated(Tp::DBusProxy*,QString,QString)));
+
+ return;
+ }
+
+ if (newStatus == ConnectionStatusConnected) {
+ // OK, the connection is Connected now - finally, we'll get down to business
+ connect(connection->becomeReady(requestedFeatures()),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onBecomeReadyReply(Tp::PendingOperation*)));
+ }
+}
+
+void Connection::PendingConnect::onBecomeReadyReply(Tp::PendingOperation *op)
+{
+ ConnectionPtr connection = ConnectionPtr::qObjectCast(proxy());
+
+ // We don't care about future disconnects even if they happen before we are destroyed
+ // (which happens two mainloop iterations from now)
+ connection->disconnect(this,
+ SLOT(onStatusChanged(Tp::ConnectionStatus)));
+ connection->disconnect(this,
+ SLOT(onConnInvalidated(Tp::DBusProxy*,QString,QString)));
+
+ if (op->isError()) {
+ debug() << "Connection->becomeReady failed with" <<
+ op->errorName() << ": " << op->errorMessage();
+ setFinishedWithError(op->errorName(), op->errorMessage());
+ } else {
+ debug() << "Connected";
+
+ if (connection->isValid()) {
+ setFinished();
+ } else {
+ debug() << " ... but the Connection was immediately invalidated!";
+ setFinishedWithError(connection->invalidationReason(), connection->invalidationMessage());
+ }
+ }
+}
+
+void Connection::PendingConnect::onConnInvalidated(Tp::DBusProxy *proxy, const QString &error,
+ const QString &message)
+{
+ ConnectionPtr connection = ConnectionPtr::qObjectCast(this->proxy());
+
+ Q_ASSERT(proxy == connection.data());
+
+ if (!isFinished()) {
+ debug() << "Unable to connect. Connection invalidated";
+ setFinishedWithError(error, message);
+ }
+
+ connection->disconnect(this,
+ SLOT(onStatusChanged(Tp::ConnectionStatus)));
+}
+
+QMap<QPair<QString, QString>, Connection::Private::HandleContext*> Connection::Private::handleContexts;
+QMutex Connection::Private::handleContextsLock;
+
+/**
+ * \class Connection
+ * \ingroup clientconn
+ * \headerfile TelepathyQt/connection.h <TelepathyQt/Connection>
+ *
+ * \brief The Connection class represents a Telepathy connection.
+ *
+ * This models a connection to a single user account on a communication service.
+ *
+ * Contacts, and server-stored lists (such as subscribed contacts,
+ * block lists, or allow lists) on a service are all represented using the
+ * ContactManager object on the connection, which is valid only for the lifetime
+ * of the connection object.
+ *
+ * The remote object accessor functions on this object (status(),
+ * statusReason(), and so on) don't make any D-Bus calls; instead, they return/use
+ * values cached from a previous introspection run. The introspection process
+ * populates their values in the most efficient way possible based on what the
+ * service implements.
+ *
+ * To avoid unnecessary D-Bus traffic, some accessors only return valid
+ * information after specific features have been enabled.
+ * For instance, to retrieve the connection self contact, it is necessary to
+ * enable the feature Connection::FeatureSelfContact.
+ * See the individual methods descriptions for more details.
+ *
+ * Connection features can be enabled by constructing a ConnectionFactory and enabling
+ * the desired features, and passing it to AccountManager, Account or ClientRegistrar
+ * when creating them as appropriate. However, if a particular
+ * feature is only ever used in a specific circumstance, such as an user opening
+ * some settings dialog separate from the general view of the application,
+ * features can be later enabled as needed by calling becomeReady() with the additional
+ * features, and waiting for the resulting PendingOperation to finish.
+ *
+ * As an addition to accessors, signals are emitted to indicate that properties have changed,
+ * for example statusChanged()(), selfContactChanged(), etc.
+ *
+ * \section conn_usage_sec Usage
+ *
+ * \subsection conn_create_sec Creating a connection object
+ *
+ * The easiest way to create connection objects is through Account. One can
+ * just use the Account::connection method to get an account active connection.
+ *
+ * If you already know the object path, you can just call create().
+ * For example:
+ *
+ * \code ConnectionPtr conn = Connection::create(busName, objectPath); \endcode
+ *
+ * A ConnectionPtr object is returned, which will automatically keep
+ * track of object lifetime.
+ *
+ * You can also provide a D-Bus connection as a QDBusConnection:
+ *
+ * \code
+ *
+ * ConnectionPtr conn = Connection::create(QDBusConnection::sessionBus(),
+ * busName, objectPath);
+ *
+ * \endcode
+ *
+ * \subsection conn_ready_sec Making connection ready to use
+ *
+ * A Connection object needs to become ready before usage, meaning that the
+ * introspection process finished and the object accessors can be used.
+ *
+ * To make the object ready, use becomeReady() and wait for the
+ * PendingOperation::finished() signal to be emitted.
+ *
+ * \code
+ *
+ * class MyClass : public QObject
+ * {
+ * QOBJECT
+ *
+ * public:
+ * MyClass(QObject *parent = 0);
+ * ~MyClass() { }
+ *
+ * private Q_SLOTS:
+ * void onConnectionReady(Tp::PendingOperation*);
+ *
+ * private:
+ * ConnectionPtr conn;
+ * };
+ *
+ * MyClass::MyClass(const QString &busName, const QString &objectPath,
+ * QObject *parent)
+ * : QObject(parent)
+ * conn(Connection::create(busName, objectPath))
+ * {
+ * // connect and become ready
+ * connect(conn->requestConnect(),
+ * SIGNAL(finished(Tp::PendingOperation*)),
+ * SLOT(onConnectionReady(Tp::PendingOperation*)));
+ * }
+ *
+ * void MyClass::onConnectionReady(Tp::PendingOperation *op)
+ * {
+ * if (op->isError()) {
+ * qWarning() << "Account cannot become ready:" <<
+ * op->errorName() << "-" << op->errorMessage();
+ * return;
+ * }
+ *
+ * // Connection is now ready
+ * }
+ *
+ * \endcode
+ *
+ * See \ref async_model, \ref shared_ptr
+ */
+
+/**
+ * Feature representing the core that needs to become ready to make the
+ * Connection object usable.
+ *
+ * Note that this feature must be enabled in order to use most Connection
+ * methods.
+ * See specific methods documentation for more details.
+ *
+ * When calling isReady(), becomeReady(), this feature is implicitly added
+ * to the requested features.
+ */
+const Feature Connection::FeatureCore = Feature(QLatin1String(Connection::staticMetaObject.className()), 0, true);
+
+/**
+ * Feature used to retrieve the connection self contact.
+ *
+ * See self contact specific methods' documentation for more details.
+ *
+ * \sa selfContact(), selfContactChanged()
+ */
+const Feature Connection::FeatureSelfContact = Feature(QLatin1String(Connection::staticMetaObject.className()), 1);
+
+/**
+ * Feature used to retrieve/keep track of the connection self presence.
+ *
+ * See simple presence specific methods' documentation for more details.
+ */
+const Feature Connection::FeatureSimplePresence = Feature(QLatin1String(Connection::staticMetaObject.className()), 2);
+
+/**
+ * Feature used to enable roster support on Connection::contactManager.
+ *
+ * See ContactManager roster specific methods' documentation for more details.
+ *
+ * \sa ContactManager::allKnownContacts()
+ */
+const Feature Connection::FeatureRoster = Feature(QLatin1String(Connection::staticMetaObject.className()), 4);
+
+/**
+ * Feature used to enable roster groups support on Connection::contactManager.
+ *
+ * See ContactManager roster groups specific methods' documentation for more
+ * details.
+ *
+ * \sa ContactManager::allKnownGroups()
+ */
+const Feature Connection::FeatureRosterGroups = Feature(QLatin1String(Connection::staticMetaObject.className()), 5);
+
+/**
+ * Feature used to retrieve/keep track of the connection account balance.
+ *
+ * See account balance specific methods' documentation for more details.
+ *
+ * \sa accountBalance(), accountBalanceChanged()
+ */
+const Feature Connection::FeatureAccountBalance = Feature(QLatin1String(Connection::staticMetaObject.className()), 6);
+
+/**
+ * When this feature is prepared, it means that the connection status() is
+ * ConnectionStatusConnected.
+ *
+ * Note that if ConnectionFactory is being used with FeatureConnected set, Connection objects will
+ * only be signalled by the library when the corresponding connection is in status()
+ * ConnectionStatusConnected.
+ */
+const Feature Connection::FeatureConnected = Feature(QLatin1String(Connection::staticMetaObject.className()), 7);
+
+/**
+ * Create a new connection object using QDBusConnection::sessionBus().
+ *
+ * A warning is printed if the factories are not for QDBusConnection::sessionBus().
+ *
+ * \param busName The connection well-known bus name (sometimes called a
+ * "service name").
+ * \param objectPath The connection object path.
+ * \param channelFactory The channel factory to use.
+ * \param contactFactory The contact factory to use.
+ * \return A ConnectionPtr object pointing to the newly created Connection object.
+ */
+ConnectionPtr Connection::create(const QString &busName,
+ const QString &objectPath,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory)
+{
+ return ConnectionPtr(new Connection(QDBusConnection::sessionBus(),
+ busName, objectPath, channelFactory, contactFactory,
+ Connection::FeatureCore));
+}
+
+/**
+ * Create a new connection object using the given \a bus.
+ *
+ * A warning is printed if the factories are not for \a bus.
+ *
+ * \param bus QDBusConnection to use.
+ * \param busName The connection well-known bus name (sometimes called a
+ * "service name").
+ * \param objectPath The connection object path.
+ * \param channelFactory The channel factory to use.
+ * \param contactFactory The contact factory to use.
+ * \return A ConnectionPtr object pointing to the newly created Connection object.
+ */
+ConnectionPtr Connection::create(const QDBusConnection &bus,
+ const QString &busName, const QString &objectPath,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory)
+{
+ return ConnectionPtr(new Connection(bus, busName, objectPath, channelFactory, contactFactory,
+ Connection::FeatureCore));
+}
+
+/**
+ * Construct a new connection object using the given \a bus.
+ *
+ * A warning is printed if the factories are not for \a bus.
+ *
+ * \param bus QDBusConnection to use.
+ * \param busName The connection's well-known bus name (sometimes called a
+ * "service name").
+ * \param objectPath The connection object path.
+ * \param channelFactory The channel factory to use.
+ * \param contactFactory The contact factory to use.
+ * \param coreFeature The core feature of the Connection subclass. The corresponding introspectable
+ * should depend on Connection::FeatureCore.
+ */
+Connection::Connection(const QDBusConnection &bus,
+ const QString &busName,
+ const QString &objectPath,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory,
+ const Feature &coreFeature)
+ : StatefulDBusProxy(bus, busName, objectPath, coreFeature),
+ OptionalInterfaceFactory<Connection>(this),
+ mPriv(new Private(this, channelFactory, contactFactory))
+{
+}
+
+/**
+ * Class destructor.
+ */
+Connection::~Connection()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the channel factory used by this connection.
+ *
+ * Only read access is provided. This allows constructing object instances and examining the object
+ * construction settings, but not changing settings. Allowing changes would lead to tricky
+ * situations where objects constructed at different times by the account would have unpredictably
+ * different construction settings (eg. subclass).
+ *
+ * \return A read-only pointer to the ChannelFactory object.
+ */
+ChannelFactoryConstPtr Connection::channelFactory() const
+{
+ return mPriv->chanFactory;
+}
+
+/**
+ * Return the contact factory used by this connection.
+ *
+ * Only read access is provided. This allows constructing object instances and examining the object
+ * construction settings, but not changing settings. Allowing changes would lead to tricky
+ * situations where objects constructed at different times by the account would have unpredictably
+ * different construction settings (eg. subclass).
+ *
+ * \return A read-only pointer to the ContactFactory object.
+ */
+ContactFactoryConstPtr Connection::contactFactory() const
+{
+ return mPriv->contactFactory;
+}
+
+/**
+ * Return the connection manager name of this connection.
+ *
+ * \return The connection manager name.
+ */
+QString Connection::cmName() const
+{
+ return mPriv->cmName;
+}
+
+/**
+ * Return the protocol name of this connection.
+ *
+ * \return The protocol name.
+ */
+QString Connection::protocolName() const
+{
+ return mPriv->protocolName;
+}
+
+/**
+ * Return the status of this connection.
+ *
+ * Change notification is via the statusChanged() signal.
+ *
+ * This method requires Connection::FeatureCore to be ready.
+ *
+ * \return The status as #ConnectionStatus.
+ * \sa statusChanged(), statusReason(), errorDetails()
+ */
+ConnectionStatus Connection::status() const
+{
+ return (ConnectionStatus) mPriv->status;
+}
+
+/**
+ * Return the reason for this connection status.
+ *
+ * The validity and change rules are the same as for status().
+ *
+ * The status reason should be only used as a fallback in error handling when the application
+ * doesn't understand an error name given as the invalidation reason, which may in some cases be
+ * domain/UI-specific.
+ *
+ * This method requires Connection::FeatureCore to be ready.
+ *
+ * \return The status reason as #ConnectionStatusReason.
+ * \sa invalidated(), invalidationReason()
+ */
+ConnectionStatusReason Connection::statusReason() const
+{
+ return (ConnectionStatusReason) mPriv->statusReason;
+}
+
+struct TP_QT_NO_EXPORT Connection::ErrorDetails::Private : public QSharedData
+{
+ Private(const QVariantMap &details)
+ : details(details) {}
+
+ QVariantMap details;
+};
+
+/**
+ * \class Connection::ErrorDetails
+ * \ingroup clientconn
+ * \headerfile TelepathyQt/connection.h <TelepathyQt/Connection>
+ *
+ * \brief The Connection::ErrorDetails class represents the details of a connection error.
+ *
+ * It contains detailed information about the reason for the connection going invalidated().
+ *
+ * Some services may provide additional error information in the ConnectionError D-Bus signal, when
+ * a Connection is disconnected / has become unusable. If the service didn't provide any, or has not
+ * been invalidated yet, the instance will be invalid, as returned by isValid().
+ *
+ * The information provided by invalidationReason() and this class should always be used in error
+ * handling in preference to statusReason(). The status reason can be used as a fallback, however,
+ * if the client doesn't understand what a particular value returned by invalidationReason() means,
+ * as it may be domain-specific with some services.
+ *
+ * Connection::errorDetails() can be used to return the instance containing the details for
+ * invalidating that connection after invalidated() has been emitted.
+ */
+
+/**
+ * Constructs a new invalid ErrorDetails instance.
+ */
+Connection::ErrorDetails::ErrorDetails()
+ : mPriv(0)
+{
+}
+
+/**
+ * Construct a error details instance with the given details. The instance will indicate that
+ * it is valid.
+ */
+Connection::ErrorDetails::ErrorDetails(const QVariantMap &details)
+ : mPriv(new Private(details))
+{
+}
+
+/**
+ * Copy constructor.
+ */
+Connection::ErrorDetails::ErrorDetails(const ErrorDetails &other)
+ : mPriv(other.mPriv)
+{
+}
+
+/**
+ * Class destructor.
+ */
+Connection::ErrorDetails::~ErrorDetails()
+{
+}
+
+/**
+ * Assigns all information (validity, details) from other to this.
+ */
+Connection::ErrorDetails &Connection::ErrorDetails::operator=(
+ const ErrorDetails &other)
+{
+ if (this->mPriv.constData() != other.mPriv.constData())
+ this->mPriv = other.mPriv;
+
+ return *this;
+}
+
+/**
+ * \fn bool Connection::ErrorDetails::isValid() const
+ *
+ * Return whether or not the details are valid (have actually been received from the service).
+ *
+ * \return \c true if valid, \c false otherwise.
+ */
+
+/**
+ * \fn bool Connection::ErrorDetails::hasDebugMessage() const
+ *
+ * Return whether or not the details specify a debug message.
+ *
+ * If present, the debug message will likely be the same string as the one returned by
+ * invalidationMessage().
+ *
+ * The debug message is purely informational, offered for display for bug reporting purposes, and
+ * should not be attempted to be parsed.
+ *
+ * \return \c true if debug message is present, \c false otherwise.
+ * \sa debugMessage()
+ */
+
+/**
+ * \fn QString Connection::ErrorDetails::debugMessage() const
+ *
+ * Return the debug message specified by the details, if any.
+ *
+ * If present, the debug message will likely be the same string as the one returned by
+ * invalidationMessage().
+ *
+ * The debug message is purely informational, offered for display for bug reporting purposes, and
+ * should not be attempted to be parsed.
+ *
+ * \return The debug message, or an empty string if there is none.
+ * \sa hasDebugMessage()
+ */
+
+/**
+ * Return a map containing all details given in the low-level ConnectionError signal.
+ *
+ * This is useful for accessing domain-specific additional details.
+ *
+ * \return The details of the connection error as QVariantMap.
+ */
+QVariantMap Connection::ErrorDetails::allDetails() const
+{
+ return isValid() ? mPriv->details : QVariantMap();
+}
+
+/**
+ * Return detailed information about the reason for the connection going invalidated().
+ *
+ * Some services may provide additional error information in the ConnectionError D-Bus signal, when
+ * a Connection is disconnected / has become unusable. If the service didn't provide any, or has not
+ * been invalidated yet, an invalid instance is returned.
+ *
+ * The information provided by invalidationReason() and this method should always be used in error
+ * handling in preference to statusReason(). The status reason can be used as a fallback, however,
+ * if the client doesn't understand what a particular value returned by invalidationReason() means,
+ * as it may be domain-specific with some services.
+ *
+ * \return The error details as a Connection::ErrorDetails object.
+ * \sa status(), statusReason(), invalidationReason()
+ */
+const Connection::ErrorDetails &Connection::errorDetails() const
+{
+ if (isValid()) {
+ warning() << "Connection::errorDetails() used on" << objectPath() << "which is valid";
+ }
+
+ return mPriv->errorDetails;
+}
+
+/**
+ * Return the handle representing the user on this connection.
+ *
+ * Note that if the connection is not yet in the ConnectionStatusConnected state,
+ * the value of this property may be zero.
+ *
+ * Change notification is via the selfHandleChanged() signal.
+ *
+ * This method requires Connection::FeatureCore to be ready.
+ *
+ * \return The user handle.
+ * \sa selfHandleChanged(), selfContact()
+ */
+uint Connection::selfHandle() const
+{
+ return mPriv->selfHandle;
+}
+
+/**
+ * Return a dictionary of presence statuses valid for use in this connection.
+ *
+ * The value may have changed arbitrarily during the time the
+ * Connection spends in status ConnectionStatusConnecting,
+ * again staying fixed for the entire time in ConnectionStatusConnected.
+ *
+ * This method requires Connection::FeatureSimplePresence to be ready.
+ *
+ * \return The allowed statuses as a map from string identifiers to SimpleStatusSpec objects.
+ */
+SimpleStatusSpecMap ConnectionLowlevel::allowedPresenceStatuses() const
+{
+ if (!isValid()) {
+ warning() << "ConnectionLowlevel::selfHandle() "
+ "called for a connection which is already destroyed";
+ return SimpleStatusSpecMap();
+ }
+
+ ConnectionPtr conn(mPriv->conn);
+
+ if (!conn->isReady(Connection::FeatureSimplePresence)) {
+ warning() << "Trying to retrieve allowed presence statuses from connection, but "
+ "simple presence is not supported or was not requested. "
+ "Enable FeatureSimplePresence in this connection";
+ }
+
+ return conn->mPriv->simplePresenceStatuses;
+}
+
+/**
+ * Return the maximum length for a presence status message.
+ *
+ * The value may have changed arbitrarily during the time the
+ * Connection spends in status ConnectionStatusConnecting,
+ * again staying fixed for the entire time in ConnectionStatusConnected.
+ *
+ * This method requires Connection::FeatureSimplePresence to be ready.
+ *
+ * \return The maximum length, or 0 if there is no limit.
+ */
+uint ConnectionLowlevel::maxPresenceStatusMessageLength() const
+{
+ if (!isValid()) {
+ warning() << "ConnectionLowlevel::maxPresenceStatusMessageLength() "
+ "called for a connection which is already destroyed";
+ return 0;
+ }
+
+ ConnectionPtr conn(mPriv->conn);
+
+ if (!conn->isReady(Connection::FeatureSimplePresence)) {
+ warning() << "Trying to retrieve max presence status message length connection, but "
+ "simple presence is not supported or was not requested. "
+ "Enable FeatureSimplePresence in this connection";
+ }
+
+ return conn->mPriv->maxPresenceStatusMessageLength;
+}
+
+/**
+ * Set the self presence status.
+ *
+ * This should generally only be called by an Account Manager. In typical usage,
+ * Account::setRequestedPresence() should be used instead.
+ *
+ * \a status must be one of the allowed statuses returned by
+ * allowedPresenceStatuses().
+ *
+ * Note that clients SHOULD set the status message for the local user to the
+ * empty string, unless the user has actually provided a specific message (i.e.
+ * one that conveys more information than the ConnectionStatus).
+ *
+ * \param status The desired status.
+ * \param statusMessage The desired status message.
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the call has finished.
+ * \sa allowedPresenceStatuses()
+ */
+PendingOperation *ConnectionLowlevel::setSelfPresence(const QString &status,
+ const QString &statusMessage)
+{
+ if (!isValid()) {
+ warning() << "ConnectionLowlevel::selfHandle() called for a connection which is already destroyed";
+ return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE, QLatin1String("Connection already destroyed"),
+ ConnectionPtr());
+ }
+
+ ConnectionPtr conn(mPriv->conn);
+
+ if (!conn->interfaces().contains(QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE))) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("Connection does not support SimplePresence"), conn);
+ }
+
+ Client::ConnectionInterfaceSimplePresenceInterface *simplePresenceInterface =
+ conn->interface<Client::ConnectionInterfaceSimplePresenceInterface>();
+ return new PendingVoid(
+ simplePresenceInterface->SetPresence(status, statusMessage), conn);
+}
+
+/**
+ * Return the object representing the user on this connection.
+ *
+ * Note that if the connection is not yet in the ConnectionStatusConnected state, the value of this
+ * property may be null.
+ *
+ * Change notification is via the selfContactChanged() signal.
+ *
+ * This method requires Connection::FeatureSelfContact to be ready.
+ *
+ * \return A pointer to the Contact object, or a null ContactPtr if unknown.
+ * \sa selfContactChanged(), selfHandle()
+ */
+ContactPtr Connection::selfContact() const
+{
+ if (!isReady(FeatureSelfContact)) {
+ warning() << "Connection::selfContact() used, but becomeReady(FeatureSelfContact) "
+ "hasn't been completed!";
+ }
+
+ return mPriv->selfContact;
+}
+
+/**
+ * Return the user's balance on the account corresponding to this connection.
+ *
+ * A negative amount may be possible on some services, and indicates that the user
+ * owes money to the service provider.
+ *
+ * Change notification is via the accountBalanceChanged() signal.
+ *
+ * This method requires Connection::FeatureAccountBalance to be ready.
+ *
+ * \return The account balance as #CurrencyAmount.
+ * \sa accountBalanceChanged()
+ */
+CurrencyAmount Connection::accountBalance() const
+{
+ if (!isReady(FeatureAccountBalance)) {
+ warning() << "Connection::accountBalance() used before connection "
+ "FeatureAccountBalance is ready";
+ }
+
+ return mPriv->accountBalance;
+}
+
+/**
+ * Return the capabilities for this connection.
+ *
+ * User interfaces can use this information to show or hide UI components.
+ *
+ * This property cannot change after the connection has gone to state
+ * ConnectionStatusConnected, so there is no change notification.
+ *
+ * This method requires Connection::FeatureCore to be ready.
+ *
+ * @return The capabilities of this connection.
+ */
+ConnectionCapabilities Connection::capabilities() const
+{
+ if (!isReady(Connection::FeatureCore)) {
+ warning() << "Connection::capabilities() used before connection "
+ "FeatureCore is ready";
+ }
+
+ return mPriv->caps;
+}
+
+void Connection::onStatusReady(uint status)
+{
+ Q_ASSERT(status == mPriv->pendingStatus);
+
+ if (mPriv->status == status) {
+ return;
+ }
+
+ mPriv->status = status;
+ mPriv->statusReason = mPriv->pendingStatusReason;
+
+ if (isValid()) {
+ emit statusChanged((ConnectionStatus) mPriv->status);
+ } else {
+ debug() << this << " not emitting statusChanged because it has been invalidated";
+ }
+}
+
+void Connection::onStatusChanged(uint status, uint reason)
+{
+ debug() << "StatusChanged from" << mPriv->pendingStatus
+ << "to" << status << "with reason" << reason;
+
+ if (mPriv->pendingStatus == status) {
+ warning() << "New status was the same as the old status! Ignoring"
+ "redundant StatusChanged";
+ return;
+ }
+
+ uint oldStatus = mPriv->pendingStatus;
+ mPriv->pendingStatus = status;
+ mPriv->pendingStatusReason = reason;
+
+ switch (status) {
+ case ConnectionStatusConnected:
+ debug() << "Performing introspection for the Connected status";
+ mPriv->setCurrentStatus(status);
+ break;
+
+ case ConnectionStatusConnecting:
+ mPriv->setCurrentStatus(status);
+ break;
+
+ case ConnectionStatusDisconnected:
+ {
+ QString errorName = ConnectionHelper::statusReasonToErrorName(
+ (ConnectionStatusReason) reason,
+ (ConnectionStatus) oldStatus);
+
+ // TODO should we signal statusChanged to Disconnected here or just
+ // invalidate?
+ // Also none of the pendingOperations will finish. The
+ // user should just consider them to fail as the connection
+ // is invalid
+ onStatusReady(ConnectionStatusDisconnected);
+ mPriv->invalidateResetCaps(errorName,
+ QString(QLatin1String("ConnectionStatusReason = %1")).arg(uint(reason)));
+ }
+ break;
+
+ default:
+ warning() << "Unknown connection status" << status;
+ break;
+ }
+}
+
+void Connection::onConnectionError(const QString &error,
+ const QVariantMap &details)
+{
+ debug().nospace() << "Connection(" << objectPath() << ") got ConnectionError(" << error
+ << ") with " << details.size() << " details";
+
+ mPriv->errorDetails = details;
+ mPriv->invalidateResetCaps(error,
+ details.value(QLatin1String("debug-message")).toString());
+}
+
+void Connection::gotMainProperties(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QVariantMap> reply = *watcher;
+ QVariantMap props;
+
+ if (!reply.isError()) {
+ props = reply.value();
+ } else {
+ warning().nospace() << "Properties::GetAll(Connection) failed with " <<
+ reply.error().name() << ": " << reply.error().message();
+ // let's try to fallback first before failing
+ }
+
+ uint status = static_cast<uint>(-1);
+ if (props.contains(QLatin1String("Status"))
+ && ((status = qdbus_cast<uint>(props[QLatin1String("Status")])) <=
+ ConnectionStatusDisconnected)) {
+ mPriv->forceCurrentStatus(status);
+ } else {
+ // only introspect status if we did not got it from StatusChanged
+ if (mPriv->pendingStatus == (uint) -1) {
+ mPriv->introspectMainQueue.enqueue(
+ &Private::introspectMainFallbackStatus);
+ }
+ }
+
+ if (props.contains(QLatin1String("Interfaces"))) {
+ mPriv->setInterfaces(qdbus_cast<QStringList>(
+ props[QLatin1String("Interfaces")]));
+ } else {
+ mPriv->introspectMainQueue.enqueue(
+ &Private::introspectMainFallbackInterfaces);
+ }
+
+ if (props.contains(QLatin1String("SelfHandle"))) {
+ mPriv->selfHandle = qdbus_cast<uint>(
+ props[QLatin1String("SelfHandle")]);
+ } else {
+ mPriv->introspectMainQueue.enqueue(
+ &Private::introspectMainFallbackSelfHandle);
+ }
+
+ if (props.contains(QLatin1String("HasImmortalHandles"))) {
+ mPriv->immortalHandles = qdbus_cast<bool>(props[QLatin1String("HasImmortalHandles")]);
+ }
+
+ if (hasInterface(TP_QT_IFACE_CONNECTION_INTERFACE_REQUESTS)) {
+ mPriv->introspectMainQueue.enqueue(
+ &Private::introspectCapabilities);
+ }
+
+ if (hasInterface(TP_QT_IFACE_CONNECTION_INTERFACE_CONTACTS)) {
+ mPriv->introspectMainQueue.enqueue(
+ &Private::introspectContactAttributeInterfaces);
+ }
+
+ mPriv->continueMainIntrospection();
+
+ watcher->deleteLater();
+}
+
+void Connection::gotStatus(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<uint> reply = *watcher;
+
+ if (!reply.isError()) {
+ mPriv->forceCurrentStatus(reply.value());
+
+ mPriv->continueMainIntrospection();
+ } else {
+ warning().nospace() << "GetStatus() failed with " <<
+ reply.error().name() << ": " << reply.error().message();
+ mPriv->invalidateResetCaps(reply.error().name(), reply.error().message());
+ }
+
+ watcher->deleteLater();
+}
+
+void Connection::gotInterfaces(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QStringList> reply = *watcher;
+
+ if (!reply.isError()) {
+ mPriv->setInterfaces(reply.value());
+ }
+ else {
+ warning().nospace() << "GetInterfaces() failed with " <<
+ reply.error().name() << ": " << reply.error().message() <<
+ " - assuming no new interfaces";
+ // let's not fail if GetInterfaces fail
+ }
+
+ mPriv->continueMainIntrospection();
+
+ watcher->deleteLater();
+}
+
+void Connection::gotSelfHandle(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<uint> reply = *watcher;
+
+ if (!reply.isError()) {
+ mPriv->selfHandle = reply.value();
+ debug() << "Got self handle:" << mPriv->selfHandle;
+
+ mPriv->continueMainIntrospection();
+ } else {
+ warning().nospace() << "GetSelfHandle() failed with " <<
+ reply.error().name() << ": " << reply.error().message();
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureCore,
+ false, reply.error());
+ }
+
+ watcher->deleteLater();
+}
+
+void Connection::gotCapabilities(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QDBusVariant> reply = *watcher;
+
+ if (!reply.isError()) {
+ debug() << "Got capabilities";
+ mPriv->caps.updateRequestableChannelClasses(
+ qdbus_cast<RequestableChannelClassList>(reply.value().variant()));
+ } else {
+ warning().nospace() << "Getting capabilities failed with " <<
+ reply.error().name() << ": " << reply.error().message();
+ // let's not fail if retrieving capabilities fail
+ }
+
+ mPriv->continueMainIntrospection();
+
+ watcher->deleteLater();
+}
+
+void Connection::gotContactAttributeInterfaces(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QDBusVariant> reply = *watcher;
+
+ if (!reply.isError()) {
+ debug() << "Got contact attribute interfaces";
+ mPriv->contactAttributeInterfaces = qdbus_cast<QStringList>(reply.value().variant());
+ } else {
+ warning().nospace() << "Getting contact attribute interfaces failed with " <<
+ reply.error().name() << ": " << reply.error().message();
+ // let's not fail if retrieving contact attribute interfaces fail
+ // TODO should we remove Contacts interface from interfaces?
+ }
+
+ mPriv->continueMainIntrospection();
+
+ watcher->deleteLater();
+}
+
+void Connection::gotSimpleStatuses(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QVariantMap> reply = *watcher;
+
+ if (!reply.isError()) {
+ QVariantMap props = reply.value();
+
+ mPriv->simplePresenceStatuses = qdbus_cast<SimpleStatusSpecMap>(
+ props[QLatin1String("Statuses")]);
+ mPriv->maxPresenceStatusMessageLength = qdbus_cast<uint>(
+ props[QLatin1String("MaximumStatusMessageLength")]);
+
+ debug() << "Got" << mPriv->simplePresenceStatuses.size() <<
+ "simple presence statuses - max status message length is" <<
+ mPriv->maxPresenceStatusMessageLength;
+
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureSimplePresence, true);
+ }
+ else {
+ warning().nospace() << "Getting simple presence statuses failed with " <<
+ reply.error().name() << ":" << reply.error().message();
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureSimplePresence, false, reply.error());
+ }
+
+ watcher->deleteLater();
+}
+
+void Connection::gotSelfContact(PendingOperation *op)
+{
+ PendingContacts *pending = qobject_cast<PendingContacts *>(op);
+
+ if (pending->isValid()) {
+ Q_ASSERT(pending->contacts().size() == 1);
+ ContactPtr contact = pending->contacts()[0];
+
+ if (mPriv->selfContact != contact) {
+ mPriv->selfContact = contact;
+
+ if (!isReady(FeatureSelfContact)) {
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureSelfContact, true);
+ }
+
+ emit selfContactChanged();
+ }
+ } else {
+ warning().nospace() << "Getting self contact failed with " <<
+ pending->errorName() << ":" << pending->errorMessage();
+
+ // check if the feature is already there, and for some reason introspectSelfContact
+ // failed when called the second time
+ if (!isReady(FeatureSelfContact)) {
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureSelfContact, false,
+ op->errorName(), op->errorMessage());
+ }
+
+ if (mPriv->selfContact) {
+ mPriv->selfContact.reset();
+ emit selfContactChanged();
+ }
+ }
+
+ mPriv->introspectingSelfContact = false;
+
+ if (mPriv->reintrospectSelfContactRequired) {
+ mPriv->introspectSelfContact(mPriv);
+ }
+}
+
+void Connection::onIntrospectRosterFinished(PendingOperation *op)
+{
+ if (op->isError()) {
+ warning().nospace() << "Introspecting roster failed with " <<
+ op->errorName() << ": " << op->errorMessage();
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureRoster, false,
+ op->errorName(), op->errorMessage());
+ return;
+ }
+
+ debug() << "Introspecting roster finished";
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureRoster, true);
+}
+
+void Connection::onIntrospectRosterGroupsFinished(PendingOperation *op)
+{
+ if (op->isError()) {
+ warning().nospace() << "Introspecting roster groups failed with " <<
+ op->errorName() << ": " << op->errorMessage();
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureRosterGroups, false,
+ op->errorName(), op->errorMessage());
+ return;
+ }
+
+ debug() << "Introspecting roster groups finished";
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureRosterGroups, true);
+}
+
+void Connection::gotBalance(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QVariant> reply = *watcher;
+
+ if (!reply.isError()) {
+ debug() << "Got balance";
+ mPriv->accountBalance = qdbus_cast<CurrencyAmount>(reply.value());
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureAccountBalance, true);
+ } else {
+ warning().nospace() << "Getting balance failed with " <<
+ reply.error().name() << ":" << reply.error().message();
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureAccountBalance, false,
+ reply.error().name(), reply.error().message());
+ }
+
+ watcher->deleteLater();
+}
+
+/**
+ * Return the Client::ConnectionInterface interface proxy object for this connection.
+ * This method is protected since the convenience methods provided by this
+ * class should generally be used instead of calling D-Bus methods
+ * directly.
+ *
+ * \return A pointer to the existing Client::ConnectionInterface object for this
+ * Connection object.
+ */
+Client::ConnectionInterface *Connection::baseInterface() const
+{
+ return mPriv->baseInterface;
+}
+
+/**
+ * Same as \c createChannel(request, -1)
+ */
+PendingChannel *ConnectionLowlevel::createChannel(const QVariantMap &request)
+{
+ return createChannel(request, -1);
+}
+
+/**
+ * Asynchronously creates a channel satisfying the given request.
+ *
+ * In typical usage, only the Channel Dispatcher should call this. Ordinary
+ * applications should use the Account::createChannel() family of methods
+ * (which invoke the Channel Dispatcher's services).
+ *
+ * The request MUST contain the following keys:
+ * org.freedesktop.Telepathy.Channel.ChannelType
+ * org.freedesktop.Telepathy.Channel.TargetHandleType
+ *
+ * Upon completion, the reply to the request can be retrieved through the
+ * returned PendingChannel object. The object also provides access to the
+ * parameters with which the call was made and a signal to connect to get
+ * notification of the request finishing processing. See the documentation
+ * for that class for more info.
+ *
+ * \param request A dictionary containing the desirable properties.
+ * \param timeout The D-Bus timeout in milliseconds used for the method call.
+ * If timeout is -1, a default implementation-defined value that
+ * is suitable for inter-process communications (generally,
+ * 25 seconds) will be used.
+ * \return A PendingChannel which will emit PendingChannel::finished
+ * when the channel has been created, or an error occurred.
+ * \sa PendingChannel, ensureChannel(),
+ * Account::createChannel(), Account::createAndHandleChannel(),
+ * Account::ensureChannel(), Account::ensureAndHandleChannel()
+ */
+PendingChannel *ConnectionLowlevel::createChannel(const QVariantMap &request,
+ int timeout)
+{
+ if (!isValid()) {
+ return new PendingChannel(ConnectionPtr(),
+ TP_QT_ERROR_NOT_AVAILABLE,
+ QLatin1String("The connection has been destroyed"));
+ }
+
+ ConnectionPtr conn(mPriv->conn);
+
+ if (conn->mPriv->pendingStatus != ConnectionStatusConnected) {
+ warning() << "Calling createChannel with connection not yet connected";
+ return new PendingChannel(conn,
+ QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Connection not yet connected"));
+ }
+
+ if (!conn->interfaces().contains(QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_REQUESTS))) {
+ warning() << "Requests interface is not support by this connection";
+ return new PendingChannel(conn,
+ QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("Connection does not support Requests Interface"));
+ }
+
+ if (!request.contains(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType"))) {
+ return new PendingChannel(conn,
+ QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT),
+ QLatin1String("Invalid 'request' argument"));
+ }
+
+ debug() << "Creating a Channel";
+ PendingChannel *channel = new PendingChannel(conn, request, true, timeout);
+ return channel;
+}
+
+/**
+ * Same as \c ensureChannel(request, -1)
+ */
+PendingChannel *ConnectionLowlevel::ensureChannel(const QVariantMap &request)
+{
+ return ensureChannel(request, -1);
+}
+
+/**
+ * Asynchronously ensures a channel exists satisfying the given request.
+ *
+ * In typical usage, only the Channel Dispatcher should call this. Ordinary
+ * applications should use the Account::ensureChannel() family of methods
+ * (which invoke the Channel Dispatcher's services).
+ *
+ * The request MUST contain the following keys:
+ * org.freedesktop.Telepathy.Channel.ChannelType
+ * org.freedesktop.Telepathy.Channel.TargetHandleType
+ *
+ * Upon completion, the reply to the request can be retrieved through the
+ * returned PendingChannel object. The object also provides access to the
+ * parameters with which the call was made and a signal to connect to get
+ * notification of the request finishing processing. See the documentation
+ * for that class for more info.
+ *
+ * \param request A dictionary containing the desirable properties.
+ * \param timeout The D-Bus timeout in milliseconds used for the method call.
+ * If timeout is -1, a default implementation-defined value that
+ * is suitable for inter-process communications (generally,
+ * 25 seconds) will be used.
+ * \return A PendingChannel which will emit PendingChannel::finished
+ * when the channel is ensured to exist, or an error occurred.
+ * \sa PendingChannel, createChannel(),
+ * Account::createChannel(), Account::createAndHandleChannel(),
+ * Account::ensureChannel(), Account::ensureAndHandleChannel()
+ */
+PendingChannel *ConnectionLowlevel::ensureChannel(const QVariantMap &request,
+ int timeout)
+{
+ if (!isValid()) {
+ return new PendingChannel(ConnectionPtr(),
+ TP_QT_ERROR_NOT_AVAILABLE,
+ QLatin1String("The connection has been destroyed"));
+ }
+
+ ConnectionPtr conn(mPriv->conn);
+
+ if (conn->mPriv->pendingStatus != ConnectionStatusConnected) {
+ warning() << "Calling ensureChannel with connection not yet connected";
+ return new PendingChannel(conn,
+ QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Connection not yet connected"));
+ }
+
+ if (!conn->interfaces().contains(QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_REQUESTS))) {
+ warning() << "Requests interface is not support by this connection";
+ return new PendingChannel(conn,
+ QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("Connection does not support Requests Interface"));
+ }
+
+ if (!request.contains(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType"))) {
+ return new PendingChannel(conn,
+ QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT),
+ QLatin1String("Invalid 'request' argument"));
+ }
+
+ debug() << "Creating a Channel";
+ PendingChannel *channel = new PendingChannel(conn, request, false, timeout);
+ return channel;
+}
+
+/**
+ * Request handles of the given type for the given entities (contacts,
+ * rooms, lists, etc.).
+ *
+ * Typically one doesn't need to request and use handles directly; instead, string identifiers
+ * and/or Contact objects are used in most APIs. File a bug for APIs in which there is no
+ * alternative to using handles. In particular however using low-level DBus interfaces for which
+ * there is no corresponding high-level (or one is implementing that abstraction) functionality does
+ * and will always require using bare handles.
+ *
+ * Upon completion, the reply to the request can be retrieved through the
+ * returned PendingHandles object. The object also provides access to the
+ * parameters with which the call was made and a signal to connect to to get
+ * notification of the request finishing processing. See the documentation
+ * for that class for more info.
+ *
+ * The returned PendingHandles object should be freed using
+ * its QObject::deleteLater() method after it is no longer used. However,
+ * all PendingHandles objects resulting from requests to a particular
+ * Connection will be freed when the Connection itself is freed. Conversely,
+ * this means that the PendingHandles object should not be used after the
+ * Connection is destroyed.
+ *
+ * \param handleType Type for the handles to request, as specified in
+ * #HandleType.
+ * \param names Names of the entities to request handles for.
+ * \return A PendingHandles which will emit PendingHandles::finished
+ * when the handles have been requested, or an error occurred.
+ * \sa PendingHandles
+ */
+PendingHandles *ConnectionLowlevel::requestHandles(HandleType handleType, const QStringList &names)
+{
+ debug() << "Request for" << names.length() << "handles of type" << handleType;
+
+ if (!isValid()) {
+ return new PendingHandles(TP_QT_ERROR_NOT_AVAILABLE,
+ QLatin1String("The connection has been destroyed"));
+ }
+
+ ConnectionPtr conn(mPriv->conn);
+ if (!hasImmortalHandles()) {
+ Connection::Private::HandleContext *handleContext = conn->mPriv->handleContext;
+ QMutexLocker locker(&handleContext->lock);
+ handleContext->types[handleType].requestsInFlight++;
+ }
+
+ PendingHandles *pending =
+ new PendingHandles(conn, handleType, names);
+ return pending;
+}
+
+/**
+ * Request a reference to the given handles. Handles not explicitly
+ * requested (via requestHandles()) but eg. observed in a signal need to be
+ * referenced to guarantee them staying valid.
+ *
+ * Typically one doesn't need to reference and use handles directly; instead, string identifiers
+ * and/or Contact objects are used in most APIs. File a bug for APIs in which there is no
+ * alternative to using handles. In particular however using low-level DBus interfaces for which
+ * there is no corresponding high-level (or one is implementing that abstraction) functionality does
+ * and will always require using bare handles.
+ *
+ * Upon completion, the reply to the operation can be retrieved through the
+ * returned PendingHandles object. The object also provides access to the
+ * parameters with which the call was made and a signal to connect to to get
+ * notification of the request finishing processing. See the documentation
+ * for that class for more info.
+ *
+ * The returned PendingHandles object should be freed using
+ * its QObject::deleteLater() method after it is no longer used. However,
+ * all PendingHandles objects resulting from requests to a particular
+ * Connection will be freed when the Connection itself is freed. Conversely,
+ * this means that the PendingHandles object should not be used after the
+ * Connection is destroyed.
+ *
+ * \sa PendingHandles
+ *
+ * \param handleType Type of the handles given, as specified in #HandleType.
+ * \param handles Handles to request a reference to.
+ * \return A PendingHandles which will emit PendingHandles::finished
+ * when the handles have been referenced, or an error occurred.
+ */
+PendingHandles *ConnectionLowlevel::referenceHandles(HandleType handleType, const UIntList &handles)
+{
+ debug() << "Reference of" << handles.length() << "handles of type" << handleType;
+
+ if (!isValid()) {
+ return new PendingHandles(TP_QT_ERROR_NOT_AVAILABLE,
+ QLatin1String("The connection has been destroyed"));
+ }
+
+ ConnectionPtr conn(mPriv->conn);
+ UIntList alreadyHeld;
+ UIntList notYetHeld;
+ if (!hasImmortalHandles()) {
+ Connection::Private::HandleContext *handleContext = conn->mPriv->handleContext;
+ QMutexLocker locker(&handleContext->lock);
+
+ foreach (uint handle, handles) {
+ if (handleContext->types[handleType].refcounts.contains(handle) ||
+ handleContext->types[handleType].toRelease.contains(handle)) {
+ alreadyHeld.push_back(handle);
+ }
+ else {
+ notYetHeld.push_back(handle);
+ }
+ }
+
+ debug() << " Already holding" << alreadyHeld.size() <<
+ "of the handles -" << notYetHeld.size() << "to go";
+ } else {
+ alreadyHeld = handles;
+ }
+
+ PendingHandles *pending =
+ new PendingHandles(conn, handleType, handles, alreadyHeld, notYetHeld);
+ return pending;
+}
+
+/**
+ * Start an asynchronous request that the connection be connected.
+ *
+ * When using a full-fledged Telepathy setup with an Account Manager service, the Account methods
+ * Account::setRequestedPresence() and Account::reconnect() must be used instead.
+ *
+ * The returned PendingOperation will finish successfully when the connection
+ * has reached ConnectionStatusConnected and the requested \a features are all ready, or
+ * finish with an error if a fatal error occurs during that process.
+ *
+ * \param requestedFeatures The features which should be enabled
+ * \return A PendingReady which will emit PendingReady::finished
+ * when the Connection has reached #ConnectionStatusConnected, and initial setup
+ * for basic functionality, plus the given features, has succeeded or
+ * failed.
+ */
+PendingReady *ConnectionLowlevel::requestConnect(const Features &requestedFeatures)
+{
+ if (!isValid()) {
+ Connection::PendingConnect *pending = new Connection::PendingConnect(ConnectionPtr(),
+ requestedFeatures);
+ pending->setFinishedWithError(TP_QT_ERROR_NOT_AVAILABLE,
+ QLatin1String("The connection has been destroyed"));
+ return pending;
+ }
+
+ return new Connection::PendingConnect(ConnectionPtr(mPriv->conn), requestedFeatures);
+}
+
+/**
+ * Start an asynchronous request that the connection be disconnected.
+ * The returned PendingOperation object will signal the success or failure
+ * of this request; under normal circumstances, it can be expected to
+ * succeed.
+ *
+ * When using a full-fledged Telepathy setup with an Account Manager service,
+ * Account::setRequestedPresence() with Presence::offline() as an argument should generally be used
+ * instead.
+ *
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the request has been made.
+ */
+PendingOperation *ConnectionLowlevel::requestDisconnect()
+{
+ if (!isValid()) {
+ return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
+ QLatin1String("The connection has been destroyed"), ConnectionPtr());
+ }
+
+ ConnectionPtr conn(mPriv->conn);
+
+ return new PendingVoid(conn->baseInterface()->Disconnect(), conn);
+}
+
+/**
+ * Requests attributes for contacts. Optionally, the handles of the contacts
+ * will be referenced automatically. Essentially, this method wraps
+ * ConnectionInterfaceContactsInterface::GetContactAttributes(), integrating it
+ * with the rest of the handle-referencing machinery.
+ *
+ * This is very low-level API the Contact/ContactManager API provides a higher level of abstraction
+ * for the same functionality.
+ *
+ * Upon completion, the reply to the request can be retrieved through the
+ * returned PendingContactAttributes object. The object also provides access to
+ * the parameters with which the call was made and a signal to connect to to get
+ * notification of the request finishing processing. See the documentation for
+ * that class for more info.
+ *
+ * If the remote object doesn't support the Contacts interface (as signified by
+ * the list returned by interfaces() not containing
+ * #TP_QT_IFACE_CONNECTION_INTERFACE_CONTACTS), the returned
+ * PendingContactAttributes instance will fail instantly with the error
+ * #TP_QT_ERROR_NOT_IMPLEMENTED.
+ *
+ * Similarly, if the connection isn't both connected and ready
+ * (<code>status() == ConnectionStatusConnected && isReady(Connection::FeatureCore)</code>),
+ * the returned PendingContactAttributes instance will fail instantly with the
+ * error #TP_QT_ERROR_NOT_AVAILABLE.
+ *
+ * This method requires Connection::FeatureCore to be ready.
+ *
+ * \sa PendingContactAttributes
+ *
+ * \param handles A list of handles of type HandleTypeContact
+ * \param interfaces D-Bus interfaces for which the client requires information
+ * \param reference Whether the handles should additionally be referenced.
+ * \return A PendingContactAttributes which will emit PendingContactAttributes::fininshed
+ * when the contact attributes have been retrieved, or an error occurred.
+ */
+PendingContactAttributes *ConnectionLowlevel::contactAttributes(const UIntList &handles,
+ const QStringList &interfaces, bool reference)
+{
+ debug() << "Request for attributes for" << handles.size() << "contacts";
+
+ if (!isValid()) {
+ PendingContactAttributes *pending = new PendingContactAttributes(ConnectionPtr(),
+ handles, interfaces, reference);
+ pending->failImmediately(TP_QT_ERROR_NOT_AVAILABLE,
+ QLatin1String("The connection has been destroyed"));
+ return pending;
+ }
+
+ ConnectionPtr conn(mPriv->conn);
+
+ PendingContactAttributes *pending =
+ new PendingContactAttributes(conn,
+ handles, interfaces, reference);
+ if (!conn->isReady(Connection::FeatureCore)) {
+ warning() << "ConnectionLowlevel::contactAttributes() used when not ready";
+ pending->failImmediately(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("The connection isn't ready"));
+ return pending;
+ } else if (conn->mPriv->pendingStatus != ConnectionStatusConnected) {
+ warning() << "ConnectionLowlevel::contactAttributes() used with status" << conn->status() << "!= ConnectionStatusConnected";
+ pending->failImmediately(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("The connection isn't Connected"));
+ return pending;
+ } else if (!conn->interfaces().contains(QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_CONTACTS))) {
+ warning() << "ConnectionLowlevel::contactAttributes() used without the remote object supporting"
+ << "the Contacts interface";
+ pending->failImmediately(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("The connection doesn't support the Contacts interface"));
+ return pending;
+ }
+
+ if (!hasImmortalHandles()) {
+ Connection::Private::HandleContext *handleContext = conn->mPriv->handleContext;
+ QMutexLocker locker(&handleContext->lock);
+ handleContext->types[HandleTypeContact].requestsInFlight++;
+ }
+
+ Client::ConnectionInterfaceContactsInterface *contactsInterface =
+ conn->interface<Client::ConnectionInterfaceContactsInterface>();
+ QDBusPendingCallWatcher *watcher =
+ new QDBusPendingCallWatcher(contactsInterface->GetContactAttributes(handles, interfaces,
+ reference));
+ pending->connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(onCallFinished(QDBusPendingCallWatcher*)));
+ return pending;
+}
+
+QStringList ConnectionLowlevel::contactAttributeInterfaces() const
+{
+ if (!isValid()) {
+ warning() << "ConnectionLowlevel::contactAttributeInterfaces() called for a destroyed Connection";
+ return QStringList();
+ }
+
+ ConnectionPtr conn(mPriv->conn);
+
+ if (conn->mPriv->pendingStatus != ConnectionStatusConnected) {
+ warning() << "ConnectionLowlevel::contactAttributeInterfaces() used with status"
+ << conn->status() << "!= ConnectionStatusConnected";
+ } else if (!conn->interfaces().contains(QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_CONTACTS))) {
+ warning() << "ConnectionLowlevel::contactAttributeInterfaces() used without the remote object supporting"
+ << "the Contacts interface";
+ }
+
+ return conn->mPriv->contactAttributeInterfaces;
+}
+
+void ConnectionLowlevel::injectContactIds(const HandleIdentifierMap &contactIds)
+{
+ if (!hasImmortalHandles()) {
+ return;
+ }
+
+ for (HandleIdentifierMap::const_iterator i = contactIds.constBegin();
+ i != contactIds.constEnd(); ++i) {
+ uint handle = i.key();
+ QString id = i.value();
+
+ if (!id.isEmpty()) {
+ QString currentId = mPriv->contactsIds.value(handle);
+
+ if (!currentId.isEmpty() && id != currentId) {
+ warning() << "Trying to overwrite contact id from" << currentId << "to" << id
+ << "for the same handle" << handle << ", ignoring";
+ } else {
+ mPriv->contactsIds.insert(handle, id);
+ }
+ }
+ }
+}
+
+void ConnectionLowlevel::injectContactId(uint handle, const QString &contactId)
+{
+ HandleIdentifierMap contactIds;
+ contactIds.insert(handle, contactId);
+ injectContactIds(contactIds);
+}
+
+bool ConnectionLowlevel::hasContactId(uint handle) const
+{
+ return mPriv->contactsIds.contains(handle);
+}
+
+QString ConnectionLowlevel::contactId(uint handle) const
+{
+ return mPriv->contactsIds.value(handle);
+}
+
+/**
+ * Return whether the handles last for the whole lifetime of the connection.
+ *
+ * \return \c true if handles are immortal, \c false otherwise.
+ */
+bool ConnectionLowlevel::hasImmortalHandles() const
+{
+ ConnectionPtr conn(mPriv->conn);
+
+ return conn->mPriv->immortalHandles;
+}
+
+/**
+ * Return the ContactManager object for this connection.
+ *
+ * The contact manager is responsible for all contact handling in this
+ * connection, including adding, removing, authorizing, etc.
+ *
+ * \return A pointer to the ContactManager object.
+ */
+ContactManagerPtr Connection::contactManager() const
+{
+ return mPriv->contactManager;
+}
+
+ConnectionLowlevelPtr Connection::lowlevel()
+{
+ return mPriv->lowlevel;
+}
+
+ConnectionLowlevelConstPtr Connection::lowlevel() const
+{
+ return mPriv->lowlevel;
+}
+
+void Connection::refHandle(HandleType handleType, uint handle)
+{
+ if (mPriv->immortalHandles) {
+ return;
+ }
+
+ Private::HandleContext *handleContext = mPriv->handleContext;
+ QMutexLocker locker(&handleContext->lock);
+
+ if (handleContext->types[handleType].toRelease.contains(handle)) {
+ handleContext->types[handleType].toRelease.remove(handle);
+ }
+
+ handleContext->types[handleType].refcounts[handle]++;
+}
+
+void Connection::unrefHandle(HandleType handleType, uint handle)
+{
+ if (mPriv->immortalHandles) {
+ return;
+ }
+
+ Private::HandleContext *handleContext = mPriv->handleContext;
+ QMutexLocker locker(&handleContext->lock);
+
+ Q_ASSERT(handleContext->types.contains(handleType));
+ Q_ASSERT(handleContext->types[handleType].refcounts.contains(handle));
+
+ if (!--handleContext->types[handleType].refcounts[handle]) {
+ handleContext->types[handleType].refcounts.remove(handle);
+ handleContext->types[handleType].toRelease.insert(handle);
+
+ if (!handleContext->types[handleType].releaseScheduled) {
+ if (!handleContext->types[handleType].requestsInFlight) {
+ debug() << "Lost last reference to at least one handle of type" <<
+ handleType <<
+ "and no requests in flight for that type - scheduling a release sweep";
+ QMetaObject::invokeMethod(this, "doReleaseSweep",
+ Qt::QueuedConnection, Q_ARG(uint, handleType));
+ handleContext->types[handleType].releaseScheduled = true;
+ }
+ }
+ }
+}
+
+void Connection::doReleaseSweep(uint handleType)
+{
+ if (mPriv->immortalHandles) {
+ return;
+ }
+
+ Private::HandleContext *handleContext = mPriv->handleContext;
+ QMutexLocker locker(&handleContext->lock);
+
+ Q_ASSERT(handleContext->types.contains(handleType));
+ Q_ASSERT(handleContext->types[handleType].releaseScheduled);
+
+ debug() << "Entering handle release sweep for type" << handleType;
+ handleContext->types[handleType].releaseScheduled = false;
+
+ if (handleContext->types[handleType].requestsInFlight > 0) {
+ debug() << " There are requests in flight, deferring sweep to when they have been completed";
+ return;
+ }
+
+ if (handleContext->types[handleType].toRelease.isEmpty()) {
+ debug() << " No handles to release - every one has been resurrected";
+ return;
+ }
+
+ debug() << " Releasing" << handleContext->types[handleType].toRelease.size() << "handles";
+
+ mPriv->baseInterface->ReleaseHandles(handleType, handleContext->types[handleType].toRelease.toList());
+ handleContext->types[handleType].toRelease.clear();
+}
+
+void Connection::handleRequestLanded(HandleType handleType)
+{
+ if (mPriv->immortalHandles) {
+ return;
+ }
+
+ Private::HandleContext *handleContext = mPriv->handleContext;
+ QMutexLocker locker(&handleContext->lock);
+
+ Q_ASSERT(handleContext->types.contains(handleType));
+ Q_ASSERT(handleContext->types[handleType].requestsInFlight > 0);
+
+ if (!--handleContext->types[handleType].requestsInFlight &&
+ !handleContext->types[handleType].toRelease.isEmpty() &&
+ !handleContext->types[handleType].releaseScheduled) {
+ debug() << "All handle requests for type" << handleType <<
+ "landed and there are handles of that type to release - scheduling a release sweep";
+ QMetaObject::invokeMethod(this, "doReleaseSweep", Qt::QueuedConnection, Q_ARG(uint, handleType));
+ handleContext->types[handleType].releaseScheduled = true;
+ }
+}
+
+void Connection::onSelfHandleChanged(uint handle)
+{
+ if (mPriv->selfHandle == handle) {
+ return;
+ }
+
+ if (mPriv->pendingStatus != ConnectionStatusConnected || !mPriv->selfHandle) {
+ debug() << "Got a self handle change before we have the initial self handle, ignoring";
+ return;
+ }
+
+ debug() << "Connection self handle changed to" << handle;
+ mPriv->selfHandle = handle;
+ emit selfHandleChanged(handle);
+
+ if (mPriv->introspectingSelfContact) {
+ // We're currently introspecting the SelfContact feature, but have started building the
+ // contact with the old handle, so we need to do it again with the new handle.
+
+ debug() << "The self contact is being built, will rebuild with the new handle shortly";
+ mPriv->reintrospectSelfContactRequired = true;
+ } else if (isReady(FeatureSelfContact)) {
+ // We've already introspected the SelfContact feature, so we can reinvoke the introspection
+ // logic directly to rebuild with the new handle.
+
+ debug() << "Re-building self contact for handle" << handle;
+ Private::introspectSelfContact(mPriv);
+ }
+
+ // If ReadinessHelper hasn't started introspecting SelfContact yet for the Connected state, we
+ // don't need to do anything. When it does start the introspection, it will do so using the
+ // correct handle.
+}
+
+void Connection::onBalanceChanged(const Tp::CurrencyAmount &value)
+{
+ mPriv->accountBalance = value;
+ emit accountBalanceChanged(value);
+}
+
+QString ConnectionHelper::statusReasonToErrorName(Tp::ConnectionStatusReason reason,
+ Tp::ConnectionStatus oldStatus)
+{
+ const char *errorName;
+
+ switch (reason) {
+ case ConnectionStatusReasonNoneSpecified:
+ errorName = TELEPATHY_ERROR_DISCONNECTED;
+ break;
+
+ case ConnectionStatusReasonRequested:
+ errorName = TELEPATHY_ERROR_CANCELLED;
+ break;
+
+ case ConnectionStatusReasonNetworkError:
+ errorName = TELEPATHY_ERROR_NETWORK_ERROR;
+ break;
+
+ case ConnectionStatusReasonAuthenticationFailed:
+ errorName = TELEPATHY_ERROR_AUTHENTICATION_FAILED;
+ break;
+
+ case ConnectionStatusReasonEncryptionError:
+ errorName = TELEPATHY_ERROR_ENCRYPTION_ERROR;
+ break;
+
+ case ConnectionStatusReasonNameInUse:
+ if (oldStatus == ConnectionStatusConnected) {
+ errorName = TELEPATHY_ERROR_CONNECTION_REPLACED;
+ } else {
+ errorName = TELEPATHY_ERROR_ALREADY_CONNECTED;
+ }
+ break;
+
+ case ConnectionStatusReasonCertNotProvided:
+ errorName = TELEPATHY_ERROR_CERT_NOT_PROVIDED;
+ break;
+
+ case ConnectionStatusReasonCertUntrusted:
+ errorName = TELEPATHY_ERROR_CERT_UNTRUSTED;
+ break;
+
+ case ConnectionStatusReasonCertExpired:
+ errorName = TELEPATHY_ERROR_CERT_EXPIRED;
+ break;
+
+ case ConnectionStatusReasonCertNotActivated:
+ errorName = TELEPATHY_ERROR_CERT_NOT_ACTIVATED;
+ break;
+
+ case ConnectionStatusReasonCertHostnameMismatch:
+ errorName = TELEPATHY_ERROR_CERT_HOSTNAME_MISMATCH;
+ break;
+
+ case ConnectionStatusReasonCertFingerprintMismatch:
+ errorName = TELEPATHY_ERROR_CERT_FINGERPRINT_MISMATCH;
+ break;
+
+ case ConnectionStatusReasonCertSelfSigned:
+ errorName = TELEPATHY_ERROR_CERT_SELF_SIGNED;
+ break;
+
+ case ConnectionStatusReasonCertOtherError:
+ errorName = TELEPATHY_ERROR_CERT_INVALID;
+ break;
+
+ default:
+ errorName = TELEPATHY_ERROR_DISCONNECTED;
+ break;
+ }
+
+ return QLatin1String(errorName);
+}
+
+/**
+ * \fn void Connection::statusChanged(Tp::ConnectionStatus newStatus)
+ *
+ * Indicates that the connection's status has changed and that all previously requested features are
+ * now ready to use for the new status.
+ *
+ * Legitimate uses for this signal, apart from waiting for a given connection status to be ready,
+ * include updating an animation based on the connection being in ConnectionStatusConnecting,
+ * ConnectionStatusConnected and ConnectionStatusDisconnected, and otherwise showing progress
+ * indication to the user. It should, however, NEVER be used for error handling:
+ *
+ * This signal doesn't contain the status reason as an argument, because statusChanged() shouldn't
+ * be used for error-handling. There are numerous cases in which a Connection may become unusable
+ * without there being a status change to ConnectionStatusDisconnected. All of these cases, and
+ * being disconnected itself, are signaled by invalidated() with appropriate error names. On the
+ * other hand, the reason for the status going to ConnectionStatusConnecting or
+ * ConnectionStatusConnected will always be ConnectionStatusReasonRequested, so signaling that would
+ * be useless.
+ *
+ * The status reason, as returned by statusReason(), may however be used as a fallback for error
+ * handling in slots connected to the invalidated() signal, if the client doesn't understand a
+ * particular (likely domain-specific if so) error name given by invalidateReason().
+ *
+ * \param newStatus The new status of the connection, as would be returned by status().
+ */
+
+/**
+ * \fn void Connection::selfHandleChanged(uint newHandle)
+ *
+ * Emitted when the value of selfHandle() changes.
+ *
+ * \param newHandle The new connection self handle.
+ * \sa selfHandle()
+ */
+
+/**
+ * \fn void Connection::selfContactChanged()
+ *
+ * Emitted when the value of selfContact() changes.
+ *
+ * \sa selfContact()
+ */
+
+/**
+ * \fn void Connection::accountBalanceChanged(const Tp::CurrencyAmount &accountBalance)
+ *
+ * Emitted when the value of accountBalance() changes.
+ *
+ * \param accountBalance The new user's balance of this connection.
+ * \sa accountBalance()
+ */
+
+} // Tp
diff --git a/TelepathyQt/connection.h b/TelepathyQt/connection.h
new file mode 100644
index 00000000..0ca47a53
--- /dev/null
+++ b/TelepathyQt/connection.h
@@ -0,0 +1,249 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_connection_h_HEADER_GUARD_
+#define _TelepathyQt_connection_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/_gen/cli-connection.h>
+
+#include <TelepathyQt/ConnectionCapabilities>
+#include <TelepathyQt/Contact>
+#include <TelepathyQt/DBus>
+#include <TelepathyQt/DBusProxy>
+#include <TelepathyQt/OptionalInterfaceFactory>
+#include <TelepathyQt/ReadinessHelper>
+#include <TelepathyQt/Types>
+#include <TelepathyQt/SharedPtr>
+
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/Types>
+
+#include <QSet>
+#include <QString>
+#include <QStringList>
+
+namespace Tp
+{
+
+class Channel;
+class ConnectionCapabilities;
+class ConnectionLowlevel;
+class Contact;
+class ContactManager;
+class PendingChannel;
+class PendingContactAttributes;
+class PendingHandles;
+class PendingOperation;
+class PendingReady;
+
+class TP_QT_EXPORT Connection : public StatefulDBusProxy,
+ public OptionalInterfaceFactory<Connection>
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(Connection)
+
+public:
+ static const Feature FeatureCore;
+ static const Feature FeatureSelfContact;
+ static const Feature FeatureSimplePresence;
+ static const Feature FeatureRoster;
+ static const Feature FeatureRosterGroups;
+ static const Feature FeatureAccountBalance; // TODO unit tests for this
+ static const Feature FeatureConnected;
+
+ static ConnectionPtr create(const QString &busName,
+ const QString &objectPath,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory);
+ static ConnectionPtr create(const QDBusConnection &bus,
+ const QString &busName, const QString &objectPath,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory);
+
+ virtual ~Connection();
+
+ ChannelFactoryConstPtr channelFactory() const;
+ ContactFactoryConstPtr contactFactory() const;
+
+ QString cmName() const;
+ QString protocolName() const;
+
+ ConnectionStatus status() const;
+ ConnectionStatusReason statusReason() const;
+
+ class ErrorDetails
+ {
+ public:
+ ErrorDetails();
+ ErrorDetails(const QVariantMap &details);
+ ErrorDetails(const ErrorDetails &other);
+ ~ErrorDetails();
+
+ ErrorDetails &operator=(const ErrorDetails &other);
+
+ bool isValid() const { return mPriv.constData() != 0; }
+
+ bool hasDebugMessage() const
+ {
+ return allDetails().contains(QLatin1String("debug-message"));
+ }
+
+ QString debugMessage() const
+ {
+ return qdbus_cast<QString>(allDetails().value(QLatin1String("debug-message")));
+ }
+
+ bool hasServerMessage() const
+ {
+ return allDetails().contains(QLatin1String("server-message"));
+ }
+
+ QString serverMessage() const
+ {
+ return qdbus_cast<QString>(allDetails().value(QLatin1String("server-message")));
+ }
+
+ bool hasUserRequested() const
+ {
+ return allDetails().contains(QLatin1String("user-requested"));
+ }
+
+ bool userRequested() const
+ {
+ return qdbus_cast<bool>(allDetails().value(QLatin1String("user-requested")));
+ }
+
+ bool hasExpectedHostname() const
+ {
+ return allDetails().contains(QLatin1String("expected-hostname"));
+ }
+
+ QString expectedHostname() const
+ {
+ return qdbus_cast<QString>(allDetails().value(QLatin1String("expected-hostname")));
+ }
+
+ bool hasCertificateHostname() const
+ {
+ return allDetails().contains(QLatin1String("certificate-hostname"));
+ }
+
+ QString certificateHostname() const
+ {
+ return qdbus_cast<QString>(allDetails().value(QLatin1String("certificate-hostname")));
+ }
+
+ QVariantMap allDetails() const;
+
+ private:
+ friend class Connection;
+
+ struct Private;
+ friend struct Private;
+ QSharedDataPointer<Private> mPriv;
+ };
+
+ const ErrorDetails &errorDetails() const;
+
+ uint selfHandle() const;
+ ContactPtr selfContact() const;
+
+ CurrencyAmount accountBalance() const;
+
+ ConnectionCapabilities capabilities() const;
+
+ ContactManagerPtr contactManager() const;
+
+#if defined(BUILDING_TP_QT) || defined(TP_QT_ENABLE_LOWLEVEL_API)
+ ConnectionLowlevelPtr lowlevel();
+ ConnectionLowlevelConstPtr lowlevel() const;
+#endif
+
+Q_SIGNALS:
+ void statusChanged(Tp::ConnectionStatus newStatus);
+
+ void selfHandleChanged(uint newHandle);
+ // FIXME: might not need this when Renaming is fixed and mapped to Contacts
+ void selfContactChanged();
+
+ void accountBalanceChanged(const Tp::CurrencyAmount &accountBalance);
+
+protected:
+ Connection(const QDBusConnection &bus, const QString &busName,
+ const QString &objectPath,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory,
+ const Feature &coreFeature);
+
+ Client::ConnectionInterface *baseInterface() const;
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onStatusReady(uint status);
+ TP_QT_NO_EXPORT void onStatusChanged(uint status, uint reason);
+ TP_QT_NO_EXPORT void onConnectionError(const QString &error, const QVariantMap &details);
+ TP_QT_NO_EXPORT void gotMainProperties(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void gotStatus(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void gotInterfaces(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void gotSelfHandle(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void gotCapabilities(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void gotContactAttributeInterfaces(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void gotSimpleStatuses(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void gotSelfContact(Tp::PendingOperation *op);
+
+ TP_QT_NO_EXPORT void onIntrospectRosterFinished(Tp::PendingOperation *op);
+ TP_QT_NO_EXPORT void onIntrospectRosterGroupsFinished(Tp::PendingOperation *op);
+
+ TP_QT_NO_EXPORT void doReleaseSweep(uint handleType);
+
+ TP_QT_NO_EXPORT void onSelfHandleChanged(uint);
+
+ TP_QT_NO_EXPORT void gotBalance(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void onBalanceChanged(const Tp::CurrencyAmount &);
+
+private:
+ class PendingConnect;
+ friend class ConnectionLowlevel;
+ friend class PendingChannel;
+ friend class PendingConnect;
+ friend class PendingContactAttributes;
+ friend class PendingContacts;
+ friend class PendingHandles;
+ friend class ReferencedHandles;
+
+ TP_QT_NO_EXPORT void refHandle(HandleType handleType, uint handle);
+ TP_QT_NO_EXPORT void unrefHandle(HandleType handleType, uint handle);
+ TP_QT_NO_EXPORT void handleRequestLanded(HandleType handleType);
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+Q_DECLARE_METATYPE(Tp::Connection::ErrorDetails);
+
+#endif
diff --git a/TelepathyQt/connection.xml b/TelepathyQt/connection.xml
new file mode 100644
index 00000000..bf726f58
--- /dev/null
+++ b/TelepathyQt/connection.xml
@@ -0,0 +1,30 @@
+<tp:spec
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<tp:title>Connection interfaces</tp:title>
+
+<xi:include href="../spec/Connection.xml"/>
+
+<xi:include href="../spec/Connection_Interface_Aliasing.xml"/>
+<xi:include href="../spec/Connection_Interface_Anonymity.xml"/>
+<xi:include href="../spec/Connection_Interface_Avatars.xml"/>
+<xi:include href="../spec/Connection_Interface_Balance.xml"/>
+<xi:include href="../spec/Connection_Interface_Capabilities.xml"/>
+<xi:include href="../spec/Connection_Interface_Cellular.xml"/>
+<xi:include href="../spec/Connection_Interface_Client_Types.xml"/>
+<xi:include href="../spec/Connection_Interface_Contacts.xml"/>
+<xi:include href="../spec/Connection_Interface_Contact_Blocking.xml"/>
+<xi:include href="../spec/Connection_Interface_Contact_Capabilities.xml"/>
+<xi:include href="../spec/Connection_Interface_Contact_Groups.xml"/>
+<xi:include href="../spec/Connection_Interface_Contact_Info.xml"/>
+<xi:include href="../spec/Connection_Interface_Contact_List.xml"/>
+<xi:include href="../spec/Connection_Interface_Location.xml"/>
+<xi:include href="../spec/Connection_Interface_Mail_Notification.xml"/>
+<xi:include href="../spec/Connection_Interface_Power_Saving.xml"/>
+<xi:include href="../spec/Connection_Interface_Presence.xml"/>
+<xi:include href="../spec/Connection_Interface_Requests.xml"/>
+<xi:include href="../spec/Connection_Interface_Service_Point.xml"/>
+<xi:include href="../spec/Connection_Interface_Simple_Presence.xml"/>
+
+</tp:spec>
diff --git a/TelepathyQt/constants.h b/TelepathyQt/constants.h
new file mode 100644
index 00000000..a3146d13
--- /dev/null
+++ b/TelepathyQt/constants.h
@@ -0,0 +1,178 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_constants_h_HEADER_GUARD_
+#define _TelepathyQt_constants_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+/**
+ * \addtogroup typesconstants Types and constants
+ *
+ * Enumerated, flag, structure, list and mapping types and utility constants.
+ */
+
+/**
+ * \defgroup utilityconsts Utility string constants
+ * \ingroup typesconstants
+ *
+ * Utility constants which aren't generated from the specification but are
+ * useful for working with the Telepathy protocol.
+ * @{
+ */
+
+/**
+ * The prefix for a connection manager's bus name, to which the CM's name (e.g.
+ * "gabble") should be appended.
+ */
+#define TP_QT_CONNECTION_MANAGER_BUS_NAME_BASE QLatin1String("org.freedesktop.Telepathy.ConnectionManager.")
+
+/**
+ * The prefix for a connection manager's object path, to which the CM's name
+ * (e.g. "gabble") should be appended.
+ */
+#define TP_QT_CONNECTION_MANAGER_OBJECT_PATH_BASE QLatin1String("/org/freedesktop/Telepathy/ConnectionManager/")
+
+/**
+ * The prefix for a connection's bus name, to which the CM's name (e.g.
+ * "gabble"), the protocol (e.g. "jabber") and an element
+ * representing the account should be appended.
+ */
+#define TP_QT_CONNECTION_BUS_NAME_BASE QLatin1String("org.freedesktop.Telepathy.Connection.")
+
+/**
+ * The prefix for a connection's object path, to which the CM's name (e.g.
+ * "gabble"), the protocol (e.g. "jabber") and an element
+ * representing the account should be appended.
+ */
+#define TP_QT_CONNECTION_OBJECT_PATH_BASE "/org/freedesktop/Telepathy/Connection/"
+
+/**
+ * The well-known bus name of the Account Manager.
+ *
+ * \see Tp::AccountManager
+ */
+#define TP_QT_ACCOUNT_MANAGER_BUS_NAME \
+ (QLatin1String("org.freedesktop.Telepathy.AccountManager"))
+
+/**
+ * The object path of the Account Manager object.
+ *
+ * \see Tp::AccountManager
+ */
+#define TP_QT_ACCOUNT_MANAGER_OBJECT_PATH \
+ (QLatin1String("/org/freedesktop/Telepathy/AccountManager"))
+
+/**
+ * The well-known bus name of the Channel Dispatcher.
+ */
+#define TP_QT_CHANNEL_DISPATCHER_BUS_NAME \
+ (QLatin1String("org.freedesktop.Telepathy.ChannelDispatcher"))
+
+/**
+ * The object path of the Channel Dispatcherr object.
+ */
+#define TP_QT_CHANNEL_DISPATCHER_OBJECT_PATH \
+ (QLatin1String("/org/freedesktop/Telepathy/ChannelDispatcher"))
+
+/**
+ * The prefix for an Account's object path, to which the CM's name (e.g.
+ * "gabble"), the protocol (e.g. "jabber") and an element
+ * identifying the particular account should be appended.
+ *
+ * \see Tp::Account
+ */
+#define TP_QT_ACCOUNT_OBJECT_PATH_BASE \
+ (QLatin1String("/org/freedesktop/Telepathy/Account"))
+
+/**
+ * @}
+ */
+
+#include <TelepathyQt/_gen/constants.h>
+
+/**
+ * \ingroup errorstrconsts
+ *
+ * The error name "org.freedesktop.DBus.Error.NameHasNoOwner" as a QLatin1String.
+ *
+ * Raised by the D-Bus daemon when looking up the owner of a well-known name,
+ * if no process owns that name.
+ *
+ * Also used by DBusProxy to indicate that the owner of a well-known name
+ * has disappeared (usually indicating that the process owning that name
+ * exited or crashed).
+ */
+#define TP_QT_DBUS_ERROR_NAME_HAS_NO_OWNER \
+ (QLatin1String("org.freedesktop.DBus.Error.NameHasNoOwner"))
+
+/**
+ * \ingroup errorstrconsts
+ *
+ * The error name "org.freedesktop.DBus.Error.UnknownInterface" as a QLatin1String.
+ */
+#define TP_QT_DBUS_ERROR_UNKNOWN_INTERFACE \
+ (QLatin1String("org.freedesktop.DBus.Error.UnknownInterface"))
+
+/**
+ * \ingroup errorstrconsts
+ *
+ * The error name "org.freedesktop.DBus.Error.UnknownMethod" as a QLatin1String.
+ *
+ * Raised by the D-Bus daemon when the method name invoked isn't
+ * known by the object you invoked it on.
+ */
+#define TP_QT_DBUS_ERROR_UNKNOWN_METHOD \
+ (QLatin1String("org.freedesktop.DBus.Error.UnknownMethod"))
+
+/**
+ * \ingroup errorstrconsts
+ *
+ * The error name "org.freedesktop.Telepathy.Qt4.Error.ObjectRemoved" as a QLatin1String.
+ */
+#define TP_QT_ERROR_OBJECT_REMOVED \
+ (QLatin1String("org.freedesktop.Telepathy.Qt4.Error.ObjectRemoved"))
+
+/**
+ * \ingroup errorstrconsts
+ *
+ * The error name "org.freedesktop.Telepathy.Qt4.Error.Inconsistent" as a QLatin1String.
+ */
+#define TP_QT_ERROR_INCONSISTENT \
+ (QLatin1String("org.freedesktop.Telepathy.Qt4.Error.Inconsistent"))
+
+/**
+ * \ingroup errorstrconsts
+ *
+ * The error name "org.freedesktop.Telepathy.Qt4.Error.Orphaned" as a QLatin1String.
+ *
+ * This error is used when the "parent" proxy of an object gets invalidated. For example, a Channel
+ * whose corresponding Connection is invalidated invalidates itself with this error, as do leftover
+ * StreamTube connections when their parent StreamTubeChannel is invalidated. The invalidation
+ * reason of the parent proxy might provide more information on the cause of the error.
+ */
+#define TP_QT_ERROR_ORPHANED \
+ (QLatin1String("org.freedesktop.Telepathy.Qt4.Error.Orphaned"))
+
+#endif
diff --git a/TelepathyQt/contact-capabilities.cpp b/TelepathyQt/contact-capabilities.cpp
new file mode 100644
index 00000000..7a59f254
--- /dev/null
+++ b/TelepathyQt/contact-capabilities.cpp
@@ -0,0 +1,127 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/ContactCapabilities>
+
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+/**
+ * \class ContactCapabilities
+ * \ingroup clientconn
+ * \headerfile TelepathyQt/contact-capabilities.h <TelepathyQt/ContactCapabilities>
+ *
+ * \brief The ContactCapabilities class represents the capabilities of a
+ * Contact.
+ */
+
+/**
+ * Construct a new ContactCapabilities object.
+ */
+ContactCapabilities::ContactCapabilities()
+ : CapabilitiesBase()
+{
+}
+
+/**
+ * Construct a new ContactCapabilities object.
+ */
+ContactCapabilities::ContactCapabilities(bool specificToContact)
+ : CapabilitiesBase(specificToContact)
+{
+}
+
+/**
+ * Construct a new ContactCapabilities object using the give \a rccs.
+ *
+ * \param rccs RequestableChannelClassList representing the capabilities of a
+ * contact.
+ */
+ContactCapabilities::ContactCapabilities(const RequestableChannelClassList &rccs,
+ bool specificToContact)
+ : CapabilitiesBase(rccs, specificToContact)
+{
+}
+
+/**
+ * Construct a new ContactCapabilities object using the give \a rccSpecs.
+ *
+ * \param rccSpecs RequestableChannelClassList representing the capabilities of a
+ * contact.
+ */
+ContactCapabilities::ContactCapabilities(const RequestableChannelClassSpecList &rccSpecs,
+ bool specificToContact)
+ : CapabilitiesBase(rccSpecs, specificToContact)
+{
+}
+
+/**
+ * Class destructor.
+ */
+ContactCapabilities::~ContactCapabilities()
+{
+}
+
+/**
+ * Return whether creating a StreamTube channel, using the given \a service, by providing a
+ * contact identifier is supported.
+ *
+ * \return \c true if supported, \c false otherwise.
+ */
+bool ContactCapabilities::streamTubes(const QString &service) const
+{
+ RequestableChannelClassSpec streamTubeSpec = RequestableChannelClassSpec::streamTube(service);
+ RequestableChannelClassSpecList rccSpecs = allClassSpecs();
+ foreach (const RequestableChannelClassSpec &rccSpec, rccSpecs) {
+ if (rccSpec.supports(streamTubeSpec)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Return the supported StreamTube services.
+ *
+ * \return A list of supported StreamTube services.
+ */
+QStringList ContactCapabilities::streamTubeServices() const
+{
+ QSet<QString> ret;
+
+ RequestableChannelClassSpecList rccSpecs = allClassSpecs();
+ foreach (const RequestableChannelClassSpec &rccSpec, rccSpecs) {
+ if (rccSpec.channelType() == TP_QT_IFACE_CHANNEL_TYPE_STREAM_TUBE &&
+ rccSpec.targetHandleType() == HandleTypeContact &&
+ rccSpec.hasFixedProperty(
+ TP_QT_IFACE_CHANNEL_TYPE_STREAM_TUBE + QLatin1String(".Service"))) {
+ ret << rccSpec.fixedProperty(
+ TP_QT_IFACE_CHANNEL_TYPE_STREAM_TUBE + QLatin1String(".Service")).toString();
+ }
+ }
+
+ return ret.toList();
+}
+
+} // Tp
diff --git a/TelepathyQt/contact-capabilities.h b/TelepathyQt/contact-capabilities.h
new file mode 100644
index 00000000..52de21e2
--- /dev/null
+++ b/TelepathyQt/contact-capabilities.h
@@ -0,0 +1,66 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_contact_capabilities_h_HEADER_GUARD_
+#define _TelepathyQt_contact_capabilities_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/CapabilitiesBase>
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+class TestBackdoors;
+
+class TP_QT_EXPORT ContactCapabilities : public CapabilitiesBase
+{
+public:
+ ContactCapabilities();
+ virtual ~ContactCapabilities();
+
+ bool streamTubes(const QString &service) const;
+ QStringList streamTubeServices() const;
+
+ // later:
+ // bool dbusTubes(const QString &service) const;
+ // QStringList dbusTubeServices() const;
+
+protected:
+ friend class Contact;
+ friend class TestBackdoors;
+
+ ContactCapabilities(bool specificToContact);
+ ContactCapabilities(const RequestableChannelClassList &rccs,
+ bool specificToContact);
+ ContactCapabilities(const RequestableChannelClassSpecList &rccSpecs,
+ bool specificToContact);
+};
+
+} // Tp
+
+Q_DECLARE_METATYPE(Tp::ContactCapabilities);
+
+#endif
diff --git a/TelepathyQt/contact-factory.cpp b/TelepathyQt/contact-factory.cpp
new file mode 100644
index 00000000..cd93acde
--- /dev/null
+++ b/TelepathyQt/contact-factory.cpp
@@ -0,0 +1,146 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/ContactFactory>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT ContactFactory::Private
+{
+ Features features;
+};
+
+/**
+ * \class ContactFactory
+ * \ingroup utils
+ * \headerfile TelepathyQt/contact-factory.h <TelepathyQt/ContactFactory>
+ *
+ * \brief The ContactFactory class is responsible for constructing Contact
+ * objects according to application-defined settings.
+ */
+
+/**
+ * Creates a new ContactFactory.
+ *
+ * \param features The features to make ready on constructed contacts.
+ * \returns A pointer to the created factory.
+ */
+ContactFactoryPtr ContactFactory::create(const Features &features)
+{
+ return ContactFactoryPtr(new ContactFactory(features));
+}
+
+/**
+ * Class constructor.
+ *
+ * \param features The features to make ready on constructed contacts.
+ */
+ContactFactory::ContactFactory(const Features &features)
+ : mPriv(new Private)
+{
+ addFeatures(features);
+}
+
+/**
+ * Class destructor.
+ */
+ContactFactory::~ContactFactory()
+{
+ delete mPriv;
+}
+
+/**
+ * Gets the features this factory will make ready on constructed contacts.
+ *
+ * \return The set of features.
+ */
+Features ContactFactory::features() const
+{
+ Features features = mPriv->features;
+ // FeatureAvatarData depends on FeatureAvatarToken
+ if (features.contains(Contact::FeatureAvatarData) &&
+ !features.contains(Contact::FeatureAvatarToken)) {
+ features.insert(Contact::FeatureAvatarToken);
+ }
+
+ return features;
+}
+
+/**
+ * Adds a single feature this factory will make ready on further constructed contacts.
+ *
+ * No feature removal is provided, to guard against uncooperative modules removing features other
+ * modules have set and depend on.
+ *
+ * \param feature The feature to add.
+ */
+void ContactFactory::addFeature(const Feature &feature)
+{
+ addFeatures(Features(feature));
+}
+
+/**
+ * Adds a set of features this factory will make ready on further constructed contacts.
+ *
+ * No feature removal is provided, to guard against uncooperative modules removing features other
+ * modules have set and depend on.
+ *
+ * \param features The features to add.
+ */
+void ContactFactory::addFeatures(const Features &features)
+{
+ mPriv->features.unite(features);
+}
+
+/**
+ * Can be used by subclasses to override the Contact subclass constructed by the factory.
+ *
+ * The default implementation constructs Tp::Contact objects.
+ *
+ * \param manager The contact manager this contact belongs.
+ * \param handle The contact handle.
+ * \param features The desired contact features.
+ * \param attributes The desired contact attributes.
+ * \return A pointer to the constructed contact.
+ */
+ContactPtr ContactFactory::construct(Tp::ContactManager *manager, const ReferencedHandles &handle,
+ const Features &features, const QVariantMap &attributes) const
+{
+ ContactPtr contact = ContactPtr(new Contact(manager, handle, features, attributes));
+ return contact;
+}
+
+/**
+ * Can be used by subclasses to do arbitrary manipulation on constructed Contact objects.
+ *
+ * The default implementation does nothing.
+ *
+ * \param contact The contact to be prepared.
+ * \return A PendingOperation used to prepare the contact or NULL if there is nothing to prepare.
+ */
+PendingOperation *ContactFactory::prepare(const ContactPtr &contact) const
+{
+ return NULL;
+}
+
+}
diff --git a/TelepathyQt/contact-factory.h b/TelepathyQt/contact-factory.h
new file mode 100644
index 00000000..d8355f2a
--- /dev/null
+++ b/TelepathyQt/contact-factory.h
@@ -0,0 +1,76 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_contact_factory_h_HEADER_GUARD_
+#define _TelepathyQt_contact_factory_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Contact>
+#include <TelepathyQt/Feature>
+#include <TelepathyQt/Global>
+#include <TelepathyQt/Types>
+
+#include <QSet>
+#include <QtGlobal>
+
+namespace Tp
+{
+
+class ContactManager;
+class ReferencedHandles;
+
+class TP_QT_EXPORT ContactFactory : public RefCounted
+{
+ Q_DISABLE_COPY(ContactFactory)
+
+public:
+ static ContactFactoryPtr create(const Features &features = Features());
+
+ virtual ~ContactFactory();
+
+ Features features() const;
+
+ void addFeature(const Feature &feature);
+ void addFeatures(const Features &features);
+
+protected:
+ ContactFactory(const Features &features);
+
+ virtual ContactPtr construct(ContactManager *manager, const ReferencedHandles &handle,
+ const Features &features, const QVariantMap &attributes) const;
+ virtual PendingOperation *prepare(const ContactPtr &contact) const;
+
+private:
+ friend class ContactManager;
+ friend class PendingContacts;
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/contact-manager-internal.h b/TelepathyQt/contact-manager-internal.h
new file mode 100644
index 00000000..e3bba7f4
--- /dev/null
+++ b/TelepathyQt/contact-manager-internal.h
@@ -0,0 +1,389 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_contact_manager_internal_h_HEADER_GUARD_
+#define _TelepathyQt_contact_manager_internal_h_HEADER_GUARD_
+
+#include <TelepathyQt/ContactManager>
+#include <TelepathyQt/Global>
+#include <TelepathyQt/PendingOperation>
+#include <TelepathyQt/Types>
+
+#include <QList>
+#include <QMap>
+#include <QObject>
+#include <QQueue>
+#include <QString>
+#include <QStringList>
+
+namespace Tp
+{
+
+class TP_QT_NO_EXPORT ContactManager::Roster : public QObject
+{
+ Q_OBJECT
+
+public:
+ Roster(ContactManager *manager);
+ virtual ~Roster();
+
+ ContactListState state() const;
+
+ PendingOperation *introspect();
+ PendingOperation *introspectGroups();
+ void reset();
+
+ Contacts allKnownContacts() const;
+ QStringList allKnownGroups() const;
+
+ PendingOperation *addGroup(const QString &group);
+ PendingOperation *removeGroup(const QString &group);
+
+ Contacts groupContacts(const QString &group) const;
+ PendingOperation *addContactsToGroup(const QString &group,
+ const QList<ContactPtr> &contacts);
+ PendingOperation *removeContactsFromGroup(const QString &group,
+ const QList<ContactPtr> &contacts);
+
+ bool canRequestPresenceSubscription() const;
+ bool subscriptionRequestHasMessage() const;
+ PendingOperation *requestPresenceSubscription(
+ const QList<ContactPtr> &contacts, const QString &message);
+ bool canRemovePresenceSubscription() const;
+ bool subscriptionRemovalHasMessage() const;
+ bool canRescindPresenceSubscriptionRequest() const;
+ bool subscriptionRescindingHasMessage() const;
+ PendingOperation *removePresenceSubscription(
+ const QList<ContactPtr> &contacts, const QString &message);
+ bool canAuthorizePresencePublication() const;
+ bool publicationAuthorizationHasMessage() const;
+ PendingOperation *authorizePresencePublication(
+ const QList<ContactPtr> &contacts, const QString &message);
+ bool publicationRejectionHasMessage() const;
+ bool canRemovePresencePublication() const;
+ bool publicationRemovalHasMessage() const;
+ PendingOperation *removePresencePublication(
+ const QList<ContactPtr> &contacts, const QString &message);
+ PendingOperation *removeContacts(
+ const QList<ContactPtr> &contacts, const QString &message);
+
+ bool canBlockContacts() const;
+ bool canReportAbuse() const;
+ PendingOperation *blockContacts(const QList<ContactPtr> &contacts, bool value, bool reportAbuse);
+
+private Q_SLOTS:
+ void gotContactBlockingCapabilities(Tp::PendingOperation *op);
+ void gotContactBlockingBlockedContacts(QDBusPendingCallWatcher *watcher);
+ void onContactBlockingBlockedContactsChanged(
+ const Tp::HandleIdentifierMap &added,
+ const Tp::HandleIdentifierMap &removed);
+
+ void gotContactListProperties(Tp::PendingOperation *op);
+ void gotContactListContacts(QDBusPendingCallWatcher *watcher);
+ void setStateSuccess();
+ void onContactListStateChanged(uint state);
+ void onContactListContactsChangedWithId(const Tp::ContactSubscriptionMap &changes,
+ const Tp::HandleIdentifierMap &ids, const Tp::HandleIdentifierMap &removals);
+ void onContactListContactsChanged(const Tp::ContactSubscriptionMap &changes,
+ const Tp::UIntList &removals);
+
+ void onContactListBlockedContactsConstructed(Tp::PendingOperation *op);
+ void onContactListNewContactsConstructed(Tp::PendingOperation *op);
+ void onContactListGroupsChanged(const Tp::UIntList &contacts,
+ const QStringList &added, const QStringList &removed);
+ void onContactListGroupsCreated(const QStringList &names);
+ void onContactListGroupRenamed(const QString &oldName, const QString &newName);
+ void onContactListGroupsRemoved(const QStringList &names);
+
+ void onModifyFinished(Tp::PendingOperation *op);
+ void onModifyFinishSignaled();
+
+ void gotContactListChannelHandle(Tp::PendingOperation *op);
+ void gotContactListChannel(Tp::PendingOperation *op);
+ void onContactListChannelReady();
+
+ void gotContactListGroupsProperties(Tp::PendingOperation *op);
+ void onContactListContactsUpgraded(Tp::PendingOperation *op);
+
+ void onNewChannels(const Tp::ChannelDetailsList &channelDetailsList);
+ void onContactListGroupChannelReady(Tp::PendingOperation *op);
+ void gotChannels(QDBusPendingCallWatcher *watcher);
+
+ void onStoredChannelMembersChanged(
+ const Tp::Contacts &groupMembersAdded,
+ const Tp::Contacts &groupLocalPendingMembersAdded,
+ const Tp::Contacts &groupRemotePendingMembersAdded,
+ const Tp::Contacts &groupMembersRemoved,
+ const Tp::Channel::GroupMemberChangeDetails &details);
+ void onSubscribeChannelMembersChanged(
+ const Tp::Contacts &groupMembersAdded,
+ const Tp::Contacts &groupLocalPendingMembersAdded,
+ const Tp::Contacts &groupRemotePendingMembersAdded,
+ const Tp::Contacts &groupMembersRemoved,
+ const Tp::Channel::GroupMemberChangeDetails &details);
+ void onPublishChannelMembersChanged(
+ const Tp::Contacts &groupMembersAdded,
+ const Tp::Contacts &groupLocalPendingMembersAdded,
+ const Tp::Contacts &groupRemotePendingMembersAdded,
+ const Tp::Contacts &groupMembersRemoved,
+ const Tp::Channel::GroupMemberChangeDetails &details);
+ void onDenyChannelMembersChanged(
+ const Tp::Contacts &groupMembersAdded,
+ const Tp::Contacts &groupLocalPendingMembersAdded,
+ const Tp::Contacts &groupRemotePendingMembersAdded,
+ const Tp::Contacts &groupMembersRemoved,
+ const Tp::Channel::GroupMemberChangeDetails &details);
+
+ void onContactListGroupMembersChanged(
+ const Tp::Contacts &groupMembersAdded,
+ const Tp::Contacts &groupLocalPendingMembersAdded,
+ const Tp::Contacts &groupRemotePendingMembersAdded,
+ const Tp::Contacts &groupMembersRemoved,
+ const Tp::Channel::GroupMemberChangeDetails &details);
+ void onContactListGroupRemoved(Tp::DBusProxy *proxy,
+ const QString &errorName, const QString &errorMessage);
+
+private:
+ struct ChannelInfo;
+ struct BlockedContactsChangedInfo;
+ struct UpdateInfo;
+ struct GroupsUpdateInfo;
+ struct GroupRenamedInfo;
+ class ModifyFinishOp;
+ class RemoveGroupOp;
+
+ void introspectContactBlocking();
+ void introspectContactBlockingBlockedContacts();
+ void introspectContactList();
+ void introspectContactListContacts();
+ void processContactListChanges();
+ void processContactListBlockedContactsChanged();
+ void processContactListUpdates();
+ void processContactListGroupsUpdates();
+ void processContactListGroupsCreated();
+ void processContactListGroupRenamed();
+ void processContactListGroupsRemoved();
+ void processFinishedModify();
+ PendingOperation *queuedFinishVoid(const QDBusPendingCall &call);
+ void setContactListChannelsReady();
+ void updateContactsBlockState();
+ void updateContactsPresenceState();
+ void computeKnownContactsChanges(const Contacts &added,
+ const Contacts &pendingAdded, const Contacts &remotePendingAdded,
+ const Contacts &removed, const Channel::GroupMemberChangeDetails &details);
+ void checkContactListGroupsReady();
+ void setContactListGroupChannelsReady();
+ QString addContactListGroupChannel(const ChannelPtr &contactListGroupChannel);
+
+ ContactManager *contactManager;
+
+ Contacts cachedAllKnownContacts;
+
+ bool usingFallbackContactList;
+ bool hasContactBlockingInterface;
+
+ PendingOperation *introspectPendingOp;
+ PendingOperation *introspectGroupsPendingOp;
+ uint pendingContactListState;
+ uint contactListState;
+ bool canReportAbusive;
+ bool gotContactBlockingInitialBlockedContacts;
+ bool canChangeContactList;
+ bool contactListRequestUsesMessage;
+ bool gotContactListInitialContacts;
+ bool gotContactListContactsChangedWithId;
+ bool groupsReintrospectionRequired;
+ QSet<QString> cachedAllKnownGroups;
+ bool contactListGroupPropertiesReceived;
+ QQueue<void (ContactManager::Roster::*)()> contactListChangesQueue;
+ QQueue<BlockedContactsChangedInfo> contactListBlockedContactsChangedQueue;
+ QQueue<UpdateInfo> contactListUpdatesQueue;
+ QQueue<GroupsUpdateInfo> contactListGroupsUpdatesQueue;
+ QQueue<QStringList> contactListGroupsCreatedQueue;
+ QQueue<GroupRenamedInfo> contactListGroupRenamedQueue;
+ QQueue<QStringList> contactListGroupsRemovedQueue;
+ bool processingContactListChanges;
+
+ QMap<PendingOperation * /* actual */, ModifyFinishOp *> returnedModifyOps;
+ QQueue<ModifyFinishOp *> modifyFinishQueue;
+
+ // old roster API
+ uint contactListChannelsReady;
+ QMap<uint, ChannelInfo> contactListChannels;
+ ChannelPtr subscribeChannel;
+ ChannelPtr publishChannel;
+ ChannelPtr storedChannel;
+ ChannelPtr denyChannel;
+
+ // Number of things left to do before the Groups feature is ready
+ // 1 for Get("Channels") + 1 per channel not ready
+ uint featureContactListGroupsTodo;
+ QList<ChannelPtr> pendingContactListGroupChannels;
+ QMap<QString, ChannelPtr> contactListGroupChannels;
+ QList<ChannelPtr> removedContactListGroupChannels;
+
+ // If RosterGroups introspection completing should advance the ContactManager state to Success
+ bool groupsSetSuccess;
+
+ // Contact list contacts using the Conn.I.ContactList API
+ Contacts contactListContacts;
+ // Blocked contacts using the new ContactBlocking API
+ Contacts blockedContacts;
+};
+
+struct TP_QT_NO_EXPORT ContactManager::Roster::ChannelInfo
+{
+ enum Type {
+ TypeSubscribe = 0,
+ TypePublish,
+ TypeStored,
+ TypeDeny,
+ LastType
+ };
+
+ ChannelInfo()
+ : type((Type) -1)
+ {
+ }
+
+ ChannelInfo(Type type)
+ : type(type)
+ {
+ }
+
+ static QString identifierForType(Type type);
+ static uint typeForIdentifier(const QString &identifier);
+
+ Type type;
+ ReferencedHandles handle;
+ ChannelPtr channel;
+};
+
+struct TP_QT_NO_EXPORT ContactManager::Roster::BlockedContactsChangedInfo
+{
+ BlockedContactsChangedInfo(const HandleIdentifierMap &added,
+ const HandleIdentifierMap &removed,
+ bool continueIntrospectionWhenFinished = false)
+ : added(added),
+ removed(removed),
+ continueIntrospectionWhenFinished(continueIntrospectionWhenFinished)
+ {
+ }
+
+ HandleIdentifierMap added;
+ HandleIdentifierMap removed;
+ bool continueIntrospectionWhenFinished;
+};
+
+struct TP_QT_NO_EXPORT ContactManager::Roster::UpdateInfo
+{
+ UpdateInfo(const ContactSubscriptionMap &changes, const HandleIdentifierMap &ids,
+ const HandleIdentifierMap &removals)
+ : changes(changes),
+ ids(ids),
+ removals(removals)
+ {
+ }
+
+ ContactSubscriptionMap changes;
+ HandleIdentifierMap ids;
+ HandleIdentifierMap removals;
+};
+
+struct TP_QT_NO_EXPORT ContactManager::Roster::GroupsUpdateInfo
+{
+ GroupsUpdateInfo(const UIntList &contacts,
+ const QStringList &groupsAdded, const QStringList &groupsRemoved)
+ : contacts(contacts),
+ groupsAdded(groupsAdded),
+ groupsRemoved(groupsRemoved)
+ {
+ }
+
+ UIntList contacts;
+ QStringList groupsAdded;
+ QStringList groupsRemoved;
+};
+
+struct TP_QT_NO_EXPORT ContactManager::Roster::GroupRenamedInfo
+{
+ GroupRenamedInfo(const QString &oldName, const QString &newName)
+ : oldName(oldName),
+ newName(newName)
+ {
+ }
+
+ QString oldName;
+ QString newName;
+};
+
+class TP_QT_NO_EXPORT ContactManager::Roster::ModifyFinishOp : public PendingOperation
+{
+ Q_OBJECT
+
+public:
+ ModifyFinishOp(const ConnectionPtr &conn);
+ ~ModifyFinishOp() {};
+
+ void setError(const QString &errorName, const QString &errorMessage);
+ void finish();
+
+private:
+ QString errorName, errorMessage;
+};
+
+class TP_QT_NO_EXPORT ContactManager::Roster::RemoveGroupOp : public PendingOperation
+{
+ Q_OBJECT
+
+public:
+ RemoveGroupOp(const ChannelPtr &channel);
+ ~RemoveGroupOp() {};
+
+private Q_SLOTS:
+ void onContactsRemoved(Tp::PendingOperation *);
+ void onChannelClosed(Tp::PendingOperation *);
+};
+
+class TP_QT_NO_EXPORT ContactManager::PendingRefreshContactInfo : public PendingOperation
+{
+ Q_OBJECT
+
+public:
+ PendingRefreshContactInfo(const ConnectionPtr &conn);
+ ~PendingRefreshContactInfo();
+
+ void addContact(Contact *contact);
+
+ void refreshInfo();
+
+private Q_SLOTS:
+ void onRefreshInfoFinished(Tp::PendingOperation *op);
+
+private:
+ ConnectionPtr mConn;
+ QSet<uint> mToRequest;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/contact-manager-roster.cpp b/TelepathyQt/contact-manager-roster.cpp
new file mode 100644
index 00000000..36fd78c3
--- /dev/null
+++ b/TelepathyQt/contact-manager-roster.cpp
@@ -0,0 +1,2217 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 "TelepathyQt/contact-manager-internal.h"
+
+#include "TelepathyQt/_gen/contact-manager-internal.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/Connection>
+#include <TelepathyQt/ConnectionLowlevel>
+#include <TelepathyQt/ContactFactory>
+#include <TelepathyQt/PendingChannel>
+#include <TelepathyQt/PendingContacts>
+#include <TelepathyQt/PendingFailure>
+#include <TelepathyQt/PendingHandles>
+#include <TelepathyQt/PendingVariant>
+#include <TelepathyQt/PendingVariantMap>
+#include <TelepathyQt/PendingReady>
+#include <TelepathyQt/ReferencedHandles>
+
+namespace Tp
+{
+
+ContactManager::Roster::Roster(ContactManager *contactManager)
+ : QObject(),
+ contactManager(contactManager),
+ usingFallbackContactList(false),
+ hasContactBlockingInterface(false),
+ introspectPendingOp(0),
+ introspectGroupsPendingOp(0),
+ pendingContactListState((uint) -1),
+ contactListState((uint) -1),
+ canReportAbusive(false),
+ gotContactBlockingInitialBlockedContacts(false),
+ canChangeContactList(false),
+ contactListRequestUsesMessage(false),
+ gotContactListInitialContacts(false),
+ gotContactListContactsChangedWithId(false),
+ groupsReintrospectionRequired(false),
+ contactListGroupPropertiesReceived(false),
+ processingContactListChanges(false),
+ contactListChannelsReady(0),
+ featureContactListGroupsTodo(0),
+ groupsSetSuccess(false)
+{
+}
+
+ContactManager::Roster::~Roster()
+{
+}
+
+ContactListState ContactManager::Roster::state() const
+{
+ return (Tp::ContactListState) contactListState;
+}
+
+PendingOperation *ContactManager::Roster::introspect()
+{
+ ConnectionPtr conn(contactManager->connection());
+
+ if (conn->hasInterface(TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_LIST)) {
+ debug() << "Connection.ContactList found, using it";
+
+ usingFallbackContactList = false;
+
+ if (conn->hasInterface(TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_BLOCKING)) {
+ debug() << "Connection.ContactBlocking found. using it";
+ hasContactBlockingInterface = true;
+ introspectContactBlocking();
+ } else {
+ debug() << "Connection.ContactBlocking not found, falling back "
+ "to contact list deny channel";
+
+ debug() << "Requesting handle for deny channel";
+
+ contactListChannels.insert(ChannelInfo::TypeDeny,
+ ChannelInfo(ChannelInfo::TypeDeny));
+
+ PendingHandles *ph = conn->lowlevel()->requestHandles(HandleTypeList,
+ QStringList() << ChannelInfo::identifierForType(
+ ChannelInfo::TypeDeny));
+ connect(ph,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(gotContactListChannelHandle(Tp::PendingOperation*)));
+ }
+ } else {
+ debug() << "Connection.ContactList not found, falling back to contact list channels";
+
+ usingFallbackContactList = true;
+
+ for (uint i = 0; i < ChannelInfo::LastType; ++i) {
+ QString channelId = ChannelInfo::identifierForType(
+ (ChannelInfo::Type) i);
+
+ debug() << "Requesting handle for" << channelId << "channel";
+
+ contactListChannels.insert(i,
+ ChannelInfo((ChannelInfo::Type) i));
+
+ PendingHandles *ph = conn->lowlevel()->requestHandles(HandleTypeList,
+ QStringList() << channelId);
+ connect(ph,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(gotContactListChannelHandle(Tp::PendingOperation*)));
+ }
+ }
+
+ Q_ASSERT(!introspectPendingOp);
+ introspectPendingOp = new PendingOperation(conn);
+ return introspectPendingOp;
+}
+
+PendingOperation *ContactManager::Roster::introspectGroups()
+{
+ ConnectionPtr conn(contactManager->connection());
+
+ Q_ASSERT(!introspectGroupsPendingOp);
+
+ if (conn->hasInterface(TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_LIST)) {
+ if (!conn->hasInterface(TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_GROUPS)) {
+ return new PendingFailure(TP_QT_ERROR_NOT_IMPLEMENTED,
+ QLatin1String("Roster groups not supported"), conn);
+ }
+
+ debug() << "Connection.ContactGroups found, using it";
+
+ if (!gotContactListInitialContacts) {
+ debug() << "Initial ContactList contacts not retrieved. Postponing introspection";
+ groupsReintrospectionRequired = true;
+ return new PendingSuccess(conn);
+ }
+
+ Client::ConnectionInterfaceContactGroupsInterface *iface =
+ conn->interface<Client::ConnectionInterfaceContactGroupsInterface>();
+
+ connect(iface,
+ SIGNAL(GroupsChanged(Tp::UIntList,QStringList,QStringList)),
+ SLOT(onContactListGroupsChanged(Tp::UIntList,QStringList,QStringList)));
+ connect(iface,
+ SIGNAL(GroupsCreated(QStringList)),
+ SLOT(onContactListGroupsCreated(QStringList)));
+ connect(iface,
+ SIGNAL(GroupRenamed(QString,QString)),
+ SLOT(onContactListGroupRenamed(QString,QString)));
+ connect(iface,
+ SIGNAL(GroupsRemoved(QStringList)),
+ SLOT(onContactListGroupsRemoved(QStringList)));
+
+ PendingVariantMap *pvm = iface->requestAllProperties();
+ connect(pvm,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(gotContactListGroupsProperties(Tp::PendingOperation*)));
+ } else {
+ debug() << "Connection.ContactGroups not found, falling back to contact list group channels";
+
+ ++featureContactListGroupsTodo; // decremented in gotChannels
+
+ // we already checked if requests interface exists, so bypass requests
+ // interface checking
+ Client::ConnectionInterfaceRequestsInterface *iface =
+ conn->interface<Client::ConnectionInterfaceRequestsInterface>();
+
+ debug() << "Connecting to Requests.NewChannels";
+ connect(iface,
+ SIGNAL(NewChannels(Tp::ChannelDetailsList)),
+ SLOT(onNewChannels(Tp::ChannelDetailsList)));
+
+ debug() << "Retrieving channels";
+ Client::DBus::PropertiesInterface *properties =
+ contactManager->connection()->interface<Client::DBus::PropertiesInterface>();
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+ properties->Get(
+ QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_REQUESTS),
+ QLatin1String("Channels")), this);
+ connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotChannels(QDBusPendingCallWatcher*)));
+ }
+
+ if (groupsReintrospectionRequired) {
+ return NULL;
+ }
+
+ Q_ASSERT(!introspectGroupsPendingOp);
+ introspectGroupsPendingOp = new PendingOperation(conn);
+ return introspectGroupsPendingOp;
+}
+
+void ContactManager::Roster::reset()
+{
+ contactListChannels.clear();
+ subscribeChannel.reset();
+ publishChannel.reset();
+ storedChannel.reset();
+ denyChannel.reset();
+ contactListGroupChannels.clear();
+ removedContactListGroupChannels.clear();
+}
+
+Contacts ContactManager::Roster::allKnownContacts() const
+{
+ return cachedAllKnownContacts;
+}
+
+QStringList ContactManager::Roster::allKnownGroups() const
+{
+ if (usingFallbackContactList) {
+ return contactListGroupChannels.keys();
+ }
+
+ return cachedAllKnownGroups.toList();
+}
+
+PendingOperation *ContactManager::Roster::addGroup(const QString &group)
+{
+ ConnectionPtr conn(contactManager->connection());
+
+ if (usingFallbackContactList) {
+ QVariantMap request;
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType"),
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_CONTACT_LIST));
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType"),
+ (uint) Tp::HandleTypeGroup);
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetID"),
+ group);
+ return conn->lowlevel()->ensureChannel(request);
+ }
+
+ if (!conn->hasInterface(TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_GROUPS)) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("Not implemented"),
+ conn);
+ }
+
+ Client::ConnectionInterfaceContactGroupsInterface *iface =
+ conn->interface<Client::ConnectionInterfaceContactGroupsInterface>();
+ Q_ASSERT(iface);
+ return queuedFinishVoid(iface->AddToGroup(group, UIntList()));
+}
+
+PendingOperation *ContactManager::Roster::removeGroup(const QString &group)
+{
+ ConnectionPtr conn(contactManager->connection());
+
+ if (usingFallbackContactList) {
+ if (!contactListGroupChannels.contains(group)) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT),
+ QLatin1String("Invalid group"),
+ conn);
+ }
+
+ ChannelPtr channel = contactListGroupChannels[group];
+ return new RemoveGroupOp(channel);
+ }
+
+ if (!conn->hasInterface(TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_GROUPS)) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("Not implemented"),
+ conn);
+ }
+
+ Client::ConnectionInterfaceContactGroupsInterface *iface =
+ conn->interface<Client::ConnectionInterfaceContactGroupsInterface>();
+ Q_ASSERT(iface);
+ return queuedFinishVoid(iface->RemoveGroup(group));
+}
+
+Contacts ContactManager::Roster::groupContacts(const QString &group) const
+{
+ if (usingFallbackContactList) {
+ if (!contactListGroupChannels.contains(group)) {
+ return Contacts();
+ }
+
+ ChannelPtr channel = contactListGroupChannels[group];
+ return channel->groupContacts();
+ }
+
+ Contacts ret;
+ foreach (const ContactPtr &contact, allKnownContacts()) {
+ if (contact->groups().contains(group))
+ ret << contact;
+ }
+ return ret;
+}
+
+PendingOperation *ContactManager::Roster::addContactsToGroup(const QString &group,
+ const QList<ContactPtr> &contacts)
+{
+ ConnectionPtr conn(contactManager->connection());
+
+ if (usingFallbackContactList) {
+ if (!contactListGroupChannels.contains(group)) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT),
+ QLatin1String("Invalid group"),
+ conn);
+ }
+
+ ChannelPtr channel = contactListGroupChannels[group];
+ return channel->groupAddContacts(contacts);
+ }
+
+ if (!conn->hasInterface(TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_GROUPS)) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("Not implemented"),
+ conn);
+ }
+
+ UIntList handles;
+ foreach (const ContactPtr &contact, contacts) {
+ handles << contact->handle()[0];
+ }
+
+ Client::ConnectionInterfaceContactGroupsInterface *iface =
+ conn->interface<Client::ConnectionInterfaceContactGroupsInterface>();
+ Q_ASSERT(iface);
+ return queuedFinishVoid(iface->AddToGroup(group, handles));
+}
+
+PendingOperation *ContactManager::Roster::removeContactsFromGroup(const QString &group,
+ const QList<ContactPtr> &contacts)
+{
+ ConnectionPtr conn(contactManager->connection());
+
+ if (usingFallbackContactList) {
+ if (!contactListGroupChannels.contains(group)) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT),
+ QLatin1String("Invalid group"),
+ conn);
+ }
+
+ ChannelPtr channel = contactListGroupChannels[group];
+ return channel->groupRemoveContacts(contacts);
+ }
+
+ if (!conn->hasInterface(TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_GROUPS)) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("Not implemented"),
+ conn);
+ }
+
+ UIntList handles;
+ foreach (const ContactPtr &contact, contacts) {
+ handles << contact->handle()[0];
+ }
+
+ Client::ConnectionInterfaceContactGroupsInterface *iface =
+ conn->interface<Client::ConnectionInterfaceContactGroupsInterface>();
+ Q_ASSERT(iface);
+ return queuedFinishVoid(iface->RemoveFromGroup(group, handles));
+}
+
+bool ContactManager::Roster::canRequestPresenceSubscription() const
+{
+ if (usingFallbackContactList) {
+ return subscribeChannel && subscribeChannel->groupCanAddContacts();
+ }
+
+ return canChangeContactList;
+}
+
+bool ContactManager::Roster::subscriptionRequestHasMessage() const
+{
+ if (usingFallbackContactList) {
+ return subscribeChannel &&
+ (subscribeChannel->groupFlags() & ChannelGroupFlagMessageAdd);
+ }
+
+ return contactListRequestUsesMessage;
+}
+
+PendingOperation *ContactManager::Roster::requestPresenceSubscription(
+ const QList<ContactPtr> &contacts, const QString &message)
+{
+ ConnectionPtr conn(contactManager->connection());
+
+ if (usingFallbackContactList) {
+ if (!subscribeChannel) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("Cannot subscribe to contacts' presence on this protocol"),
+ conn);
+ }
+
+ return subscribeChannel->groupAddContacts(contacts, message);
+ }
+
+ UIntList handles;
+ foreach (const ContactPtr &contact, contacts) {
+ handles << contact->handle()[0];
+ }
+
+ Client::ConnectionInterfaceContactListInterface *iface =
+ conn->interface<Client::ConnectionInterfaceContactListInterface>();
+ Q_ASSERT(iface);
+ return queuedFinishVoid(iface->RequestSubscription(handles, message));
+}
+
+bool ContactManager::Roster::canRemovePresenceSubscription() const
+{
+ if (usingFallbackContactList) {
+ return subscribeChannel && subscribeChannel->groupCanRemoveContacts();
+ }
+
+ return canChangeContactList;
+}
+
+bool ContactManager::Roster::subscriptionRemovalHasMessage() const
+{
+ if (usingFallbackContactList) {
+ return subscribeChannel &&
+ (subscribeChannel->groupFlags() & ChannelGroupFlagMessageRemove);
+ }
+
+ return false;
+}
+
+bool ContactManager::Roster::canRescindPresenceSubscriptionRequest() const
+{
+ if (usingFallbackContactList) {
+ return subscribeChannel && subscribeChannel->groupCanRescindContacts();
+ }
+
+ return canChangeContactList;
+}
+
+bool ContactManager::Roster::subscriptionRescindingHasMessage() const
+{
+ if (usingFallbackContactList) {
+ return subscribeChannel &&
+ (subscribeChannel->groupFlags() & ChannelGroupFlagMessageRescind);
+ }
+
+ return false;
+}
+
+PendingOperation *ContactManager::Roster::removePresenceSubscription(
+ const QList<ContactPtr> &contacts, const QString &message)
+{
+ ConnectionPtr conn(contactManager->connection());
+
+ if (usingFallbackContactList) {
+ if (!subscribeChannel) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("Cannot subscribe to contacts' presence on this protocol"),
+ conn);
+ }
+
+ return subscribeChannel->groupRemoveContacts(contacts, message);
+ }
+
+ UIntList handles;
+ foreach (const ContactPtr &contact, contacts) {
+ handles << contact->handle()[0];
+ }
+
+ Client::ConnectionInterfaceContactListInterface *iface =
+ conn->interface<Client::ConnectionInterfaceContactListInterface>();
+ Q_ASSERT(iface);
+ return queuedFinishVoid(iface->Unsubscribe(handles));
+}
+
+bool ContactManager::Roster::canAuthorizePresencePublication() const
+{
+ if (usingFallbackContactList) {
+ // do not check for Channel::groupCanAddContacts as all contacts in local
+ // pending can be added, even if the Channel::groupFlags() does not contain
+ // the flag CanAdd
+ return (bool) publishChannel;
+ }
+
+ return canChangeContactList;
+}
+
+bool ContactManager::Roster::publicationAuthorizationHasMessage() const
+{
+ if (usingFallbackContactList) {
+ return subscribeChannel &&
+ (subscribeChannel->groupFlags() & ChannelGroupFlagMessageAccept);
+ }
+
+ return false;
+}
+
+PendingOperation *ContactManager::Roster::authorizePresencePublication(
+ const QList<ContactPtr> &contacts, const QString &message)
+{
+ ConnectionPtr conn(contactManager->connection());
+
+ if (usingFallbackContactList) {
+ if (!publishChannel) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("Cannot control publication of presence on this protocol"),
+ conn);
+ }
+
+ return publishChannel->groupAddContacts(contacts, message);
+ }
+
+ UIntList handles;
+ foreach (const ContactPtr &contact, contacts) {
+ handles << contact->handle()[0];
+ }
+
+ Client::ConnectionInterfaceContactListInterface *iface =
+ conn->interface<Client::ConnectionInterfaceContactListInterface>();
+ Q_ASSERT(iface);
+ return queuedFinishVoid(iface->AuthorizePublication(handles));
+}
+
+bool ContactManager::Roster::publicationRejectionHasMessage() const
+{
+ if (usingFallbackContactList) {
+ return subscribeChannel &&
+ (subscribeChannel->groupFlags() & ChannelGroupFlagMessageReject);
+ }
+
+ return false;
+}
+
+bool ContactManager::Roster::canRemovePresencePublication() const
+{
+ if (usingFallbackContactList) {
+ return publishChannel && publishChannel->groupCanRemoveContacts();
+ }
+
+ return canChangeContactList;
+}
+
+bool ContactManager::Roster::publicationRemovalHasMessage() const
+{
+ if (usingFallbackContactList) {
+ return subscribeChannel &&
+ (subscribeChannel->groupFlags() & ChannelGroupFlagMessageRemove);
+ }
+
+ return false;
+}
+
+PendingOperation *ContactManager::Roster::removePresencePublication(
+ const QList<ContactPtr> &contacts, const QString &message)
+{
+ ConnectionPtr conn(contactManager->connection());
+
+ if (usingFallbackContactList) {
+ if (!publishChannel) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("Cannot control publication of presence on this protocol"),
+ conn);
+ }
+
+ return publishChannel->groupRemoveContacts(contacts, message);
+ }
+
+ UIntList handles;
+ foreach (const ContactPtr &contact, contacts) {
+ handles << contact->handle()[0];
+ }
+
+ Client::ConnectionInterfaceContactListInterface *iface =
+ conn->interface<Client::ConnectionInterfaceContactListInterface>();
+ Q_ASSERT(iface);
+ return queuedFinishVoid(iface->Unpublish(handles));
+}
+
+PendingOperation *ContactManager::Roster::removeContacts(
+ const QList<ContactPtr> &contacts, const QString &message)
+{
+ ConnectionPtr conn(contactManager->connection());
+
+ if (usingFallbackContactList) {
+ /* If the CM implements stored channel correctly, it should have the
+ * wanted behaviour. Otherwise we have to to remove from publish
+ * and subscribe channels.
+ */
+
+ if (storedChannel && storedChannel->groupCanRemoveContacts()) {
+ debug() << "Removing contacts from stored list";
+ return storedChannel->groupRemoveContacts(contacts, message);
+ }
+
+ QList<PendingOperation*> operations;
+
+ if (canRemovePresenceSubscription()) {
+ debug() << "Removing contacts from subscribe list";
+ operations << removePresenceSubscription(contacts, message);
+ }
+
+ if (canRemovePresencePublication()) {
+ debug() << "Removing contacts from publish list";
+ operations << removePresencePublication(contacts, message);
+ }
+
+ if (operations.isEmpty()) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("Cannot remove contacts on this protocol"),
+ conn);
+ }
+
+ return new PendingComposite(operations, conn);
+ }
+
+ UIntList handles;
+ foreach (const ContactPtr &contact, contacts) {
+ handles << contact->handle()[0];
+ }
+
+ Client::ConnectionInterfaceContactListInterface *iface =
+ conn->interface<Client::ConnectionInterfaceContactListInterface>();
+ Q_ASSERT(iface);
+ return queuedFinishVoid(iface->RemoveContacts(handles));
+}
+
+bool ContactManager::Roster::canBlockContacts() const
+{
+ if (!usingFallbackContactList && hasContactBlockingInterface) {
+ return true;
+ } else {
+ return (bool) denyChannel;
+ }
+}
+
+bool ContactManager::Roster::canReportAbuse() const
+{
+ return canReportAbusive;
+}
+
+PendingOperation *ContactManager::Roster::blockContacts(
+ const QList<ContactPtr> &contacts, bool value, bool reportAbuse)
+{
+ if (!contactManager->connection()->isValid()) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Connection is invalid"),
+ contactManager->connection());
+ } else if (!contactManager->connection()->isReady(Connection::FeatureRoster)) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Connection::FeatureRoster is not ready"),
+ contactManager->connection());
+ }
+
+ if (!usingFallbackContactList && hasContactBlockingInterface) {
+ ConnectionPtr conn(contactManager->connection());
+ Client::ConnectionInterfaceContactBlockingInterface *iface =
+ conn->interface<Client::ConnectionInterfaceContactBlockingInterface>();
+
+ UIntList handles;
+ foreach (const ContactPtr &contact, contacts) {
+ handles << contact->handle()[0];
+ }
+
+ Q_ASSERT(iface);
+ if(value) {
+ return queuedFinishVoid(iface->BlockContacts(handles, reportAbuse));
+ } else {
+ return queuedFinishVoid(iface->UnblockContacts(handles));
+ }
+
+ } else {
+ ConnectionPtr conn(contactManager->connection());
+
+ if (!denyChannel) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("Cannot block contacts on this protocol"),
+ conn);
+ }
+
+ if (value) {
+ return denyChannel->groupAddContacts(contacts);
+ } else {
+ return denyChannel->groupRemoveContacts(contacts);
+ }
+ }
+}
+
+void ContactManager::Roster::gotContactBlockingCapabilities(PendingOperation *op)
+{
+ if (op->isError()) {
+ warning() << "Getting ContactBlockingCapabilities property failed with" <<
+ op->errorName() << ":" << op->errorMessage();
+ introspectContactList();
+ return;
+ }
+
+ debug() << "Got ContactBlockingCapabilities property";
+
+ PendingVariant *pv = qobject_cast<PendingVariant*>(op);
+
+ uint contactBlockingCaps = pv->result().toUInt();
+ canReportAbusive =
+ contactBlockingCaps & ContactBlockingCapabilityCanReportAbusive;
+
+ introspectContactBlockingBlockedContacts();
+}
+
+void ContactManager::Roster::gotContactBlockingBlockedContacts(
+ QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<HandleIdentifierMap> reply = *watcher;
+
+ if (watcher->isError()) {
+ warning() << "Getting initial ContactBlocking blocked "
+ "contacts failed with" <<
+ watcher->error().name() << ":" << watcher->error().message();
+ introspectContactList();
+ return;
+ }
+
+ debug() << "Got initial ContactBlocking blocked contacts";
+
+ gotContactBlockingInitialBlockedContacts = true;
+
+ ConnectionPtr conn(contactManager->connection());
+ HandleIdentifierMap contactIds = reply.value();
+
+ if (!contactIds.isEmpty()) {
+ conn->lowlevel()->injectContactIds(contactIds);
+
+ //fake change event where all the contacts are added
+ contactListBlockedContactsChangedQueue.enqueue(
+ BlockedContactsChangedInfo(contactIds, HandleIdentifierMap(), true));
+ contactListChangesQueue.enqueue(
+ &ContactManager::Roster::processContactListBlockedContactsChanged);
+ processContactListChanges();
+ } else {
+ introspectContactList();
+ }
+}
+
+void ContactManager::Roster::onContactBlockingBlockedContactsChanged(
+ const HandleIdentifierMap &added,
+ const HandleIdentifierMap &removed)
+{
+ if (!gotContactBlockingInitialBlockedContacts) {
+ return;
+ }
+
+ ConnectionPtr conn(contactManager->connection());
+ conn->lowlevel()->injectContactIds(added);
+ conn->lowlevel()->injectContactIds(removed);
+
+ contactListBlockedContactsChangedQueue.enqueue(
+ BlockedContactsChangedInfo(added, removed));
+ contactListChangesQueue.enqueue(
+ &ContactManager::Roster::processContactListBlockedContactsChanged);
+ processContactListChanges();
+}
+
+void ContactManager::Roster::gotContactListProperties(PendingOperation *op)
+{
+ if (op->isError()) {
+ // We may have been in state Failure and then Success, and FeatureRoster is already ready
+ if (introspectPendingOp) {
+ introspectPendingOp->setFinishedWithError(
+ op->errorName(), op->errorMessage());
+ introspectPendingOp = 0;
+ }
+ return;
+ }
+
+ debug() << "Got ContactList properties";
+
+ PendingVariantMap *pvm = qobject_cast<PendingVariantMap*>(op);
+
+ QVariantMap props = pvm->result();
+
+ canChangeContactList = qdbus_cast<uint>(props[QLatin1String("CanChangeContactList")]);
+ contactListRequestUsesMessage = qdbus_cast<uint>(props[QLatin1String("RequestUsesMessage")]);
+
+ // only update the status if we did not get it from ContactListStateChanged
+ if (pendingContactListState == (uint) -1) {
+ uint state = qdbus_cast<uint>(props[QLatin1String("ContactListState")]);
+ onContactListStateChanged(state);
+ }
+}
+
+void ContactManager::Roster::gotContactListContacts(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<ContactAttributesMap> reply = *watcher;
+
+ if (watcher->isError()) {
+ warning() << "Failed introspecting ContactList contacts";
+
+ contactListState = ContactListStateFailure;
+ debug() << "Setting state to failure";
+ emit contactManager->stateChanged((Tp::ContactListState) contactListState);
+
+ // We may have been in state Failure and then Success, and FeatureRoster is already ready
+ if (introspectPendingOp) {
+ introspectPendingOp->setFinishedWithError(
+ reply.error());
+ introspectPendingOp = 0;
+ }
+ return;
+ }
+
+ debug() << "Got initial ContactList contacts";
+
+ gotContactListInitialContacts = true;
+
+ ConnectionPtr conn(contactManager->connection());
+ ContactAttributesMap attrsMap = reply.value();
+ ContactAttributesMap::const_iterator begin = attrsMap.constBegin();
+ ContactAttributesMap::const_iterator end = attrsMap.constEnd();
+ for (ContactAttributesMap::const_iterator i = begin; i != end; ++i) {
+ uint bareHandle = i.key();
+ QVariantMap attrs = i.value();
+
+ ContactPtr contact = contactManager->ensureContact(ReferencedHandles(conn,
+ HandleTypeContact, UIntList() << bareHandle),
+ conn->contactFactory()->features(), attrs);
+ cachedAllKnownContacts.insert(contact);
+ contactListContacts.insert(contact);
+ }
+
+ if (contactManager->connection()->requestedFeatures().contains(
+ Connection::FeatureRosterGroups)) {
+ groupsSetSuccess = true;
+ }
+
+ // We may have been in state Failure and then Success, and FeatureRoster is already ready
+ // In any case, if we're going to reintrospect Groups, we only advance to state success once
+ // that is finished. We connect to the op finishing already here to catch all the failure finish
+ // cases as well.
+ if (introspectPendingOp) {
+ if (!groupsSetSuccess) {
+ // Will emit stateChanged() signal when the op is finished in idle
+ // callback. This is to ensure FeatureRoster (and Groups) is marked ready.
+ connect(introspectPendingOp,
+ SIGNAL(finished(Tp::PendingOperation *)),
+ SLOT(setStateSuccess()));
+ }
+
+ introspectPendingOp->setFinished();
+ introspectPendingOp = 0;
+ } else if (!groupsSetSuccess) {
+ setStateSuccess();
+ } else {
+ // Verify that Groups is actually going to set the state
+ // As far as I can see, this will always be the case.
+ Q_ASSERT(groupsReintrospectionRequired);
+ }
+
+ if (groupsReintrospectionRequired) {
+ introspectGroups();
+ }
+}
+
+void ContactManager::Roster::setStateSuccess()
+{
+ if (contactManager->connection()->isValid()) {
+ debug() << "State is now success";
+ contactListState = ContactListStateSuccess;
+ emit contactManager->stateChanged((Tp::ContactListState) contactListState);
+ }
+}
+
+void ContactManager::Roster::onContactListStateChanged(uint state)
+{
+ if (pendingContactListState == state) {
+ // ignore redundant state changes
+ return;
+ }
+
+ pendingContactListState = state;
+
+ if (state == ContactListStateSuccess) {
+ introspectContactListContacts();
+ return;
+ }
+
+ contactListState = state;
+
+ if (state == ContactListStateFailure) {
+ debug() << "State changed to failure, finishing roster introspection";
+ }
+
+ emit contactManager->stateChanged((Tp::ContactListState) state);
+
+ if (state == ContactListStateFailure) {
+ // Consider it done here as the state may go from Failure to Success afterwards, in which
+ // case the contacts will appear.
+ Q_ASSERT(introspectPendingOp);
+ introspectPendingOp->setFinished();
+ introspectPendingOp = 0;
+ }
+}
+
+void ContactManager::Roster::onContactListContactsChangedWithId(const Tp::ContactSubscriptionMap &changes,
+ const Tp::HandleIdentifierMap &ids, const Tp::HandleIdentifierMap &removals)
+{
+ debug() << "Got ContactList.ContactsChangedWithID with" << changes.size() <<
+ "changes and" << removals.size() << "removals";
+
+ gotContactListContactsChangedWithId = true;
+
+ if (!gotContactListInitialContacts) {
+ debug() << "Ignoring ContactList changes until initial contacts are retrieved";
+ return;
+ }
+
+ ConnectionPtr conn(contactManager->connection());
+ conn->lowlevel()->injectContactIds(ids);
+
+ contactListUpdatesQueue.enqueue(UpdateInfo(changes, ids, removals));
+ contactListChangesQueue.enqueue(&ContactManager::Roster::processContactListUpdates);
+ processContactListChanges();
+}
+
+void ContactManager::Roster::onContactListContactsChanged(const Tp::ContactSubscriptionMap &changes,
+ const Tp::UIntList &removals)
+{
+ if (gotContactListContactsChangedWithId) {
+ return;
+ }
+
+ debug() << "Got ContactList.ContactsChanged with" << changes.size() <<
+ "changes and" << removals.size() << "removals";
+
+ if (!gotContactListInitialContacts) {
+ debug() << "Ignoring ContactList changes until initial contacts are retrieved";
+ return;
+ }
+
+ HandleIdentifierMap removalsMap;
+ foreach (uint handle, removals) {
+ removalsMap.insert(handle, QString());
+ }
+
+ contactListUpdatesQueue.enqueue(UpdateInfo(changes, HandleIdentifierMap(), removalsMap));
+ contactListChangesQueue.enqueue(&ContactManager::Roster::processContactListUpdates);
+ processContactListChanges();
+}
+
+void ContactManager::Roster::onContactListBlockedContactsConstructed(Tp::PendingOperation *op)
+{
+ BlockedContactsChangedInfo info = contactListBlockedContactsChangedQueue.dequeue();
+
+ if (op->isError()) {
+ if (info.continueIntrospectionWhenFinished) {
+ introspectContactList();
+ }
+ processingContactListChanges = false;
+ processContactListChanges();
+ return;
+ }
+
+ Contacts newBlockedContacts;
+ Contacts unblockedContacts;
+
+ HandleIdentifierMap::const_iterator begin = info.added.constBegin();
+ HandleIdentifierMap::const_iterator end = info.added.constEnd();
+ for (HandleIdentifierMap::const_iterator i = begin; i != end; ++i) {
+ uint bareHandle = i.key();
+
+ ContactPtr contact = contactManager->lookupContactByHandle(bareHandle);
+ if (!contact) {
+ warning() << "Unable to construct contact for handle" << bareHandle;
+ continue;
+ }
+
+ debug() << "Contact" << contact->id() << "is now blocked";
+ blockedContacts.insert(contact);
+ newBlockedContacts.insert(contact);
+ contact->setBlocked(true);
+ }
+
+ begin = info.removed.constBegin();
+ end = info.removed.constEnd();
+ for (HandleIdentifierMap::const_iterator i = begin; i != end; ++i) {
+ uint bareHandle = i.key();
+
+ ContactPtr contact = contactManager->lookupContactByHandle(bareHandle);
+ if (!contact) {
+ warning() << "Unable to construct contact for handle" << bareHandle;
+ continue;
+ }
+
+ debug() << "Contact" << contact->id() << "is now unblocked";
+ blockedContacts.remove(contact);
+ unblockedContacts.insert(contact);
+ contact->setBlocked(false);
+ }
+
+ // Perform the needed computation for allKnownContactsChanged
+ computeKnownContactsChanges(newBlockedContacts, Contacts(),
+ Contacts(), unblockedContacts, Channel::GroupMemberChangeDetails());
+
+ if (info.continueIntrospectionWhenFinished) {
+ introspectContactList();
+ }
+
+ processingContactListChanges = false;
+ processContactListChanges();
+}
+
+void ContactManager::Roster::onContactListNewContactsConstructed(Tp::PendingOperation *op)
+{
+ if (op->isError()) {
+ contactListUpdatesQueue.dequeue();
+ processingContactListChanges = false;
+ processContactListChanges();
+ return;
+ }
+
+ UpdateInfo info = contactListUpdatesQueue.dequeue();
+
+ Tp::Contacts added;
+ Tp::Contacts removed;
+
+ Tp::Contacts publishRequested;
+
+ ContactSubscriptionMap::const_iterator begin = info.changes.constBegin();
+ ContactSubscriptionMap::const_iterator end = info.changes.constEnd();
+ for (ContactSubscriptionMap::const_iterator i = begin; i != end; ++i) {
+ uint bareHandle = i.key();
+ ContactSubscriptions subscriptions = i.value();
+
+ ContactPtr contact = contactManager->lookupContactByHandle(bareHandle);
+ if (!contact) {
+ warning() << "Unable to construct contact for handle" << bareHandle;
+ continue;
+ }
+
+ contactListContacts.insert(contact);
+ added << contact;
+
+ Contact::PresenceState oldContactPublishState = contact->publishState();
+ QString oldContactPublishStateMessage = contact->publishStateMessage();
+ contact->setSubscriptionState((SubscriptionState) subscriptions.subscribe);
+ contact->setPublishState((SubscriptionState) subscriptions.publish,
+ subscriptions.publishRequest);
+ if (subscriptions.publish == SubscriptionStateAsk &&
+ (oldContactPublishState != Contact::PresenceStateAsk ||
+ oldContactPublishStateMessage != contact->publishStateMessage())) {
+ Channel::GroupMemberChangeDetails publishRequestDetails;
+ QVariantMap detailsMap;
+ detailsMap.insert(QLatin1String("message"), subscriptions.publishRequest);
+ publishRequestDetails = Channel::GroupMemberChangeDetails(ContactPtr(), detailsMap);
+ // FIXME (API/ABI break) remove both of these signals
+ emit contactManager->presencePublicationRequested(Contacts() << contact,
+ publishRequestDetails);
+ emit contactManager->presencePublicationRequested(Contacts() << contact,
+ subscriptions.publishRequest);
+
+ publishRequested.insert(contact);
+ }
+ }
+
+ if (!publishRequested.empty()) {
+ emit contactManager->presencePublicationRequested(publishRequested);
+ }
+
+ foreach (uint bareHandle, info.removals.keys()) {
+ ContactPtr contact = contactManager->lookupContactByHandle(bareHandle);
+ if (!contact) {
+ warning() << "Unable to find removed contact with handle" << bareHandle;
+ continue;
+ }
+
+ if (!contactListContacts.contains(contact)) {
+ warning() << "Contact" << contact->id() << "removed from ContactList "
+ "but it wasn't present, ignoring.";
+ continue;
+ }
+
+ contactListContacts.remove(contact);
+ removed << contact;
+ }
+
+ computeKnownContactsChanges(added, Contacts(), Contacts(),
+ removed, Channel::GroupMemberChangeDetails());
+
+ foreach (const Tp::ContactPtr &contact, removed) {
+ contact->setSubscriptionState(SubscriptionStateNo);
+ contact->setPublishState(SubscriptionStateNo);
+ }
+
+ processingContactListChanges = false;
+ processContactListChanges();
+}
+
+void ContactManager::Roster::onContactListGroupsChanged(const Tp::UIntList &contacts,
+ const QStringList &added, const QStringList &removed)
+{
+ Q_ASSERT(usingFallbackContactList == false);
+
+ if (!contactListGroupPropertiesReceived) {
+ return;
+ }
+
+ contactListGroupsUpdatesQueue.enqueue(GroupsUpdateInfo(contacts,
+ added, removed));
+ contactListChangesQueue.enqueue(&ContactManager::Roster::processContactListGroupsUpdates);
+ processContactListChanges();
+}
+
+void ContactManager::Roster::onContactListGroupsCreated(const QStringList &names)
+{
+ Q_ASSERT(usingFallbackContactList == false);
+
+ if (!contactListGroupPropertiesReceived) {
+ return;
+ }
+
+ contactListGroupsCreatedQueue.enqueue(names);
+ contactListChangesQueue.enqueue(&ContactManager::Roster::processContactListGroupsCreated);
+ processContactListChanges();
+}
+
+void ContactManager::Roster::onContactListGroupRenamed(const QString &oldName, const QString &newName)
+{
+ Q_ASSERT(usingFallbackContactList == false);
+
+ if (!contactListGroupPropertiesReceived) {
+ return;
+ }
+
+ contactListGroupRenamedQueue.enqueue(GroupRenamedInfo(oldName, newName));
+ contactListChangesQueue.enqueue(&ContactManager::Roster::processContactListGroupRenamed);
+ processContactListChanges();
+}
+
+void ContactManager::Roster::onContactListGroupsRemoved(const QStringList &names)
+{
+ Q_ASSERT(usingFallbackContactList == false);
+
+ if (!contactListGroupPropertiesReceived) {
+ return;
+ }
+
+ contactListGroupsRemovedQueue.enqueue(names);
+ contactListChangesQueue.enqueue(&ContactManager::Roster::processContactListGroupsRemoved);
+ processContactListChanges();
+}
+
+void ContactManager::Roster::onModifyFinished(Tp::PendingOperation *op)
+{
+ ModifyFinishOp *returned = returnedModifyOps.take(op);
+
+ // Finished twice, or we didn't add the returned op at all?
+ Q_ASSERT(returned);
+
+ if (op->isError()) {
+ returned->setError(op->errorName(), op->errorMessage());
+ }
+
+ modifyFinishQueue.enqueue(returned);
+ contactListChangesQueue.enqueue(&ContactManager::Roster::processFinishedModify);
+ processContactListChanges();
+}
+
+void ContactManager::Roster::gotContactListChannelHandle(PendingOperation *op)
+{
+ PendingHandles *ph = qobject_cast<PendingHandles*>(op);
+ Q_ASSERT(ph->namesRequested().size() == 1);
+ QString channelId = ph->namesRequested().first();
+ uint type = ChannelInfo::typeForIdentifier(channelId);
+
+ if (op->isError()) {
+ // let's not fail, because the contact lists are not supported
+ debug() << "Unable to retrieve handle for" << channelId << "channel, ignoring";
+ contactListChannels.remove(type);
+ onContactListChannelReady();
+ return;
+ }
+
+ if (ph->invalidNames().size() == 1) {
+ // let's not fail, because the contact lists are not supported
+ debug() << "Unable to retrieve handle for" << channelId << "channel, ignoring";
+ contactListChannels.remove(type);
+ onContactListChannelReady();
+ return;
+ }
+
+ Q_ASSERT(ph->handles().size() == 1);
+
+ debug() << "Got handle for" << channelId << "channel";
+
+ if (!usingFallbackContactList) {
+ Q_ASSERT(type == ChannelInfo::TypeDeny);
+ } else {
+ Q_ASSERT(type != (uint) -1 && type < ChannelInfo::LastType);
+ }
+
+ ReferencedHandles handle = ph->handles();
+ contactListChannels[type].handle = handle;
+
+ debug() << "Requesting channel for" << channelId << "channel";
+ QVariantMap request;
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType"),
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_CONTACT_LIST));
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType"),
+ (uint) HandleTypeList);
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandle"),
+ handle[0]);
+ ConnectionPtr conn(contactManager->connection());
+ /* Request the channel passing INT_MAX as timeout (meaning no timeout), as
+ * some CMs may take too long to return from ensureChannel when still
+ * loading the contact list */
+ connect(conn->lowlevel()->ensureChannel(request, INT_MAX),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(gotContactListChannel(Tp::PendingOperation*)));
+}
+
+void ContactManager::Roster::gotContactListChannel(PendingOperation *op)
+{
+ if (op->isError()) {
+ debug() << "Unable to create channel, ignoring";
+ onContactListChannelReady();
+ return;
+ }
+
+ PendingChannel *pc = qobject_cast<PendingChannel*>(op);
+ ChannelPtr channel = pc->channel();
+ Q_ASSERT(channel);
+ uint handle = pc->targetHandle();
+ Q_ASSERT(handle);
+
+ for (uint i = 0; i < ChannelInfo::LastType; ++i) {
+ if (contactListChannels.contains(i) &&
+ contactListChannels[i].handle.size() > 0 &&
+ contactListChannels[i].handle[0] == handle) {
+ Q_ASSERT(!contactListChannels[i].channel);
+ contactListChannels[i].channel = channel;
+
+ // deref connection refcount here as connection will keep a ref to channel and we don't
+ // want a contact list channel keeping a ref of connection, otherwise connection will
+ // leak, thus the channels.
+ channel->connection()->deref();
+
+ connect(channel->becomeReady(),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onContactListChannelReady()));
+ }
+ }
+}
+
+void ContactManager::Roster::onContactListChannelReady()
+{
+ if (!usingFallbackContactList) {
+ setContactListChannelsReady();
+
+ updateContactsBlockState();
+
+ if (denyChannel) {
+ cachedAllKnownContacts.unite(denyChannel->groupContacts());
+ }
+
+ introspectContactList();
+ } else if (++contactListChannelsReady == ChannelInfo::LastType) {
+ if (contactListChannels.isEmpty()) {
+ contactListState = ContactListStateFailure;
+ debug() << "State is failure, roster not supported";
+ emit contactManager->stateChanged((Tp::ContactListState) contactListState);
+
+ Q_ASSERT(introspectPendingOp);
+ introspectPendingOp->setFinishedWithError(TP_QT_ERROR_NOT_IMPLEMENTED,
+ QLatin1String("Roster not supported"));
+ introspectPendingOp = 0;
+ return;
+ }
+
+ setContactListChannelsReady();
+
+ updateContactsBlockState();
+
+ // Refresh the cache for the current known contacts
+ foreach (const ChannelInfo &contactListChannel, contactListChannels) {
+ ChannelPtr channel = contactListChannel.channel;
+ if (!channel) {
+ continue;
+ }
+ cachedAllKnownContacts.unite(channel->groupContacts());
+ cachedAllKnownContacts.unite(channel->groupLocalPendingContacts());
+ cachedAllKnownContacts.unite(channel->groupRemotePendingContacts());
+ }
+
+ updateContactsPresenceState();
+
+ Q_ASSERT(introspectPendingOp);
+
+ if (!contactManager->connection()->requestedFeatures().contains(
+ Connection::FeatureRosterGroups)) {
+ // Will emit stateChanged() signal when the op is finished in idle
+ // callback. This is to ensure FeatureRoster is marked ready.
+ connect(introspectPendingOp,
+ SIGNAL(finished(Tp::PendingOperation *)),
+ SLOT(setStateSuccess()));
+ } else {
+ Q_ASSERT(!groupsSetSuccess);
+ groupsSetSuccess = true;
+ }
+
+ introspectPendingOp->setFinished();
+ introspectPendingOp = 0;
+ }
+}
+
+void ContactManager::Roster::gotContactListGroupsProperties(PendingOperation *op)
+{
+ Q_ASSERT(introspectGroupsPendingOp != NULL);
+
+ if (groupsSetSuccess) {
+ // Connect here, so we catch the following and the other failure cases
+ connect(introspectGroupsPendingOp,
+ SIGNAL(finished(Tp::PendingOperation *)),
+ SLOT(setStateSuccess()));
+ }
+
+ if (op->isError()) {
+ warning() << "Getting contact list groups properties failed:" << op->errorName() << '-'
+ << op->errorMessage();
+
+ introspectGroupsPendingOp->setFinishedWithError(
+ op->errorName(), op->errorMessage());
+ introspectGroupsPendingOp = 0;
+
+ return;
+ }
+
+ debug() << "Got contact list groups properties";
+ PendingVariantMap *pvm = qobject_cast<PendingVariantMap*>(op);
+
+ QVariantMap props = pvm->result();
+
+ cachedAllKnownGroups = qdbus_cast<QStringList>(props[QLatin1String("Groups")]).toSet();
+ contactListGroupPropertiesReceived = true;
+
+ processingContactListChanges = true;
+ PendingContacts *pc = contactManager->upgradeContacts(
+ contactManager->allKnownContacts().toList(),
+ Contact::FeatureRosterGroups);
+ connect(pc,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onContactListContactsUpgraded(Tp::PendingOperation*)));
+}
+
+void ContactManager::Roster::onContactListContactsUpgraded(PendingOperation *op)
+{
+ Q_ASSERT(processingContactListChanges);
+ processingContactListChanges = false;
+
+ Q_ASSERT(introspectGroupsPendingOp != NULL);
+
+ if (op->isError()) {
+ warning() << "Upgrading contacts with group membership failed:" << op->errorName() << '-'
+ << op->errorMessage();
+
+ introspectGroupsPendingOp->setFinishedWithError(
+ op->errorName(), op->errorMessage());
+ introspectGroupsPendingOp = 0;
+ processContactListChanges();
+ return;
+ }
+
+ introspectGroupsPendingOp->setFinished();
+ introspectGroupsPendingOp = 0;
+ processContactListChanges();
+}
+
+void ContactManager::Roster::onNewChannels(const Tp::ChannelDetailsList &channelDetailsList)
+{
+ ConnectionPtr conn(contactManager->connection());
+
+ QString channelType;
+ uint handleType;
+ foreach (const ChannelDetails &channelDetails, channelDetailsList) {
+ channelType = channelDetails.properties.value(
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType")).toString();
+ if (channelType != QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_CONTACT_LIST)) {
+ continue;
+ }
+
+ handleType = channelDetails.properties.value(
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType")).toUInt();
+ if (handleType != Tp::HandleTypeGroup) {
+ continue;
+ }
+
+ ++featureContactListGroupsTodo; // decremented in onContactListGroupChannelReady
+ ChannelPtr channel = Channel::create(conn,
+ channelDetails.channel.path(), channelDetails.properties);
+ pendingContactListGroupChannels.append(channel);
+
+ // deref connection refcount here as connection will keep a ref to channel and we don't
+ // want a contact list group channel keeping a ref of connection, otherwise connection will
+ // leak, thus the channels.
+ channel->connection()->deref();
+
+ connect(channel->becomeReady(),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onContactListGroupChannelReady(Tp::PendingOperation*)));
+ }
+}
+
+void ContactManager::Roster::onContactListGroupChannelReady(PendingOperation *op)
+{
+ --featureContactListGroupsTodo; // incremented in onNewChannels
+
+ ConnectionPtr conn(contactManager->connection());
+
+ if (introspectGroupsPendingOp) {
+ checkContactListGroupsReady();
+ } else {
+ PendingReady *pr = qobject_cast<PendingReady*>(op);
+ ChannelPtr channel = ChannelPtr::qObjectCast(pr->proxy());
+ QString id = addContactListGroupChannel(channel);
+ emit contactManager->groupAdded(id);
+ pendingContactListGroupChannels.removeOne(channel);
+ }
+}
+
+void ContactManager::Roster::gotChannels(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QVariant> reply = *watcher;
+
+ if (!reply.isError()) {
+ debug() << "Got channels";
+ onNewChannels(qdbus_cast<ChannelDetailsList>(reply.value()));
+ } else {
+ warning().nospace() << "Getting channels failed with " <<
+ reply.error().name() << ":" << reply.error().message();
+ }
+
+ --featureContactListGroupsTodo; // incremented in introspectRosterGroups
+
+ checkContactListGroupsReady();
+
+ watcher->deleteLater();
+}
+
+void ContactManager::Roster::onStoredChannelMembersChanged(
+ const Contacts &groupMembersAdded,
+ const Contacts &groupLocalPendingMembersAdded,
+ const Contacts &groupRemotePendingMembersAdded,
+ const Contacts &groupMembersRemoved,
+ const Channel::GroupMemberChangeDetails &details)
+{
+ if (!groupLocalPendingMembersAdded.isEmpty()) {
+ warning() << "Found local pending contacts on stored list";
+ }
+
+ if (!groupRemotePendingMembersAdded.isEmpty()) {
+ warning() << "Found remote pending contacts on stored list";
+ }
+
+ foreach (ContactPtr contact, groupMembersAdded) {
+ debug() << "Contact" << contact->id() << "on stored list";
+ }
+
+ foreach (ContactPtr contact, groupMembersRemoved) {
+ debug() << "Contact" << contact->id() << "removed from stored list";
+ }
+
+ // Perform the needed computation for allKnownContactsChanged
+ computeKnownContactsChanges(groupMembersAdded,
+ groupLocalPendingMembersAdded, groupRemotePendingMembersAdded,
+ groupMembersRemoved, details);
+}
+
+void ContactManager::Roster::onSubscribeChannelMembersChanged(
+ const Contacts &groupMembersAdded,
+ const Contacts &groupLocalPendingMembersAdded,
+ const Contacts &groupRemotePendingMembersAdded,
+ const Contacts &groupMembersRemoved,
+ const Channel::GroupMemberChangeDetails &details)
+{
+ if (!groupLocalPendingMembersAdded.isEmpty()) {
+ warning() << "Found local pending contacts on subscribe list";
+ }
+
+ foreach (ContactPtr contact, groupMembersAdded) {
+ debug() << "Contact" << contact->id() << "on subscribe list";
+ contact->setSubscriptionState(SubscriptionStateYes);
+ }
+
+ foreach (ContactPtr contact, groupRemotePendingMembersAdded) {
+ debug() << "Contact" << contact->id() << "added to subscribe list";
+ contact->setSubscriptionState(SubscriptionStateAsk);
+ }
+
+ foreach (ContactPtr contact, groupMembersRemoved) {
+ debug() << "Contact" << contact->id() << "removed from subscribe list";
+ contact->setSubscriptionState(SubscriptionStateNo);
+ }
+
+ // Perform the needed computation for allKnownContactsChanged
+ computeKnownContactsChanges(groupMembersAdded,
+ groupLocalPendingMembersAdded, groupRemotePendingMembersAdded,
+ groupMembersRemoved, details);
+}
+
+void ContactManager::Roster::onPublishChannelMembersChanged(
+ const Contacts &groupMembersAdded,
+ const Contacts &groupLocalPendingMembersAdded,
+ const Contacts &groupRemotePendingMembersAdded,
+ const Contacts &groupMembersRemoved,
+ const Channel::GroupMemberChangeDetails &details)
+{
+ if (!groupRemotePendingMembersAdded.isEmpty()) {
+ warning() << "Found remote pending contacts on publish list";
+ }
+
+ foreach (ContactPtr contact, groupMembersAdded) {
+ debug() << "Contact" << contact->id() << "on publish list";
+ contact->setPublishState(SubscriptionStateYes);
+ }
+
+ foreach (ContactPtr contact, groupLocalPendingMembersAdded) {
+ debug() << "Contact" << contact->id() << "added to publish list";
+ contact->setPublishState(SubscriptionStateAsk, details.message());
+ }
+
+ foreach (ContactPtr contact, groupMembersRemoved) {
+ debug() << "Contact" << contact->id() << "removed from publish list";
+ contact->setPublishState(SubscriptionStateNo);
+ }
+
+ if (!groupLocalPendingMembersAdded.isEmpty()) {
+ emit contactManager->presencePublicationRequested(groupLocalPendingMembersAdded);
+ // FIXME (API/ABI break) remove both of these signals
+ emit contactManager->presencePublicationRequested(groupLocalPendingMembersAdded,
+ details);
+ emit contactManager->presencePublicationRequested(groupLocalPendingMembersAdded,
+ details.message());
+ }
+
+ // Perform the needed computation for allKnownContactsChanged
+ computeKnownContactsChanges(groupMembersAdded,
+ groupLocalPendingMembersAdded, groupRemotePendingMembersAdded,
+ groupMembersRemoved, details);
+}
+
+void ContactManager::Roster::onDenyChannelMembersChanged(
+ const Contacts &groupMembersAdded,
+ const Contacts &groupLocalPendingMembersAdded,
+ const Contacts &groupRemotePendingMembersAdded,
+ const Contacts &groupMembersRemoved,
+ const Channel::GroupMemberChangeDetails &details)
+{
+ if (!groupLocalPendingMembersAdded.isEmpty()) {
+ warning() << "Found local pending contacts on deny list";
+ }
+
+ if (!groupRemotePendingMembersAdded.isEmpty()) {
+ warning() << "Found remote pending contacts on deny list";
+ }
+
+ foreach (ContactPtr contact, groupMembersAdded) {
+ debug() << "Contact" << contact->id() << "added to deny list";
+ contact->setBlocked(true);
+ }
+
+ foreach (ContactPtr contact, groupMembersRemoved) {
+ debug() << "Contact" << contact->id() << "removed from deny list";
+ contact->setBlocked(false);
+ }
+
+ // Perform the needed computation for allKnownContactsChanged
+ computeKnownContactsChanges(groupMembersAdded, Contacts(),
+ Contacts(), groupMembersRemoved, details);
+}
+
+void ContactManager::Roster::onContactListGroupMembersChanged(
+ const Tp::Contacts &groupMembersAdded,
+ const Tp::Contacts &groupLocalPendingMembersAdded,
+ const Tp::Contacts &groupRemotePendingMembersAdded,
+ const Tp::Contacts &groupMembersRemoved,
+ const Tp::Channel::GroupMemberChangeDetails &details)
+{
+ ChannelPtr contactListGroupChannel = ChannelPtr(
+ qobject_cast<Channel*>(sender()));
+ QString id = contactListGroupChannel->immutableProperties().value(
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetID")).toString();
+
+ foreach (const ContactPtr &contact, groupMembersAdded) {
+ contact->setAddedToGroup(id);
+ }
+ foreach (const ContactPtr &contact, groupMembersRemoved) {
+ contact->setRemovedFromGroup(id);
+ }
+
+ emit contactManager->groupMembersChanged(id, groupMembersAdded,
+ groupMembersRemoved, details);
+}
+
+void ContactManager::Roster::onContactListGroupRemoved(Tp::DBusProxy *proxy,
+ const QString &errorName, const QString &errorMessage)
+{
+ Q_UNUSED(errorName);
+ Q_UNUSED(errorMessage);
+
+ // Is it correct to assume that if an user-defined contact list
+ // gets invalidated it means it was removed? Spec states that if a
+ // user-defined contact list gets closed it was removed, and Channel
+ // invalidates itself when it gets closed.
+ ChannelPtr contactListGroupChannel = ChannelPtr(qobject_cast<Channel*>(proxy));
+ QString id = contactListGroupChannel->immutableProperties().value(
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetID")).toString();
+ contactListGroupChannels.remove(id);
+ removedContactListGroupChannels.append(contactListGroupChannel);
+ disconnect(contactListGroupChannel.data(), 0, 0, 0);
+ emit contactManager->groupRemoved(id);
+}
+
+void ContactManager::Roster::introspectContactBlocking()
+{
+ debug() << "Requesting ContactBlockingCapabilities property";
+
+ ConnectionPtr conn(contactManager->connection());
+
+ Client::ConnectionInterfaceContactBlockingInterface *iface =
+ conn->interface<Client::ConnectionInterfaceContactBlockingInterface>();
+
+ PendingVariant *pv = iface->requestPropertyContactBlockingCapabilities();
+ connect(pv,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(gotContactBlockingCapabilities(Tp::PendingOperation*)));
+}
+
+void ContactManager::Roster::introspectContactBlockingBlockedContacts()
+{
+ ConnectionPtr conn(contactManager->connection());
+
+ Client::ConnectionInterfaceContactBlockingInterface *iface =
+ conn->interface<Client::ConnectionInterfaceContactBlockingInterface>();
+
+ Q_ASSERT(iface);
+
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+ iface->RequestBlockedContacts(), contactManager);
+ connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotContactBlockingBlockedContacts(QDBusPendingCallWatcher*)));
+
+ connect(iface,
+ SIGNAL(BlockedContactsChanged(Tp::HandleIdentifierMap,Tp::HandleIdentifierMap)),
+ SLOT(onContactBlockingBlockedContactsChanged(Tp::HandleIdentifierMap,Tp::HandleIdentifierMap)));
+}
+
+void ContactManager::Roster::introspectContactList()
+{
+ debug() << "Requesting ContactList properties";
+
+ ConnectionPtr conn(contactManager->connection());
+
+ Client::ConnectionInterfaceContactListInterface *iface =
+ conn->interface<Client::ConnectionInterfaceContactListInterface>();
+
+ connect(iface,
+ SIGNAL(ContactListStateChanged(uint)),
+ SLOT(onContactListStateChanged(uint)));
+ connect(iface,
+ SIGNAL(ContactsChangedWithID(Tp::ContactSubscriptionMap,Tp::HandleIdentifierMap,Tp::HandleIdentifierMap)),
+ SLOT(onContactListContactsChangedWithId(Tp::ContactSubscriptionMap,Tp::HandleIdentifierMap,Tp::HandleIdentifierMap)));
+ connect(iface,
+ SIGNAL(ContactsChanged(Tp::ContactSubscriptionMap,Tp::UIntList)),
+ SLOT(onContactListContactsChanged(Tp::ContactSubscriptionMap,Tp::UIntList)));
+
+ PendingVariantMap *pvm = iface->requestAllProperties();
+ connect(pvm,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(gotContactListProperties(Tp::PendingOperation*)));
+}
+
+void ContactManager::Roster::introspectContactListContacts()
+{
+ ConnectionPtr conn(contactManager->connection());
+
+ Client::ConnectionInterfaceContactListInterface *iface =
+ conn->interface<Client::ConnectionInterfaceContactListInterface>();
+
+ Features features(conn->contactFactory()->features());
+ Features supportedFeatures(contactManager->supportedFeatures());
+ QSet<QString> interfaces;
+ foreach (const Feature &feature, features) {
+ contactManager->ensureTracking(feature);
+
+ if (supportedFeatures.contains(feature)) {
+ // Only query interfaces which are reported as supported to not get an error
+ interfaces.insert(contactManager->featureToInterface(feature));
+ }
+ }
+ interfaces.insert(TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_LIST);
+
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+ iface->GetContactListAttributes(interfaces.toList(), true), contactManager);
+ connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotContactListContacts(QDBusPendingCallWatcher*)));
+}
+
+void ContactManager::Roster::processContactListChanges()
+{
+ if (processingContactListChanges || contactListChangesQueue.isEmpty()) {
+ return;
+ }
+
+ processingContactListChanges = true;
+ (this->*(contactListChangesQueue.dequeue()))();
+}
+
+void ContactManager::Roster::processContactListBlockedContactsChanged()
+{
+ BlockedContactsChangedInfo info = contactListBlockedContactsChangedQueue.head();
+
+ UIntList contacts;
+ HandleIdentifierMap::const_iterator begin = info.added.constBegin();
+ HandleIdentifierMap::const_iterator end = info.added.constEnd();
+ for (HandleIdentifierMap::const_iterator i = begin; i != end; ++i) {
+ uint bareHandle = i.key();
+ contacts << bareHandle;
+ }
+
+ begin = info.removed.constBegin();
+ end = info.removed.constEnd();
+ for (HandleIdentifierMap::const_iterator i = begin; i != end; ++i) {
+ uint bareHandle = i.key();
+ contacts << bareHandle;
+ }
+
+ Features features;
+ if (contactManager->connection()->isReady(Connection::FeatureRosterGroups)) {
+ features << Contact::FeatureRosterGroups;
+ }
+ PendingContacts *pc = contactManager->contactsForHandles(contacts, features);
+ connect(pc,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onContactListBlockedContactsConstructed(Tp::PendingOperation*)));
+}
+
+void ContactManager::Roster::processContactListUpdates()
+{
+ UpdateInfo info = contactListUpdatesQueue.head();
+
+ // construct Contact objects for all contacts in added to the contact list
+ UIntList contacts;
+ ContactSubscriptionMap::const_iterator begin = info.changes.constBegin();
+ ContactSubscriptionMap::const_iterator end = info.changes.constEnd();
+ for (ContactSubscriptionMap::const_iterator i = begin; i != end; ++i) {
+ uint bareHandle = i.key();
+ contacts << bareHandle;
+ }
+
+ Features features;
+ if (contactManager->connection()->isReady(Connection::FeatureRosterGroups)) {
+ features << Contact::FeatureRosterGroups;
+ }
+ PendingContacts *pc = contactManager->contactsForHandles(contacts, features);
+ connect(pc,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onContactListNewContactsConstructed(Tp::PendingOperation*)));
+}
+
+void ContactManager::Roster::processContactListGroupsUpdates()
+{
+ GroupsUpdateInfo info = contactListGroupsUpdatesQueue.dequeue();
+
+ foreach (const QString &group, info.groupsAdded) {
+ Contacts contacts;
+ foreach (uint bareHandle, info.contacts) {
+ ContactPtr contact = contactManager->lookupContactByHandle(bareHandle);
+ if (!contact) {
+ warning() << "contact with handle" << bareHandle << "was added to a group but "
+ "never added to the contact list, ignoring";
+ continue;
+ }
+ contacts << contact;
+ contact->setAddedToGroup(group);
+ }
+
+ emit contactManager->groupMembersChanged(group, contacts,
+ Contacts(), Channel::GroupMemberChangeDetails());
+ }
+
+ foreach (const QString &group, info.groupsRemoved) {
+ Contacts contacts;
+ foreach (uint bareHandle, info.contacts) {
+ ContactPtr contact = contactManager->lookupContactByHandle(bareHandle);
+ if (!contact) {
+ warning() << "contact with handle" << bareHandle << "was removed from a group but "
+ "never added to the contact list, ignoring";
+ continue;
+ }
+ contacts << contact;
+ contact->setRemovedFromGroup(group);
+ }
+
+ emit contactManager->groupMembersChanged(group, Contacts(),
+ contacts, Channel::GroupMemberChangeDetails());
+ }
+
+ processingContactListChanges = false;
+ processContactListChanges();
+}
+
+void ContactManager::Roster::processContactListGroupsCreated()
+{
+ QStringList names = contactListGroupsCreatedQueue.dequeue();
+ foreach (const QString &name, names) {
+ cachedAllKnownGroups.insert(name);
+ emit contactManager->groupAdded(name);
+ }
+
+ processingContactListChanges = false;
+ processContactListChanges();
+}
+
+void ContactManager::Roster::processContactListGroupRenamed()
+{
+ GroupRenamedInfo info = contactListGroupRenamedQueue.dequeue();
+ cachedAllKnownGroups.remove(info.oldName);
+ cachedAllKnownGroups.insert(info.newName);
+ emit contactManager->groupRenamed(info.oldName, info.newName);
+
+ processingContactListChanges = false;
+ processContactListChanges();
+}
+
+void ContactManager::Roster::processContactListGroupsRemoved()
+{
+ QStringList names = contactListGroupsRemovedQueue.dequeue();
+ foreach (const QString &name, names) {
+ cachedAllKnownGroups.remove(name);
+ emit contactManager->groupRemoved(name);
+ }
+
+ processingContactListChanges = false;
+ processContactListChanges();
+}
+
+void ContactManager::Roster::processFinishedModify()
+{
+ ModifyFinishOp *op = modifyFinishQueue.dequeue();
+ // Only continue processing changes (and thus, emitting change signals) when the op has signaled
+ // finish (it'll only do this after we've returned to the mainloop)
+ connect(op,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onModifyFinishSignaled()));
+ op->finish();
+}
+
+PendingOperation *ContactManager::Roster::queuedFinishVoid(const QDBusPendingCall &call)
+{
+ PendingOperation *actual = new PendingVoid(call, contactManager->connection());
+ connect(actual,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onModifyFinished(Tp::PendingOperation*)));
+ ModifyFinishOp *toReturn = new ModifyFinishOp(contactManager->connection());
+ returnedModifyOps.insert(actual, toReturn);
+ return toReturn;
+}
+
+void ContactManager::Roster::onModifyFinishSignaled()
+{
+ processingContactListChanges = false;
+ processContactListChanges();
+}
+
+void ContactManager::Roster::setContactListChannelsReady()
+{
+ if (!usingFallbackContactList) {
+ Q_ASSERT(!contactListChannels.contains(ChannelInfo::TypeSubscribe));
+ Q_ASSERT(!contactListChannels.contains(ChannelInfo::TypePublish));
+ Q_ASSERT(!contactListChannels.contains(ChannelInfo::TypeStored));
+ }
+
+ if (contactListChannels.contains(ChannelInfo::TypeSubscribe)) {
+ subscribeChannel = contactListChannels[ChannelInfo::TypeSubscribe].channel;
+ }
+
+ if (contactListChannels.contains(ChannelInfo::TypePublish)) {
+ publishChannel = contactListChannels[ChannelInfo::TypePublish].channel;
+ }
+
+ if (contactListChannels.contains(ChannelInfo::TypeStored)) {
+ storedChannel = contactListChannels[ChannelInfo::TypeStored].channel;
+ }
+
+ if (contactListChannels.contains(ChannelInfo::TypeDeny)) {
+ denyChannel = contactListChannels[ChannelInfo::TypeDeny].channel;
+ }
+
+ uint type;
+ ChannelPtr channel;
+ const char *method;
+ for (QMap<uint, ChannelInfo>::const_iterator i = contactListChannels.constBegin();
+ i != contactListChannels.constEnd(); ++i) {
+ type = i.key();
+ channel = i.value().channel;
+ if (!channel) {
+ continue;
+ }
+
+ if (type == ChannelInfo::TypeStored) {
+ method = SLOT(onStoredChannelMembersChanged(
+ Tp::Contacts,
+ Tp::Contacts,
+ Tp::Contacts,
+ Tp::Contacts,
+ Tp::Channel::GroupMemberChangeDetails));
+ } else if (type == ChannelInfo::TypeSubscribe) {
+ method = SLOT(onSubscribeChannelMembersChanged(
+ Tp::Contacts,
+ Tp::Contacts,
+ Tp::Contacts,
+ Tp::Contacts,
+ Tp::Channel::GroupMemberChangeDetails));
+ } else if (type == ChannelInfo::TypePublish) {
+ method = SLOT(onPublishChannelMembersChanged(
+ Tp::Contacts,
+ Tp::Contacts,
+ Tp::Contacts,
+ Tp::Contacts,
+ Tp::Channel::GroupMemberChangeDetails));
+ } else if (type == ChannelInfo::TypeDeny) {
+ method = SLOT(onDenyChannelMembersChanged(
+ Tp::Contacts,
+ Tp::Contacts,
+ Tp::Contacts,
+ Tp::Contacts,
+ Tp::Channel::GroupMemberChangeDetails));
+ } else {
+ continue;
+ }
+
+ connect(channel.data(),
+ SIGNAL(groupMembersChanged(
+ Tp::Contacts,
+ Tp::Contacts,
+ Tp::Contacts,
+ Tp::Contacts,
+ Tp::Channel::GroupMemberChangeDetails)),
+ method);
+ }
+}
+
+void ContactManager::Roster::updateContactsBlockState()
+{
+ Q_ASSERT(!hasContactBlockingInterface);
+
+ if (!denyChannel) {
+ return;
+ }
+
+ Contacts denyContacts = denyChannel->groupContacts();
+ foreach (const ContactPtr &contact, denyContacts) {
+ contact->setBlocked(true);
+ }
+}
+
+void ContactManager::Roster::updateContactsPresenceState()
+{
+ if (!subscribeChannel && !publishChannel) {
+ return;
+ }
+
+ Contacts subscribeContacts;
+ Contacts subscribeContactsRP;
+
+ if (subscribeChannel) {
+ subscribeContacts = subscribeChannel->groupContacts();
+ subscribeContactsRP = subscribeChannel->groupRemotePendingContacts();
+ }
+
+ Contacts publishContacts;
+ Contacts publishContactsLP;
+ if (publishChannel) {
+ publishContacts = publishChannel->groupContacts();
+ publishContactsLP = publishChannel->groupLocalPendingContacts();
+ }
+
+ Contacts contacts = cachedAllKnownContacts;
+ foreach (ContactPtr contact, contacts) {
+ if (subscribeChannel) {
+ // not in "subscribe" -> No, in "subscribe" lp -> Ask, in "subscribe" current -> Yes
+ if (subscribeContacts.contains(contact)) {
+ contact->setSubscriptionState(SubscriptionStateYes);
+ } else if (subscribeContactsRP.contains(contact)) {
+ contact->setSubscriptionState(SubscriptionStateAsk);
+ } else {
+ contact->setSubscriptionState(SubscriptionStateNo);
+ }
+ }
+
+ if (publishChannel) {
+ // not in "publish" -> No, in "subscribe" rp -> Ask, in "publish" current -> Yes
+ if (publishContacts.contains(contact)) {
+ contact->setPublishState(SubscriptionStateYes);
+ } else if (publishContactsLP.contains(contact)) {
+ contact->setPublishState(SubscriptionStateAsk,
+ publishChannel->groupLocalPendingContactChangeInfo(contact).message());
+ } else {
+ contact->setPublishState(SubscriptionStateNo);
+ }
+ }
+ }
+}
+
+void ContactManager::Roster::computeKnownContactsChanges(const Tp::Contacts& added,
+ const Tp::Contacts& pendingAdded, const Tp::Contacts& remotePendingAdded,
+ const Tp::Contacts& removed, const Channel::GroupMemberChangeDetails &details)
+{
+ // First of all, compute the real additions/removals based upon our cache
+ Tp::Contacts realAdded;
+ realAdded.unite(added);
+ realAdded.unite(pendingAdded);
+ realAdded.unite(remotePendingAdded);
+ realAdded.subtract(cachedAllKnownContacts);
+ Tp::Contacts realRemoved = removed;
+ realRemoved.intersect(cachedAllKnownContacts);
+
+ // Check if realRemoved have been _really_ removed from all lists
+ foreach (const ChannelInfo &contactListChannel, contactListChannels) {
+ ChannelPtr channel = contactListChannel.channel;
+ if (!channel) {
+ continue;
+ }
+ realRemoved.subtract(channel->groupContacts());
+ realRemoved.subtract(channel->groupLocalPendingContacts());
+ realRemoved.subtract(channel->groupRemotePendingContacts());
+ }
+
+ // ...and from the Conn.I.ContactList / Conn.I.ContactBlocking contacts
+ realRemoved.subtract(contactListContacts);
+ realRemoved.subtract(blockedContacts);
+
+ // Are there any real changes?
+ if (!realAdded.isEmpty() || !realRemoved.isEmpty()) {
+ // Yes, update our "cache" and emit the signal
+ cachedAllKnownContacts.unite(realAdded);
+ cachedAllKnownContacts.subtract(realRemoved);
+ emit contactManager->allKnownContactsChanged(realAdded, realRemoved, details);
+ }
+}
+
+void ContactManager::Roster::checkContactListGroupsReady()
+{
+ if (featureContactListGroupsTodo != 0) {
+ return;
+ }
+
+ if (groupsSetSuccess) {
+ Q_ASSERT(contactManager->state() != ContactListStateSuccess);
+
+ if (introspectGroupsPendingOp) {
+ // Will emit stateChanged() signal when the op is finished in idle
+ // callback. This is to ensure FeatureRosterGroups is marked ready.
+ connect(introspectGroupsPendingOp,
+ SIGNAL(finished(Tp::PendingOperation *)),
+ SLOT(setStateSuccess()));
+ } else {
+ setStateSuccess();
+ }
+
+ groupsSetSuccess = false;
+ }
+
+ setContactListGroupChannelsReady();
+ if (introspectGroupsPendingOp) {
+ introspectGroupsPendingOp->setFinished();
+ introspectGroupsPendingOp = 0;
+ }
+ pendingContactListGroupChannels.clear();
+}
+
+void ContactManager::Roster::setContactListGroupChannelsReady()
+{
+ Q_ASSERT(usingFallbackContactList == true);
+ Q_ASSERT(contactListGroupChannels.isEmpty());
+
+ foreach (const ChannelPtr &contactListGroupChannel, pendingContactListGroupChannels) {
+ addContactListGroupChannel(contactListGroupChannel);
+ }
+}
+
+QString ContactManager::Roster::addContactListGroupChannel(const ChannelPtr &contactListGroupChannel)
+{
+ QString id = contactListGroupChannel->immutableProperties().value(
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetID")).toString();
+ contactListGroupChannels.insert(id, contactListGroupChannel);
+ connect(contactListGroupChannel.data(),
+ SIGNAL(groupMembersChanged(
+ Tp::Contacts,
+ Tp::Contacts,
+ Tp::Contacts,
+ Tp::Contacts,
+ Tp::Channel::GroupMemberChangeDetails)),
+ SLOT(onContactListGroupMembersChanged(
+ Tp::Contacts,
+ Tp::Contacts,
+ Tp::Contacts,
+ Tp::Contacts,
+ Tp::Channel::GroupMemberChangeDetails)));
+ connect(contactListGroupChannel.data(),
+ SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
+ SLOT(onContactListGroupRemoved(Tp::DBusProxy*,QString,QString)));
+
+ foreach (const ContactPtr &contact, contactListGroupChannel->groupContacts()) {
+ contact->setAddedToGroup(id);
+ }
+
+ return id;
+}
+
+/**** ContactManager::Roster::ChannelInfo ****/
+QString ContactManager::Roster::ChannelInfo::identifierForType(Type type)
+{
+ static QString identifiers[LastType] = {
+ QLatin1String("subscribe"),
+ QLatin1String("publish"),
+ QLatin1String("stored"),
+ QLatin1String("deny"),
+ };
+ return identifiers[type];
+}
+
+uint ContactManager::Roster::ChannelInfo::typeForIdentifier(const QString &identifier)
+{
+ static QHash<QString, uint> types;
+ if (types.isEmpty()) {
+ types.insert(QLatin1String("subscribe"), TypeSubscribe);
+ types.insert(QLatin1String("publish"), TypePublish);
+ types.insert(QLatin1String("stored"), TypeStored);
+ types.insert(QLatin1String("deny"), TypeDeny);
+ }
+ if (types.contains(identifier)) {
+ return types[identifier];
+ }
+ return (uint) -1;
+}
+
+/**** ContactManager::Roster::ModifyFinishOp ****/
+ContactManager::Roster::ModifyFinishOp::ModifyFinishOp(const ConnectionPtr &conn)
+ : PendingOperation(conn)
+{
+}
+
+void ContactManager::Roster::ModifyFinishOp::setError(const QString &errorName, const QString &errorMessage)
+{
+ Q_ASSERT(this->errorName.isEmpty());
+ Q_ASSERT(this->errorMessage.isEmpty());
+
+ Q_ASSERT(!errorName.isEmpty());
+
+ this->errorName = errorName;
+ this->errorMessage = errorMessage;
+}
+
+void ContactManager::Roster::ModifyFinishOp::finish()
+{
+ if (errorName.isEmpty()) {
+ setFinished();
+ } else {
+ setFinishedWithError(errorName, errorMessage);
+ }
+}
+
+/**** ContactManager::Roster::RemoveGroupOp ****/
+ContactManager::Roster::RemoveGroupOp::RemoveGroupOp(const ChannelPtr &channel)
+ : PendingOperation(channel)
+{
+ Contacts contacts = channel->groupContacts();
+ if (!contacts.isEmpty()) {
+ connect(channel->groupRemoveContacts(contacts.toList()),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onContactsRemoved(Tp::PendingOperation*)));
+ } else {
+ connect(channel->requestClose(),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onChannelClosed(Tp::PendingOperation*)));
+ }
+}
+
+void ContactManager::Roster::RemoveGroupOp::onContactsRemoved(PendingOperation *op)
+{
+ if (op->isError()) {
+ setFinishedWithError(op->errorName(), op->errorMessage());
+ return;
+ }
+
+ // Let's ignore possible errors and try to remove the group
+ ChannelPtr channel = ChannelPtr(qobject_cast<Channel*>((Channel *) _object().data()));
+ connect(channel->requestClose(),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onChannelClosed(Tp::PendingOperation*)));
+}
+
+void ContactManager::Roster::RemoveGroupOp::onChannelClosed(PendingOperation *op)
+{
+ if (!op->isError()) {
+ setFinished();
+ } else {
+ setFinishedWithError(op->errorName(), op->errorMessage());
+ }
+}
+
+} // Tp
diff --git a/TelepathyQt/contact-manager.cpp b/TelepathyQt/contact-manager.cpp
new file mode 100644
index 00000000..9e4709d6
--- /dev/null
+++ b/TelepathyQt/contact-manager.cpp
@@ -0,0 +1,1592 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/ContactManager>
+#include "TelepathyQt/contact-manager-internal.h"
+
+#include "TelepathyQt/_gen/contact-manager.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/AvatarData>
+#include <TelepathyQt/Connection>
+#include <TelepathyQt/ConnectionLowlevel>
+#include <TelepathyQt/ContactFactory>
+#include <TelepathyQt/PendingChannel>
+#include <TelepathyQt/PendingContactAttributes>
+#include <TelepathyQt/PendingContacts>
+#include <TelepathyQt/PendingFailure>
+#include <TelepathyQt/PendingHandles>
+#include <TelepathyQt/PendingVariantMap>
+#include <TelepathyQt/ReferencedHandles>
+#include <TelepathyQt/Utils>
+
+#include <QMap>
+#include <QWeakPointer>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT ContactManager::Private
+{
+ Private(ContactManager *parent, Connection *connection);
+ ~Private();
+
+ // avatar specific methods
+ bool buildAvatarFileName(QString token, bool createDir,
+ QString &avatarFileName, QString &mimeTypeFileName);
+
+ ContactManager *parent;
+ QWeakPointer<Connection> connection;
+ ContactManager::Roster *roster;
+
+ QMap<uint, QWeakPointer<Contact> > contacts;
+
+ QMap<Feature, bool> tracking;
+ Features supportedFeatures;
+
+ // avatar
+ QSet<ContactPtr> requestAvatarsQueue;
+ bool requestAvatarsIdle;
+
+ // contact info
+ PendingRefreshContactInfo *refreshInfoOp;
+};
+
+ContactManager::Private::Private(ContactManager *parent, Connection *connection)
+ : parent(parent),
+ connection(connection),
+ roster(new ContactManager::Roster(parent)),
+ requestAvatarsIdle(false),
+ refreshInfoOp(0)
+{
+}
+
+ContactManager::Private::~Private()
+{
+ delete refreshInfoOp;
+ delete roster;
+}
+
+bool ContactManager::Private::buildAvatarFileName(QString token, bool createDir,
+ QString &avatarFileName, QString &mimeTypeFileName)
+{
+ QString cacheDir = QString(QLatin1String(qgetenv("XDG_CACHE_HOME")));
+ if (cacheDir.isEmpty()) {
+ cacheDir = QString(QLatin1String("%1/.cache")).arg(QLatin1String(qgetenv("HOME")));
+ }
+
+ ConnectionPtr conn(parent->connection());
+ QString path = QString(QLatin1String("%1/telepathy/avatars/%2/%3")).
+ arg(cacheDir).arg(conn->cmName()).arg(conn->protocolName());
+
+ if (createDir && !QDir().mkpath(path)) {
+ return false;
+ }
+
+ avatarFileName = QString(QLatin1String("%1/%2")).arg(path).arg(escapeAsIdentifier(token));
+ mimeTypeFileName = QString(QLatin1String("%1.mime")).arg(avatarFileName);
+
+ return true;
+}
+
+ContactManager::PendingRefreshContactInfo::PendingRefreshContactInfo(const ConnectionPtr &conn)
+ : PendingOperation(conn),
+ mConn(conn)
+{
+}
+
+ContactManager::PendingRefreshContactInfo::~PendingRefreshContactInfo()
+{
+}
+
+void ContactManager::PendingRefreshContactInfo::addContact(Contact *contact)
+{
+ mToRequest.insert(contact->handle()[0]);
+}
+
+void ContactManager::PendingRefreshContactInfo::refreshInfo()
+{
+ Q_ASSERT(!mToRequest.isEmpty());
+
+ if (!mConn->isValid()) {
+ setFinishedWithError(TP_QT_ERROR_NOT_AVAILABLE,
+ QLatin1String("Connection is invalid"));
+ return;
+ }
+
+ if (!mConn->hasInterface(TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_INFO)) {
+ setFinishedWithError(TP_QT_ERROR_NOT_IMPLEMENTED,
+ QLatin1String("Connection does not support ContactInfo interface"));
+ return;
+ }
+
+ debug() << "Calling ContactInfo.RefreshContactInfo for" << mToRequest.size() << "handles";
+ Client::ConnectionInterfaceContactInfoInterface *contactInfoInterface =
+ mConn->interface<Client::ConnectionInterfaceContactInfoInterface>();
+ Q_ASSERT(contactInfoInterface);
+ PendingVoid *nested = new PendingVoid(
+ contactInfoInterface->RefreshContactInfo(mToRequest.toList()),
+ mConn);
+ connect(nested,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onRefreshInfoFinished(Tp::PendingOperation*)));
+}
+
+void ContactManager::PendingRefreshContactInfo::onRefreshInfoFinished(PendingOperation *op)
+{
+ if (op->isError()) {
+ warning() << "ContactInfo.RefreshContactInfo failed with" <<
+ op->errorName() << "-" << op->errorMessage();
+ setFinishedWithError(op->errorName(), op->errorMessage());
+ } else {
+ debug() << "Got reply to ContactInfo.RefreshContactInfo";
+ setFinished();
+ }
+}
+
+/**
+ * \class ContactManager
+ * \ingroup clientconn
+ * \headerfile TelepathyQt/contact-manager.h <TelepathyQt/ContactManager>
+ *
+ * \brief The ContactManager class is responsible for managing contacts.
+ *
+ * See \ref async_model, \ref shared_ptr
+ */
+
+/**
+ * Construct a new ContactManager object.
+ *
+ * \param connection The connection owning this ContactManager.
+ */
+ContactManager::ContactManager(Connection *connection)
+ : Object(),
+ mPriv(new Private(this, connection))
+{
+}
+
+/**
+ * Class destructor.
+ */
+ContactManager::~ContactManager()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the connection owning this ContactManager.
+ *
+ * \return A pointer to the Connection object.
+ */
+ConnectionPtr ContactManager::connection() const
+{
+ return ConnectionPtr(mPriv->connection);
+}
+
+/**
+ * Return the features that are expected to work on contacts on this ContactManager connection.
+ *
+ * This method requires Connection::FeatureCore to be ready.
+ *
+ * \return The supported features as a set of Feature objects.
+ */
+Features ContactManager::supportedFeatures() const
+{
+ if (mPriv->supportedFeatures.isEmpty() &&
+ connection()->interfaces().contains(QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_CONTACTS))) {
+ Features allFeatures = Features()
+ << Contact::FeatureAlias
+ << Contact::FeatureAvatarToken
+ << Contact::FeatureAvatarData
+ << Contact::FeatureSimplePresence
+ << Contact::FeatureCapabilities
+ << Contact::FeatureLocation
+ << Contact::FeatureInfo
+ << Contact::FeatureRosterGroups;
+ QStringList interfaces = connection()->lowlevel()->contactAttributeInterfaces();
+ foreach (const Feature &feature, allFeatures) {
+ if (interfaces.contains(featureToInterface(feature))) {
+ mPriv->supportedFeatures.insert(feature);
+ }
+ }
+
+ debug() << mPriv->supportedFeatures.size() << "contact features supported using" << this;
+ }
+
+ return mPriv->supportedFeatures;
+}
+
+/**
+ * Return the progress made in retrieving the contact list.
+ *
+ * Change notification is via the stateChanged() signal.
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \return The contact list state as #ContactListState.
+ * \sa stateChanged()
+ */
+ContactListState ContactManager::state() const
+{
+ return mPriv->roster->state();
+}
+
+/**
+ * Return a list of relevant contacts (a reasonable guess as to what should
+ * be displayed as "the contact list").
+ *
+ * This may include any or all of: contacts whose presence the user receives,
+ * contacts who are allowed to see the user's presence, contacts stored in
+ * some persistent contact list on the server, contacts who the user
+ * has blocked from communicating with them, or contacts who are relevant
+ * in some other way.
+ *
+ * User interfaces displaying a contact list will probably want to filter this
+ * list and display some suitable subset of it.
+ *
+ * On protocols where there is no concept of presence or a centrally-stored
+ * contact list (like IRC), this method may return an empty list.
+ *
+ * Change notification is via the allKnownContactsChanged() signal.
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \return A set of pointers to the Contact objects.
+ * \sa allKnownContactsChanged()
+ */
+Contacts ContactManager::allKnownContacts() const
+{
+ if (!connection()->isReady(Connection::FeatureRoster)) {
+ warning() << "Calling allKnownContacts() before FeatureRoster is ready";
+ return Contacts();
+ }
+
+ return mPriv->roster->allKnownContacts();
+}
+
+/**
+ * Return a list of user-defined contact list groups' names.
+ *
+ * Change notification is via the groupAdded(), groupRemoved() and groupRenamed() signals.
+ *
+ * This method requires Connection::FeatureRosterGroups to be ready.
+ *
+ * \return The list of user-defined contact list groups names.
+ * \sa groupMembersChanged(), groupAdded(), groupRemoved(), groupRenamed()
+ */
+QStringList ContactManager::allKnownGroups() const
+{
+ if (!connection()->isReady(Connection::FeatureRosterGroups)) {
+ return QStringList();
+ }
+
+ return mPriv->roster->allKnownGroups();
+}
+
+/**
+ * Attempt to add an user-defined contact list group named \a group.
+ *
+ * On some protocols (e.g. XMPP) empty groups are not represented on the server,
+ * so disconnecting from the server and reconnecting might cause empty groups to
+ * vanish.
+ *
+ * The returned pending operation will finish successfully if the group already
+ * exists.
+ *
+ * Change notification is via the groupAdded() signal.
+ *
+ * This method requires Connection::FeatureRosterGroups to be ready.
+ *
+ * \param group The group name.
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when an attempt has been made to add an user-defined contact list group.
+ * \sa allKnownGroups(), groupAdded(), addContactsToGroup()
+ */
+PendingOperation *ContactManager::addGroup(const QString &group)
+{
+ if (!connection()->isValid()) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Connection is invalid"),
+ connection());
+ } else if (!connection()->isReady(Connection::FeatureRosterGroups)) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Connection::FeatureRosterGroups is not ready"),
+ connection());
+ }
+
+ return mPriv->roster->addGroup(group);
+}
+
+/**
+ * Attempt to remove an user-defined contact list group named \a group.
+ *
+ * Change notification is via the groupRemoved() signal.
+ *
+ * This method requires Connection::FeatureRosterGroups to be ready.
+ *
+ * \param group The group name.
+ * \return A PendingOperation which will emit PendingOperation::finished()
+ * when an attempt has been made to remove an user-defined contact list group.
+ * \sa allKnownGroups(), groupRemoved(), removeContactsFromGroup()
+ */
+PendingOperation *ContactManager::removeGroup(const QString &group)
+{
+ if (!connection()->isValid()) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Connection is invalid"),
+ connection());
+ } else if (!connection()->isReady(Connection::FeatureRosterGroups)) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Connection::FeatureRosterGroups is not ready"),
+ connection());
+ }
+
+ return mPriv->roster->removeGroup(group);
+}
+
+/**
+ * Return the contacts in the given user-defined contact list group
+ * named \a group.
+ *
+ * Change notification is via the groupMembersChanged() signal.
+ *
+ * This method requires Connection::FeatureRosterGroups to be ready.
+ *
+ * \param group The group name.
+ * \return A set of pointers to the Contact objects, or an empty set if the group does not exist.
+ * \sa allKnownGroups(), groupMembersChanged()
+ */
+Contacts ContactManager::groupContacts(const QString &group) const
+{
+ if (!connection()->isReady(Connection::FeatureRosterGroups)) {
+ return Contacts();
+ }
+
+ return mPriv->roster->groupContacts(group);
+}
+
+/**
+ * Attempt to add the given \a contacts to the user-defined contact list
+ * group named \a group.
+ *
+ * Change notification is via the groupMembersChanged() signal.
+ *
+ * This method requires Connection::FeatureRosterGroups to be ready.
+ *
+ * \param group The group name.
+ * \param contacts Contacts to add.
+ * \return A PendingOperation which will emit PendingOperation::finished()
+ * when an attempt has been made to add the contacts to the user-defined
+ * contact list group.
+ * \sa groupMembersChanged(), groupContacts()
+ */
+PendingOperation *ContactManager::addContactsToGroup(const QString &group,
+ const QList<ContactPtr> &contacts)
+{
+ if (!connection()->isValid()) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Connection is invalid"),
+ connection());
+ } else if (!connection()->isReady(Connection::FeatureRosterGroups)) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Connection::FeatureRosterGroups is not ready"),
+ connection());
+ }
+
+ return mPriv->roster->addContactsToGroup(group, contacts);
+}
+
+/**
+ * Attempt to remove the given \a contacts from the user-defined contact list
+ * group named \a group.
+ *
+ * Change notification is via the groupMembersChanged() signal.
+ *
+ * This method requires Connection::FeatureRosterGroups to be ready.
+ *
+ * \param group The group name.
+ * \param contacts Contacts to remove.
+ * \return A PendingOperation which will PendingOperation::finished
+ * when an attempt has been made to remove the contacts from the user-defined
+ * contact list group.
+ * \sa groupMembersChanged(), groupContacts()
+ */
+PendingOperation *ContactManager::removeContactsFromGroup(const QString &group,
+ const QList<ContactPtr> &contacts)
+{
+ if (!connection()->isValid()) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Connection is invalid"),
+ connection());
+ } else if (!connection()->isReady(Connection::FeatureRosterGroups)) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Connection::FeatureRosterGroups is not ready"),
+ connection());
+ }
+
+ return mPriv->roster->removeContactsFromGroup(group, contacts);
+}
+
+/**
+ * Return whether subscribing to additional contacts' presence is supported.
+ *
+ * In some protocols, the list of contacts whose presence can be seen is
+ * fixed, so we can't subscribe to the presence of additional contacts.
+ *
+ * Notably, in link-local XMPP, you can see the presence of everyone on the
+ * local network, and trying to add more subscriptions would be meaningless.
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \return \c true if Contact::requestPresenceSubscription() and
+ * requestPresenceSubscription() are likely to succeed, \c false otherwise.
+ * \sa requestPresenceSubscription(), subscriptionRequestHasMessage()
+ */
+bool ContactManager::canRequestPresenceSubscription() const
+{
+ if (!connection()->isReady(Connection::FeatureRoster)) {
+ return false;
+ }
+
+ return mPriv->roster->canRequestPresenceSubscription();
+}
+
+/**
+ * Return whether a message can be sent when subscribing to contacts'
+ * presence.
+ *
+ * If no message will actually be sent, user interfaces should avoid prompting
+ * the user for a message, and use an empty string for the message argument.
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \return \c true if the message argument to Contact::requestPresenceSubscription() and
+ * requestPresenceSubscription() is actually used, \c false otherwise.
+ * \sa canRemovePresenceSubscription(), requestPresenceSubscription()
+ */
+bool ContactManager::subscriptionRequestHasMessage() const
+{
+ if (!connection()->isReady(Connection::FeatureRoster)) {
+ return false;
+ }
+
+ return mPriv->roster->subscriptionRequestHasMessage();
+}
+
+/**
+ * Attempt to subscribe to the presence of the given contacts.
+ *
+ * This operation is sometimes called "adding contacts to the buddy
+ * list" or "requesting authorization".
+ *
+ * On most protocols, the contacts will need to give permission
+ * before the user will be able to receive their presence: if so, they will
+ * be in presence state Contact::PresenceStateAsk until they authorize
+ * or deny the request.
+ *
+ * The returned PendingOperation will return successfully when a request to
+ * subscribe to the contacts' presence has been submitted, or fail if this
+ * cannot happen. In particular, it does not wait for the contacts to give
+ * permission for the presence subscription.
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \param contacts Contacts whose presence is desired
+ * \param message A message from the user which is either transmitted to the
+ * contacts, or ignored, depending on the protocol
+ * \return A PendingOperation which will PendingOperation::finished()
+ * when an attempt has been made to subscribe to the contacts' presence.
+ * \sa canRequestPresenceSubscription(), subscriptionRequestHasMessage()
+ */
+PendingOperation *ContactManager::requestPresenceSubscription(
+ const QList<ContactPtr> &contacts, const QString &message)
+{
+ if (!connection()->isValid()) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Connection is invalid"),
+ connection());
+ } else if (!connection()->isReady(Connection::FeatureRoster)) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Connection::FeatureRoster is not ready"),
+ connection());
+ }
+
+ return mPriv->roster->requestPresenceSubscription(contacts, message);
+}
+
+/**
+ * Return whether the user can stop receiving the presence of a contact
+ * whose presence they have subscribed to.
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \return \c true if Contact::removePresenceSubscription() and
+ * removePresenceSubscription() are likely to succeed
+ * for contacts with subscription state Contact::PresenceStateYes,
+ * \c false otherwise.
+ * \sa removePresenceSubscription(), subscriptionRemovalHasMessage()
+ */
+bool ContactManager::canRemovePresenceSubscription() const
+{
+ if (!connection()->isReady(Connection::FeatureRoster)) {
+ return false;
+ }
+
+ return mPriv->roster->canRemovePresenceSubscription();
+}
+
+/**
+ * Return whether a message can be sent when removing an existing subscription
+ * to the presence of a contact.
+ *
+ * If no message will actually be sent, user interfaces should avoid prompting
+ * the user for a message, and use an empty string for the message argument.
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \return \c true if the message argument to Contact::removePresenceSubscription() and
+ * removePresenceSubscription() is actually used,
+ * for contacts with subscription state Contact::PresenceStateYes,
+ * \c false otherwise.
+ * \sa canRemovePresencePublication(), removePresenceSubscription()
+ */
+bool ContactManager::subscriptionRemovalHasMessage() const
+{
+ if (!connection()->isReady(Connection::FeatureRoster)) {
+ return false;
+ }
+
+ return mPriv->roster->subscriptionRemovalHasMessage();
+}
+
+/**
+ * Return whether the user can cancel a request to subscribe to a contact's
+ * presence before that contact has responded.
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \return \c true if Contact::removePresenceSubscription() and
+ * removePresenceSubscription() are likely to succeed
+ * for contacts with subscription state Contact::PresenceStateAsk,
+ * \c false otherwise.
+ * \sa removePresenceSubscription(), subscriptionRescindingHasMessage()
+ */
+bool ContactManager::canRescindPresenceSubscriptionRequest() const
+{
+ if (!connection()->isReady(Connection::FeatureRoster)) {
+ return false;
+ }
+
+ return mPriv->roster->canRescindPresenceSubscriptionRequest();
+}
+
+/**
+ * Return whether a message can be sent when cancelling a request to
+ * subscribe to the presence of a contact.
+ *
+ * If no message will actually be sent, user interfaces should avoid prompting
+ * the user for a message, and use an empty string for the message argument.
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \return \c true if the message argument to Contact::removePresenceSubscription() and
+ * removePresenceSubscription() is actually used,
+ * for contacts with subscription state Contact::PresenceStateAsk,
+ * \c false otherwise.
+ * \sa canRescindPresenceSubscriptionRequest(), removePresenceSubscription()
+ */
+bool ContactManager::subscriptionRescindingHasMessage() const
+{
+ if (!connection()->isReady(Connection::FeatureRoster)) {
+ return false;
+ }
+
+ return mPriv->roster->subscriptionRescindingHasMessage();
+}
+
+/**
+ * Attempt to stop receiving the presence of the given contacts, or cancel
+ * a request to subscribe to their presence that was previously sent.
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \param contacts Contacts whose presence is no longer required
+ * \message A message from the user which is either transmitted to the
+ * contacts, or ignored, depending on the protocol
+ * \return A PendingOperation which will PendingOperation::finished()
+ * when an attempt has been made to remove any subscription to the contacts' presence.
+ * \sa canRemovePresenceSubscription(), canRescindPresenceSubscriptionRequest(),
+ * subscriptionRemovalHasMessage(), subscriptionRescindingHasMessage()
+ */
+PendingOperation *ContactManager::removePresenceSubscription(
+ const QList<ContactPtr> &contacts, const QString &message)
+{
+ if (!connection()->isValid()) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Connection is invalid"),
+ connection());
+ } else if (!connection()->isReady(Connection::FeatureRoster)) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Connection::FeatureRoster is not ready"),
+ connection());
+ }
+
+ return mPriv->roster->removePresenceSubscription(contacts, message);
+}
+
+/**
+ * Return true if the publication of the user's presence to contacts can be
+ * authorized.
+ *
+ * This is always true, unless the protocol has no concept of authorizing
+ * publication (in which case contacts' publication status can never be
+ * Contact::PresenceStateAsk).
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \return \c true if Contact::authorizePresencePublication() and
+ * authorizePresencePublication() are likely to succeed
+ * for contacts with subscription state Contact::PresenceStateAsk,
+ * \c false otherwise.
+ * \sa publicationAuthorizationHasMessage(), authorizePresencePublication()
+ */
+bool ContactManager::canAuthorizePresencePublication() const
+{
+ if (!connection()->isReady(Connection::FeatureRoster)) {
+ return false;
+ }
+
+ return mPriv->roster->canAuthorizePresencePublication();
+}
+
+/**
+ * Return whether a message can be sent when authorizing a request from a
+ * contact that the user's presence is published to them.
+ *
+ * If no message will actually be sent, user interfaces should avoid prompting
+ * the user for a message, and use an empty string for the message argument.
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \return \c true if the message argument to Contact::authorizePresencePublication() and
+ * authorizePresencePublication() is actually used,
+ * for contacts with subscription state Contact::PresenceStateAsk,
+ * \c false otherwise.
+ * \sa canAuthorizePresencePublication(), authorizePresencePublication()
+ */
+bool ContactManager::publicationAuthorizationHasMessage() const
+{
+ if (!connection()->isReady(Connection::FeatureRoster)) {
+ return false;
+ }
+
+ return mPriv->roster->publicationAuthorizationHasMessage();
+}
+
+/**
+ * If the given contacts have asked the user to publish presence to them,
+ * grant permission for this publication to take place.
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \param contacts Contacts who should be allowed to receive the user's
+ * presence
+ * \message A message from the user which is either transmitted to the
+ * contacts, or ignored, depending on the protocol
+ * \return A PendingOperation which will emit PendingOperation::fininshed
+ * when an attempt has been made to authorize publication of the user's presence
+ * to the contacts.
+ * \sa canAuthorizePresencePublication(), publicationAuthorizationHasMessage()
+ */
+PendingOperation *ContactManager::authorizePresencePublication(
+ const QList<ContactPtr> &contacts, const QString &message)
+{
+ if (!connection()->isValid()) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Connection is invalid"),
+ connection());
+ } else if (!connection()->isReady(Connection::FeatureRoster)) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Connection::FeatureRoster is not ready"),
+ connection());
+ }
+
+ return mPriv->roster->authorizePresencePublication(contacts, message);
+}
+
+/**
+ * Return whether a message can be sent when rejecting a request from a
+ * contact that the user's presence is published to them.
+ *
+ * If no message will actually be sent, user interfaces should avoid prompting
+ * the user for a message, and use an empty string for the message argument.
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \return \c true if the message argument to Contact::removePresencePublication() and
+ * removePresencePublication() is actually used,
+ * for contacts with subscription state Contact::PresenceStateAsk,
+ * \c false otherwise.
+ * \sa canRemovePresencePublication(), removePresencePublication()
+ */
+bool ContactManager::publicationRejectionHasMessage() const
+{
+ if (!connection()->isReady(Connection::FeatureRoster)) {
+ return false;
+ }
+
+ return mPriv->roster->publicationRejectionHasMessage();
+}
+
+/**
+ * Return true if the publication of the user's presence to contacts can be
+ * removed, even after permission has been given.
+ *
+ * (Rejecting requests for presence to be published is always allowed.)
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \return \c true if Contact::removePresencePublication() and
+ * removePresencePublication() are likely to succeed
+ * for contacts with subscription state Contact::PresenceStateYes,
+ * \c false otherwise.
+ * \sa publicationRemovalHasMessage(), removePresencePublication()
+ */
+bool ContactManager::canRemovePresencePublication() const
+{
+ if (!connection()->isReady(Connection::FeatureRoster)) {
+ return false;
+ }
+
+ return mPriv->roster->canRemovePresencePublication();
+}
+
+/**
+ * Return whether a message can be sent when revoking earlier permission
+ * that the user's presence is published to a contact.
+ *
+ * If no message will actually be sent, user interfaces should avoid prompting
+ * the user for a message, and use an empty string for the message argument.
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \return \c true if the message argument to Contact::removePresencePublication and
+ * removePresencePublication() is actually used,
+ * for contacts with subscription state Contact::PresenceStateYes,
+ * \c false otherwise.
+ * \sa canRemovePresencePublication(), removePresencePublication()
+ */
+bool ContactManager::publicationRemovalHasMessage() const
+{
+ if (!connection()->isReady(Connection::FeatureRoster)) {
+ return false;
+ }
+
+ return mPriv->roster->publicationRemovalHasMessage();
+}
+
+/**
+ * If the given contacts have asked the user to publish presence to them,
+ * deny this request (this should always succeed, unless a network error
+ * occurs).
+ *
+ * If the given contacts already have permission to receive
+ * the user's presence, attempt to revoke that permission (this might not
+ * be supported by the protocol - canRemovePresencePublication
+ * indicates whether it is likely to succeed).
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \param contacts Contacts who should no longer be allowed to receive the
+ * user's presence
+ * \message A message from the user which is either transmitted to the
+ * contacts, or ignored, depending on the protocol
+ * \return A PendingOperation which will emit PendingOperation::finished()
+ * when an attempt has been made to remove any publication of the user's presence to the
+ * contacts.
+ * \sa canRemovePresencePublication(), publicationRejectionHasMessage(),
+ * publicationRemovalHasMessage()
+ */
+PendingOperation *ContactManager::removePresencePublication(
+ const QList<ContactPtr> &contacts, const QString &message)
+{
+ if (!connection()->isValid()) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Connection is invalid"),
+ connection());
+ } else if (!connection()->isReady(Connection::FeatureRoster)) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Connection::FeatureRoster is not ready"),
+ connection());
+ }
+
+ return mPriv->roster->removePresencePublication(contacts, message);
+}
+
+/**
+ * Remove completely contacts from the server. It has the same effect than
+ * calling removePresencePublication() and removePresenceSubscription(),
+ * but also remove from 'stored' list if it exists.
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \param contacts Contacts who should be removed
+ * \message A message from the user which is either transmitted to the
+ * contacts, or ignored, depending on the protocol
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when an attempt has been made to remove any publication of the user's presence to
+ * the contacts.
+ */
+PendingOperation *ContactManager::removeContacts(
+ const QList<ContactPtr> &contacts, const QString &message)
+{
+ if (!connection()->isValid()) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Connection is invalid"),
+ connection());
+ } else if (!connection()->isReady(Connection::FeatureRoster)) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Connection::FeatureRoster is not ready"),
+ connection());
+ }
+
+ return mPriv->roster->removeContacts(contacts, message);
+}
+
+/**
+ * Return whether this protocol has a list of blocked contacts.
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \return \c true if blockContacts() is likely to succeed, \c false otherwise.
+ */
+bool ContactManager::canBlockContacts() const
+{
+ if (!connection()->isReady(Connection::FeatureRoster)) {
+ return false;
+ }
+
+ return mPriv->roster->canBlockContacts();
+}
+
+/**
+ * Return whether this protocol can report abusive contacts.
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \return \c true if reporting abuse when blocking contacts is supported, \c false otherwise.
+ */
+bool ContactManager::canReportAbuse() const
+{
+ if (!connection()->isReady(Connection::FeatureRoster)) {
+ return false;
+ }
+
+ return mPriv->roster->canReportAbuse();
+}
+
+/**
+ * \deprecated Use blockContacts(const QList<ContactPtr> &contacts) instead.
+ */
+PendingOperation *ContactManager::blockContacts(
+ const QList<ContactPtr> &contacts, bool value)
+{
+ return mPriv->roster->blockContacts(contacts, value, false);
+}
+
+/**
+ * Block the given contacts. Blocked contacts cannot send messages
+ * to the user; depending on the protocol, blocking a contact may
+ * have other effects.
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \param contacts Contacts that should be blocked.
+ * \return A PendingOperation which will emit PendingOperation::finished()
+ * when an attempt has been made to take the requested action.
+ * \sa canBlockContacts(), unblockContacts(), blockContactsAndReportAbuse()
+ */
+PendingOperation *ContactManager::blockContacts(const QList<ContactPtr> &contacts)
+{
+ return mPriv->roster->blockContacts(contacts, true, false);
+}
+
+/**
+ * Block the given contacts and additionally report abusive behaviour
+ * to the server.
+ *
+ * If reporting abusive behaviour is not supported by the protocol,
+ * this method has the same effect as blockContacts().
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \param contacts Contacts who should be added to the list of blocked contacts.
+ * \return A PendingOperation which will emit PendingOperation::finished()
+ * when an attempt has been made to take the requested action.
+ * \sa canBlockContacts(), canReportAbuse(), blockContacts()
+ */
+PendingOperation *ContactManager::blockContactsAndReportAbuse(
+ const QList<ContactPtr> &contacts)
+{
+ return mPriv->roster->blockContacts(contacts, true, true);
+}
+
+/**
+ * Unblock the given contacts.
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \param contacts Contacts that should be unblocked.
+ * \return A PendingOperation which will emit PendingOperation::finished()
+ * when an attempt has been made to take the requested action.
+ * \sa canBlockContacts(), blockContacts(), blockContactsAndReportAbuse()
+ */
+PendingOperation *ContactManager::unblockContacts(const QList<ContactPtr> &contacts)
+{
+ return mPriv->roster->blockContacts(contacts, false, false);
+}
+
+PendingContacts *ContactManager::contactsForHandles(const UIntList &handles,
+ const Features &features)
+{
+ QMap<uint, ContactPtr> satisfyingContacts;
+ QSet<uint> otherContacts;
+ Features missingFeatures;
+
+ Features realFeatures(features);
+ realFeatures.unite(connection()->contactFactory()->features());
+ // FeatureAvatarData depends on FeatureAvatarToken
+ if (realFeatures.contains(Contact::FeatureAvatarData) &&
+ !realFeatures.contains(Contact::FeatureAvatarToken)) {
+ realFeatures.insert(Contact::FeatureAvatarToken);
+ }
+
+ if (!connection()->isValid()) {
+ return new PendingContacts(ContactManagerPtr(this), handles, features, Features(),
+ QStringList(), satisfyingContacts, otherContacts,
+ QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Connection is invalid"));
+ } else if (!connection()->isReady(Connection::FeatureCore)) {
+ return new PendingContacts(ContactManagerPtr(this), handles, features, Features(),
+ QStringList(), satisfyingContacts, otherContacts,
+ QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Connection::FeatureCore is not ready"));
+ }
+
+ ConnectionLowlevelPtr connLowlevel = connection()->lowlevel();
+
+ if (connLowlevel->hasImmortalHandles() && realFeatures.isEmpty()) {
+ // try to avoid a roundtrip if all handles have an id set and no feature was requested
+ foreach (uint handle, handles) {
+ if (connLowlevel->hasContactId(handle)) {
+ ContactPtr contact = ensureContact(handle,
+ connLowlevel->contactId(handle), realFeatures);
+ satisfyingContacts.insert(handle, contact);
+ }
+ }
+ }
+
+ foreach (uint handle, handles) {
+ ContactPtr contact = lookupContactByHandle(handle);
+ if (contact) {
+ if ((realFeatures - contact->requestedFeatures()).isEmpty()) {
+ // Contact exists and has all the requested features
+ satisfyingContacts.insert(handle, contact);
+ } else {
+ // Contact exists but is missing features
+ otherContacts.insert(handle);
+ missingFeatures.unite(realFeatures - contact->requestedFeatures());
+ }
+ } else {
+ // Contact doesn't exist - we need to get all of the features (same as unite(features))
+ missingFeatures = realFeatures;
+ otherContacts.insert(handle);
+ }
+ }
+
+ Features supported = supportedFeatures();
+ QSet<QString> interfaces;
+ foreach (const Feature &feature, missingFeatures) {
+ ensureTracking(feature);
+
+ if (supported.contains(feature)) {
+ // Only query interfaces which are reported as supported to not get an error
+ interfaces.insert(featureToInterface(feature));
+ }
+ }
+
+ PendingContacts *contacts =
+ new PendingContacts(ContactManagerPtr(this), handles, features, missingFeatures,
+ interfaces.toList(), satisfyingContacts, otherContacts);
+ return contacts;
+}
+
+PendingContacts *ContactManager::contactsForHandles(const ReferencedHandles &handles,
+ const Features &features)
+{
+ return contactsForHandles(handles.toList(), features);
+}
+
+PendingContacts *ContactManager::contactsForHandles(const HandleIdentifierMap &handles,
+ const Features &features)
+{
+ connection()->lowlevel()->injectContactIds(handles);
+
+ return contactsForHandles(handles.keys(), features);
+}
+
+PendingContacts *ContactManager::contactsForIdentifiers(const QStringList &identifiers,
+ const Features &features)
+{
+ if (!connection()->isValid()) {
+ return new PendingContacts(ContactManagerPtr(this), identifiers, features,
+ QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Connection is invalid"));
+ } else if (!connection()->isReady(Connection::FeatureCore)) {
+ return new PendingContacts(ContactManagerPtr(this), identifiers, features,
+ QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Connection::FeatureCore is not ready"));
+ }
+
+ Features realFeatures(features);
+ realFeatures.unite(connection()->contactFactory()->features());
+ PendingContacts *contacts = new PendingContacts(ContactManagerPtr(this), identifiers,
+ realFeatures);
+ return contacts;
+}
+
+PendingContacts *ContactManager::upgradeContacts(const QList<ContactPtr> &contacts,
+ const Features &features)
+{
+ if (!connection()->isValid()) {
+ return new PendingContacts(ContactManagerPtr(this), contacts, features,
+ QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Connection is invalid"));
+ } else if (!connection()->isReady(Connection::FeatureCore)) {
+ return new PendingContacts(ContactManagerPtr(this), contacts, features,
+ QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Connection::FeatureCore is not ready"));
+ }
+
+ return new PendingContacts(ContactManagerPtr(this), contacts, features);
+}
+
+ContactPtr ContactManager::lookupContactByHandle(uint handle)
+{
+ ContactPtr contact;
+
+ if (mPriv->contacts.contains(handle)) {
+ contact = ContactPtr(mPriv->contacts.value(handle));
+ if (!contact) {
+ // Dangling weak pointer, remove it
+ mPriv->contacts.remove(handle);
+ }
+ }
+
+ return contact;
+}
+
+/**
+ * Start a request to retrieve the avatar for the given \a contacts.
+ *
+ * Force the request of the avatar data. This method returns directly, emitting
+ * Contact::avatarTokenChanged() and Contact::avatarDataChanged() signals once the token
+ * and data are fetched from the server.
+ *
+ * This is only useful if the avatar token is unknown; see Contact::isAvatarTokenKnown().
+ * It happens in the case of offline XMPP contacts, because the server does not
+ * send the token for them and an explicit request of the avatar data is needed.
+ *
+ * This method requires Contact::FeatureAvatarData to be ready.
+ *
+ * \sa Contact::avatarData(), Contact::avatarDataChanged(),
+ * Contact::avatarToken(), Contact::avatarTokenChanged()
+ */
+void ContactManager::requestContactAvatars(const QList<ContactPtr> &contacts)
+{
+ if (contacts.isEmpty()) {
+ return;
+ }
+
+ if (!mPriv->requestAvatarsIdle) {
+ mPriv->requestAvatarsIdle = true;
+ QTimer::singleShot(0, this, SLOT(doRequestAvatars()));
+ }
+
+ mPriv->requestAvatarsQueue.unite(contacts.toSet());
+}
+
+/**
+ * \deprecated
+ *
+ * This was never intended to be public API.
+ * Use Contact::requestAvatarData() or ContactManager::requestContactAvatars() instead.
+ */
+void ContactManager::requestContactAvatar(Contact *contact)
+{
+ requestContactAvatars(QList<ContactPtr>() << ContactPtr(contact));
+}
+
+/**
+ * Refresh information for the given contact.
+ *
+ * Once the information is retrieved infoFieldsChanged() will be emitted.
+ *
+ * This method requires Contact::FeatureInfo to be ready.
+ *
+ * \return A PendingOperation, which will emit PendingOperation::finished
+ * when the call has finished.
+ * \sa infoFieldsChanged()
+ */
+PendingOperation *ContactManager::refreshContactInfo(const QList<ContactPtr> &contacts)
+{
+ if (!mPriv->refreshInfoOp) {
+ mPriv->refreshInfoOp = new PendingRefreshContactInfo(connection());
+ QTimer::singleShot(0, this, SLOT(doRefreshInfo()));
+ }
+
+ foreach (const ContactPtr &contact, contacts) {
+ mPriv->refreshInfoOp->addContact(contact.data());
+ }
+
+ return mPriv->refreshInfoOp;
+}
+
+void ContactManager::onAliasesChanged(const AliasPairList &aliases)
+{
+ debug() << "Got AliasesChanged for" << aliases.size() << "contacts";
+
+ foreach (AliasPair pair, aliases) {
+ ContactPtr contact = lookupContactByHandle(pair.handle);
+
+ if (contact) {
+ contact->receiveAlias(pair.alias);
+ }
+ }
+}
+
+void ContactManager::doRequestAvatars()
+{
+ Q_ASSERT(mPriv->requestAvatarsIdle);
+ QSet<ContactPtr> contacts = mPriv->requestAvatarsQueue;
+ Q_ASSERT(contacts.size() > 0);
+
+ mPriv->requestAvatarsQueue.clear();
+ mPriv->requestAvatarsIdle = false;
+
+ int found = 0;
+ UIntList notFound;
+ foreach (const ContactPtr &contact, contacts) {
+ if (!contact) {
+ continue;
+ }
+
+ QString avatarFileName;
+ QString mimeTypeFileName;
+ bool success = (contact->isAvatarTokenKnown() &&
+ mPriv->buildAvatarFileName(contact->avatarToken(), false,
+ avatarFileName, mimeTypeFileName));
+
+ /* Check if the avatar is already in the cache */
+ if (success && QFile::exists(avatarFileName)) {
+ QFile mimeTypeFile(mimeTypeFileName);
+ mimeTypeFile.open(QIODevice::ReadOnly);
+ QString mimeType = QString(QLatin1String(mimeTypeFile.readAll()));
+ mimeTypeFile.close();
+
+ found++;
+
+ contact->receiveAvatarData(AvatarData(avatarFileName, mimeType));
+
+ continue;
+ }
+
+ notFound << contact->handle()[0];
+ }
+
+ if (found > 0) {
+ debug() << "Avatar(s) found in cache for" << found << "contact(s)";
+ }
+
+ if (found == contacts.size()) {
+ return;
+ }
+
+ debug() << "Requesting avatar(s) for" << contacts.size() - found << "contact(s)";
+
+ Client::ConnectionInterfaceAvatarsInterface *avatarsInterface =
+ connection()->interface<Client::ConnectionInterfaceAvatarsInterface>();
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+ avatarsInterface->RequestAvatars(notFound),
+ this);
+ connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), watcher,
+ SLOT(deleteLater()));
+}
+
+void ContactManager::onAvatarUpdated(uint handle, const QString &token)
+{
+ debug() << "Got AvatarUpdate for contact with handle" << handle;
+
+ ContactPtr contact = lookupContactByHandle(handle);
+ if (contact) {
+ contact->receiveAvatarToken(token);
+ }
+}
+
+void ContactManager::onAvatarRetrieved(uint handle, const QString &token,
+ const QByteArray &data, const QString &mimeType)
+{
+ QString avatarFileName;
+ QString mimeTypeFileName;
+
+ debug() << "Got AvatarRetrieved for contact with handle" << handle;
+
+ bool success = mPriv->buildAvatarFileName(token, true, avatarFileName,
+ mimeTypeFileName);
+
+ if (success) {
+ debug() << "Write avatar in cache for handle" << handle;
+ debug() << "Filename:" << avatarFileName;
+ debug() << "MimeType:" << mimeType;
+
+ QTemporaryFile mimeTypeFile(mimeTypeFileName);
+ mimeTypeFile.open();
+ mimeTypeFile.write(mimeType.toLatin1());
+ mimeTypeFile.setAutoRemove(false);
+ mimeTypeFile.rename(mimeTypeFileName);
+
+ QTemporaryFile avatarFile(avatarFileName);
+ avatarFile.open();
+ avatarFile.write(data);
+ avatarFile.setAutoRemove(false);
+ avatarFile.rename(avatarFileName);
+ }
+
+ ContactPtr contact = lookupContactByHandle(handle);
+ if (contact) {
+ contact->setAvatarToken(token);
+ contact->receiveAvatarData(AvatarData(avatarFileName, mimeType));
+ }
+}
+
+void ContactManager::onPresencesChanged(const SimpleContactPresences &presences)
+{
+ debug() << "Got PresencesChanged for" << presences.size() << "contacts";
+
+ foreach (uint handle, presences.keys()) {
+ ContactPtr contact = lookupContactByHandle(handle);
+
+ if (contact) {
+ contact->receiveSimplePresence(presences[handle]);
+ }
+ }
+}
+
+void ContactManager::onCapabilitiesChanged(const ContactCapabilitiesMap &caps)
+{
+ debug() << "Got ContactCapabilitiesChanged for" << caps.size() << "contacts";
+
+ foreach (uint handle, caps.keys()) {
+ ContactPtr contact = lookupContactByHandle(handle);
+
+ if (contact) {
+ contact->receiveCapabilities(caps[handle]);
+ }
+ }
+}
+
+void ContactManager::onLocationUpdated(uint handle, const QVariantMap &location)
+{
+ debug() << "Got LocationUpdated for contact with handle" << handle;
+
+ ContactPtr contact = lookupContactByHandle(handle);
+
+ if (contact) {
+ contact->receiveLocation(location);
+ }
+}
+
+void ContactManager::onContactInfoChanged(uint handle, const Tp::ContactInfoFieldList &info)
+{
+ debug() << "Got ContactInfoChanged for contact with handle" << handle;
+
+ ContactPtr contact = lookupContactByHandle(handle);
+
+ if (contact) {
+ contact->receiveInfo(info);
+ }
+}
+
+void ContactManager::doRefreshInfo()
+{
+ PendingRefreshContactInfo *op = mPriv->refreshInfoOp;
+ Q_ASSERT(op);
+ mPriv->refreshInfoOp = 0;
+ op->refreshInfo();
+}
+
+ContactPtr ContactManager::ensureContact(const ReferencedHandles &handle,
+ const Features &features, const QVariantMap &attributes)
+{
+ uint bareHandle = handle[0];
+ ContactPtr contact = lookupContactByHandle(bareHandle);
+
+ if (!contact) {
+ contact = connection()->contactFactory()->construct(this, handle, features, attributes);
+ mPriv->contacts.insert(bareHandle, contact.data());
+ }
+
+ contact->augment(features, attributes);
+
+ return contact;
+}
+
+ContactPtr ContactManager::ensureContact(uint bareHandle, const QString &id,
+ const Features &features)
+{
+ ContactPtr contact = lookupContactByHandle(bareHandle);
+
+ if (!contact) {
+ QVariantMap attributes;
+ attributes.insert(QLatin1String(TELEPATHY_INTERFACE_CONNECTION "/contact-id"), id);
+
+ contact = connection()->contactFactory()->construct(this,
+ ReferencedHandles(connection(), HandleTypeContact, UIntList() << bareHandle),
+ features, attributes);
+ mPriv->contacts.insert(bareHandle, contact.data());
+
+ // do not call augment here as this is a fake contact
+ }
+
+ return contact;
+}
+
+QString ContactManager::featureToInterface(const Feature &feature)
+{
+ if (feature == Contact::FeatureAlias) {
+ return TP_QT_IFACE_CONNECTION_INTERFACE_ALIASING;
+ } else if (feature == Contact::FeatureAvatarToken) {
+ return TP_QT_IFACE_CONNECTION_INTERFACE_AVATARS;
+ } else if (feature == Contact::FeatureAvatarData) {
+ return TP_QT_IFACE_CONNECTION_INTERFACE_AVATARS;
+ } else if (feature == Contact::FeatureSimplePresence) {
+ return TP_QT_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE;
+ } else if (feature == Contact::FeatureCapabilities) {
+ return TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_CAPABILITIES;
+ } else if (feature == Contact::FeatureLocation) {
+ return TP_QT_IFACE_CONNECTION_INTERFACE_LOCATION;
+ } else if (feature == Contact::FeatureInfo) {
+ return TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_INFO;
+ } else if (feature == Contact::FeatureRosterGroups) {
+ return TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_GROUPS;
+ } else {
+ warning() << "ContactManager doesn't know which interface corresponds to feature"
+ << feature;
+ return QString();
+ }
+}
+
+void ContactManager::ensureTracking(const Feature &feature)
+{
+ if (mPriv->tracking[feature]) {
+ return;
+ }
+
+ ConnectionPtr conn(connection());
+
+ if (feature == Contact::FeatureAlias) {
+ Client::ConnectionInterfaceAliasingInterface *aliasingInterface =
+ conn->interface<Client::ConnectionInterfaceAliasingInterface>();
+
+ connect(aliasingInterface,
+ SIGNAL(AliasesChanged(Tp::AliasPairList)),
+ SLOT(onAliasesChanged(Tp::AliasPairList)));
+ } else if (feature == Contact::FeatureAvatarData) {
+ Client::ConnectionInterfaceAvatarsInterface *avatarsInterface =
+ conn->interface<Client::ConnectionInterfaceAvatarsInterface>();
+
+ connect(avatarsInterface,
+ SIGNAL(AvatarRetrieved(uint,QString,QByteArray,QString)),
+ SLOT(onAvatarRetrieved(uint,QString,QByteArray,QString)));
+ } else if (feature == Contact::FeatureAvatarToken) {
+ Client::ConnectionInterfaceAvatarsInterface *avatarsInterface =
+ conn->interface<Client::ConnectionInterfaceAvatarsInterface>();
+
+ connect(avatarsInterface,
+ SIGNAL(AvatarUpdated(uint,QString)),
+ SLOT(onAvatarUpdated(uint,QString)));
+ } else if (feature == Contact::FeatureCapabilities) {
+ Client::ConnectionInterfaceContactCapabilitiesInterface *contactCapabilitiesInterface =
+ conn->interface<Client::ConnectionInterfaceContactCapabilitiesInterface>();
+
+ connect(contactCapabilitiesInterface,
+ SIGNAL(ContactCapabilitiesChanged(Tp::ContactCapabilitiesMap)),
+ SLOT(onCapabilitiesChanged(Tp::ContactCapabilitiesMap)));
+ } else if (feature == Contact::FeatureInfo) {
+ Client::ConnectionInterfaceContactInfoInterface *contactInfoInterface =
+ conn->interface<Client::ConnectionInterfaceContactInfoInterface>();
+
+ connect(contactInfoInterface,
+ SIGNAL(ContactInfoChanged(uint,Tp::ContactInfoFieldList)),
+ SLOT(onContactInfoChanged(uint,Tp::ContactInfoFieldList)));
+ } else if (feature == Contact::FeatureLocation) {
+ Client::ConnectionInterfaceLocationInterface *locationInterface =
+ conn->interface<Client::ConnectionInterfaceLocationInterface>();
+
+ connect(locationInterface,
+ SIGNAL(LocationUpdated(uint,QVariantMap)),
+ SLOT(onLocationUpdated(uint,QVariantMap)));
+ } else if (feature == Contact::FeatureSimplePresence) {
+ Client::ConnectionInterfaceSimplePresenceInterface *simplePresenceInterface =
+ conn->interface<Client::ConnectionInterfaceSimplePresenceInterface>();
+
+ connect(simplePresenceInterface,
+ SIGNAL(PresencesChanged(Tp::SimpleContactPresences)),
+ SLOT(onPresencesChanged(Tp::SimpleContactPresences)));
+ } else if (feature == Contact::FeatureRosterGroups) {
+ // nothing to do here, but we don't want to warn
+ ;
+ } else {
+ warning() << " Unknown feature" << feature
+ << "when trying to figure out how to connect change notification!";
+ }
+
+ mPriv->tracking[feature] = true;
+}
+
+PendingOperation *ContactManager::introspectRoster()
+{
+ return mPriv->roster->introspect();
+}
+
+PendingOperation *ContactManager::introspectRosterGroups()
+{
+ return mPriv->roster->introspectGroups();
+}
+
+void ContactManager::resetRoster()
+{
+ mPriv->roster->reset();
+}
+
+/**
+ * \fn void ContactManager::presencePublicationRequested(const Tp::Contacts &contacts)
+ *
+ * Emitted whenever some contacts request for presence publication.
+ *
+ * \param contacts A set of contacts which requested presence publication.
+ */
+
+/**
+ * \fn void ContactManager::presencePublicationRequested(const Tp::Contacts &contacts,
+ * const QString &message)
+ *
+ * \deprecated Turned out this didn't make sense at all. There can be multiple contacts, but this
+ * signal carries just a single message.
+ * Use presencePublicationRequested(const Tp::Contacts &contacts) instead,
+ * and extract the messages from the individual Tp::Contact objects.
+ */
+
+/**
+ * \fn void ContactManager::presencePublicationRequested(const Tp::Contacts &contacts,
+ * const Tp::Channel::GroupMemberChangeDetails &details)
+ *
+ * \deprecated Turned out this didn't make sense at all. There can be multiple contacts, but this
+ * signal carries just a single details.
+ * Use presencePublicationRequested(const Tp::Contacts &contacts) instead,
+ * and extract the details (message) from the individual Tp::Contact objects.
+ */
+
+/**
+ * \fn void ContactManager::groupAdded(const QString &group)
+ *
+ * Emitted when a new contact list group is created.
+ *
+ * \param group The group name.
+ * \sa allKnownGroups()
+ */
+
+/**
+ * \fn void ContactManager::groupRenamed(const QString &oldGroup, const QString &newGroup)
+ *
+ * Emitted when a new contact list group is renamed.
+ *
+ * \param oldGroup The old group name.
+ * \param newGroup The new group name.
+ * \sa allKnownGroups()
+ */
+
+/**
+ * \fn void ContactManager::groupRemoved(const QString &group)
+ *
+ * Emitted when a contact list group is removed.
+ *
+ * \param group The group name.
+ * \sa allKnownGroups()
+ */
+
+/**
+ * \fn void ContactManager::groupMembersChanged(const QString &group,
+ * const Tp::Contacts &groupMembersAdded,
+ * const Tp::Contacts &groupMembersRemoved,
+ * const Tp::Channel::GroupMemberChangeDetails &details)
+ *
+ * Emitted whenever some contacts got removed or added from
+ * a group.
+ *
+ * \param group The name of the group that changed.
+ * \param groupMembersAdded A set of contacts which were added to the group \a group.
+ * \param groupMembersRemoved A set of contacts which were removed from the group \a group.
+ * \param details The change details.
+ * \sa groupContacts()
+ */
+
+/**
+ * \fn void ContactManager::allKnownContactsChanged(const Tp::Contacts &contactsAdded,
+ * const Tp::Contacts &contactsRemoved,
+ * const Tp::Channel::GroupMemberChangeDetails &details)
+ *
+ * Emitted whenever some contacts got removed or added from
+ * ContactManager's known contact list. It is useful for monitoring which contacts
+ * are currently known by ContactManager.
+ *
+ * Note that, in some protocols, this signal could stream newly added contacts
+ * with both presence subscription and publication state set to No. Be sure to watch
+ * over publication and/or subscription state changes if that is the case.
+ *
+ * \param contactsAdded A set of contacts which were added to the known contact list.
+ * \param contactsRemoved A set of contacts which were removed from the known contact list.
+ * \param details The change details.
+ * \sa allKnownContacts()
+ */
+
+void ContactManager::connectNotify(const char *signalName)
+{
+ if (qstrcmp(signalName, SIGNAL(presencePublicationRequested(Tp::Contacts,Tp::Channel::GroupMemberChangeDetails))) == 0) {
+ warning() << "Connecting to deprecated signal presencePublicationRequested(Tp::Contacts,Tp::Channel::GroupMemberChangeDetails)";
+ } else if (qstrcmp(signalName, SIGNAL(presencePublicationRequested(Tp::Contacts,QString))) == 0) {
+ warning() << "Connecting to deprecated signal presencePublicationRequested(Tp::Contacts,QString)";
+ }
+}
+
+} // Tp
diff --git a/TelepathyQt/contact-manager.h b/TelepathyQt/contact-manager.h
new file mode 100644
index 00000000..6f4e76cc
--- /dev/null
+++ b/TelepathyQt/contact-manager.h
@@ -0,0 +1,201 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_contact_manager_h_HEADER_GUARD_
+#define _TelepathyQt_contact_manager_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Channel>
+#include <TelepathyQt/Contact>
+#include <TelepathyQt/Feature>
+#include <TelepathyQt/Object>
+#include <TelepathyQt/ReferencedHandles>
+#include <TelepathyQt/Types>
+
+#include <QList>
+#include <QSet>
+#include <QString>
+#include <QStringList>
+#include <QVariantMap>
+
+namespace Tp
+{
+
+class Connection;
+class PendingContacts;
+class PendingOperation;
+
+class TP_QT_EXPORT ContactManager : public Object
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(ContactManager)
+
+public:
+ virtual ~ContactManager();
+
+ ConnectionPtr connection() const;
+
+ Features supportedFeatures() const;
+
+ ContactListState state() const;
+
+ Contacts allKnownContacts() const;
+ QStringList allKnownGroups() const;
+
+ PendingOperation *addGroup(const QString &group);
+ PendingOperation *removeGroup(const QString &group);
+
+ Contacts groupContacts(const QString &group) const;
+ PendingOperation *addContactsToGroup(const QString &group,
+ const QList<ContactPtr> &contacts);
+ PendingOperation *removeContactsFromGroup(const QString &group,
+ const QList<ContactPtr> &contacts);
+
+ bool canRequestPresenceSubscription() const;
+ bool subscriptionRequestHasMessage() const;
+ PendingOperation *requestPresenceSubscription(
+ const QList<ContactPtr> &contacts,
+ const QString &message = QString());
+ bool canRemovePresenceSubscription() const;
+ bool subscriptionRemovalHasMessage() const;
+ bool canRescindPresenceSubscriptionRequest() const;
+ bool subscriptionRescindingHasMessage() const;
+ PendingOperation *removePresenceSubscription(
+ const QList<ContactPtr> &contacts,
+ const QString &message = QString());
+ bool canAuthorizePresencePublication() const;
+ bool publicationAuthorizationHasMessage() const;
+ PendingOperation *authorizePresencePublication(
+ const QList<ContactPtr> &contacts,
+ const QString &message = QString());
+ bool publicationRejectionHasMessage() const;
+ bool canRemovePresencePublication() const;
+ bool publicationRemovalHasMessage() const;
+ PendingOperation *removePresencePublication(
+ const QList<ContactPtr> &contacts,
+ const QString &message = QString());
+ PendingOperation *removeContacts(
+ const QList<ContactPtr> &contacts,
+ const QString &message = QString());
+
+ bool canBlockContacts() const;
+ bool canReportAbuse() const;
+ TP_QT_DEPRECATED PendingOperation *blockContacts(
+ const QList<ContactPtr> &contacts, bool value);
+ PendingOperation *blockContacts(const QList<ContactPtr> &contacts);
+ PendingOperation *blockContactsAndReportAbuse(const QList<ContactPtr> &contacts);
+ PendingOperation *unblockContacts(const QList<ContactPtr> &contacts);
+
+ PendingContacts *contactsForHandles(const UIntList &handles,
+ const Features &features = Features());
+ PendingContacts *contactsForHandles(const ReferencedHandles &handles,
+ const Features &features = Features());
+ PendingContacts *contactsForHandles(const HandleIdentifierMap &handles,
+ const Features &features = Features());
+
+ PendingContacts *contactsForIdentifiers(const QStringList &identifiers,
+ const Features &features = Features());
+
+ PendingContacts *upgradeContacts(const QList<ContactPtr> &contacts,
+ const Features &features);
+
+ ContactPtr lookupContactByHandle(uint handle);
+
+ TP_QT_DEPRECATED void requestContactAvatar(Contact *contact);
+ void requestContactAvatars(const QList<ContactPtr> &contacts);
+
+ PendingOperation *refreshContactInfo(const QList<ContactPtr> &contact);
+
+Q_SIGNALS:
+ void stateChanged(Tp::ContactListState state);
+
+ void presencePublicationRequested(const Tp::Contacts &contacts);
+ // deprecated - carry redundant data which can be retrieved (meaningfully) from the Contacts
+ // themselves (note: multiple contacts, but just a single message/details!)
+ void presencePublicationRequested(const Tp::Contacts &contacts, const QString &message);
+ void presencePublicationRequested(const Tp::Contacts &contacts,
+ const Tp::Channel::GroupMemberChangeDetails &details);
+
+ void groupAdded(const QString &group);
+ void groupRenamed(const QString &oldGroup, const QString &newGroup);
+ void groupRemoved(const QString &group);
+
+ void groupMembersChanged(const QString &group,
+ const Tp::Contacts &groupMembersAdded,
+ const Tp::Contacts &groupMembersRemoved,
+ const Tp::Channel::GroupMemberChangeDetails &details);
+
+ void allKnownContactsChanged(const Tp::Contacts &contactsAdded,
+ const Tp::Contacts &contactsRemoved,
+ const Tp::Channel::GroupMemberChangeDetails &details);
+
+protected:
+ // FIXME: (API/ABI break) Remove connectNotify
+ void connectNotify(const char *);
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onAliasesChanged(const Tp::AliasPairList &);
+ TP_QT_NO_EXPORT void doRequestAvatars();
+ TP_QT_NO_EXPORT void onAvatarUpdated(uint, const QString &);
+ TP_QT_NO_EXPORT void onAvatarRetrieved(uint, const QString &, const QByteArray &, const QString &);
+ TP_QT_NO_EXPORT void onPresencesChanged(const Tp::SimpleContactPresences &);
+ TP_QT_NO_EXPORT void onCapabilitiesChanged(const Tp::ContactCapabilitiesMap &);
+ TP_QT_NO_EXPORT void onLocationUpdated(uint, const QVariantMap &);
+ TP_QT_NO_EXPORT void onContactInfoChanged(uint, const Tp::ContactInfoFieldList &);
+ TP_QT_NO_EXPORT void doRefreshInfo();
+
+private:
+ class PendingRefreshContactInfo;
+ class Roster;
+ friend class Connection;
+ friend class PendingContacts;
+ friend class PendingRefreshContactInfo;
+ friend class Roster;
+
+ TP_QT_NO_EXPORT ContactManager(Connection *parent);
+
+ TP_QT_NO_EXPORT ContactPtr ensureContact(const ReferencedHandles &handle,
+ const Features &features,
+ const QVariantMap &attributes);
+ TP_QT_NO_EXPORT ContactPtr ensureContact(uint bareHandle,
+ const QString &id, const Features &features);
+
+ TP_QT_NO_EXPORT static QString featureToInterface(const Feature &feature);
+ TP_QT_NO_EXPORT void ensureTracking(const Feature &feature);
+
+ TP_QT_NO_EXPORT PendingOperation *introspectRoster();
+ TP_QT_NO_EXPORT PendingOperation *introspectRosterGroups();
+ TP_QT_NO_EXPORT void resetRoster();
+
+ TP_QT_NO_EXPORT PendingOperation *refreshContactInfo(Contact *contact);
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/contact-messenger.cpp b/TelepathyQt/contact-messenger.cpp
new file mode 100644
index 00000000..fdb5608c
--- /dev/null
+++ b/TelepathyQt/contact-messenger.cpp
@@ -0,0 +1,257 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/ContactMessenger>
+
+#include "TelepathyQt/_gen/contact-messenger.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include "TelepathyQt/future-internal.h"
+
+#include <TelepathyQt/Account>
+#include <TelepathyQt/ChannelDispatcher>
+#include <TelepathyQt/ClientRegistrar>
+#include <TelepathyQt/MessageContentPartList>
+#include <TelepathyQt/PendingSendMessage>
+#include <TelepathyQt/SimpleTextObserver>
+#include <TelepathyQt/TextChannel>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT ContactMessenger::Private
+{
+ Private(ContactMessenger *parent, const AccountPtr &account, const QString &contactIdentifier)
+ : parent(parent),
+ account(account),
+ contactIdentifier(contactIdentifier),
+ cdMessagesInterface(0)
+ {
+ }
+
+ PendingSendMessage *sendMessage(const Message &message, MessageSendingFlags flags);
+
+ ContactMessenger *parent;
+ AccountPtr account;
+ QString contactIdentifier;
+ SimpleTextObserverPtr observer;
+ TpFuture::Client::ChannelDispatcherInterfaceMessagesInterface *cdMessagesInterface;
+};
+
+PendingSendMessage *ContactMessenger::Private::sendMessage(const Message &message,
+ MessageSendingFlags flags)
+{
+ if (!cdMessagesInterface) {
+ cdMessagesInterface = new TpFuture::Client::ChannelDispatcherInterfaceMessagesInterface(
+ account->dbusConnection(),
+ TP_QT_CHANNEL_DISPATCHER_BUS_NAME, TP_QT_CHANNEL_DISPATCHER_OBJECT_PATH, parent);
+ }
+
+ PendingSendMessage *op = new PendingSendMessage(ContactMessengerPtr(parent), message);
+
+ // TODO: is there a way to avoid this? Ideally TpFuture classes should use Tp types.
+ TpFuture::MessagePartList parts;
+ foreach (const Tp::MessagePart &part, message.parts()) {
+ parts << static_cast<QMap<QString, QDBusVariant> >(part);
+ }
+
+ connect(new QDBusPendingCallWatcher(
+ cdMessagesInterface->SendMessage(QDBusObjectPath(account->objectPath()),
+ contactIdentifier, parts, (uint) flags)),
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ op,
+ SLOT(onCDMessageSent(QDBusPendingCallWatcher*)));
+ return op;
+}
+
+/**
+ * \class ContactMessenger
+ * \ingroup clientaccount
+ * \headerfile TelepathyQt/contact-messenger.h <TelepathyQt/ContactMessenger>
+ *
+ * \brief The ContactMessenger class provides an easy way to send text messages to a contact
+ * and also track sent/receive text messages from the same contact.
+ */
+
+/**
+ * Create a new ContactMessenger object.
+ *
+ * \param account The account this messenger is communicating with.
+ * \param contact The contact this messenger is communicating with.
+ * \return An ContactMessengerPtr object pointing to the newly created ContactMessenger object,
+ * or a null ContactMessengerPtr if \a contact is null.
+ */
+ContactMessengerPtr ContactMessenger::create(const AccountPtr &account,
+ const ContactPtr &contact)
+{
+ if (!contact) {
+ warning() << "Contact used to create a ContactMessenger object must be "
+ "valid";
+ return ContactMessengerPtr();
+ }
+ return ContactMessengerPtr(new ContactMessenger(account, contact->id()));
+}
+
+/**
+ * Create a new ContactMessenger object.
+ *
+ * \param account The account this messenger is communicating with.
+ * \param contactIdentifier The identifier of the contact this messenger is communicating with.
+ * \return An ContactMessengerPtr object pointing to the newly created ContactMessenger object,
+ * or a null ContactMessengerPtr if \a contact is null.
+ */
+ContactMessengerPtr ContactMessenger::create(const AccountPtr &account,
+ const QString &contactIdentifier)
+{
+ if (contactIdentifier.isEmpty()) {
+ warning() << "Contact identifier used to create a ContactMessenger object must be "
+ "non-empty";
+ return ContactMessengerPtr();
+ }
+ return ContactMessengerPtr(new ContactMessenger(account, contactIdentifier));
+}
+
+/**
+ * Construct a new ContactMessenger object.
+ *
+ * \param account The account this messenger is communicating with.
+ * \param contactIdentifier The identifier of the contact this messenger is communicating with.
+ */
+ContactMessenger::ContactMessenger(const AccountPtr &account, const QString &contactIdentifier)
+ : mPriv(new Private(this, account, contactIdentifier))
+{
+ mPriv->observer = SimpleTextObserver::create(account, contactIdentifier);
+ connect(mPriv->observer.data(),
+ SIGNAL(messageSent(Tp::Message,Tp::MessageSendingFlags,QString,Tp::TextChannelPtr)),
+ SIGNAL(messageSent(Tp::Message,Tp::MessageSendingFlags,QString,Tp::TextChannelPtr)));
+ connect(mPriv->observer.data(),
+ SIGNAL(messageReceived(Tp::ReceivedMessage,Tp::TextChannelPtr)),
+ SIGNAL(messageReceived(Tp::ReceivedMessage,Tp::TextChannelPtr)));
+}
+
+/**
+ * Class destructor.
+ */
+ContactMessenger::~ContactMessenger()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the account this messenger is communicating with.
+ *
+ * \return A pointer to the Account object.
+ */
+AccountPtr ContactMessenger::account() const
+{
+ return mPriv->account;
+}
+
+/**
+ * Return the identifier of the contact this messenger is communicating with.
+ *
+ * \return The identifier of the contact.
+ */
+QString ContactMessenger::contactIdentifier() const
+{
+ return mPriv->contactIdentifier;
+}
+
+/**
+ * Return the list of text chats currently being observed.
+ *
+ * \return A list of pointers to TextChannel objects.
+ */
+QList<TextChannelPtr> ContactMessenger::textChats() const
+{
+ return mPriv->observer->textChats();
+}
+
+/**
+ * Send a message to the contact identified by contactIdentifier() using account().
+ *
+ * Note that the return from this method isn't ordered in any sane way, meaning that
+ * messageSent() can be signalled either before or after the returned PendingSendMessage object
+ * finishes.
+ *
+ * \param text The message text.
+ * \param type The message type.
+ * \param flags The message flags.
+ * \return A PendingSendMessage which will emit PendingSendMessage::finished
+ * once the reply is received and that can be used to check whether sending the
+ * message succeeded or not.
+ */
+PendingSendMessage *ContactMessenger::sendMessage(const QString &text,
+ ChannelTextMessageType type,
+ MessageSendingFlags flags)
+{
+ Message message(type, text);
+ return mPriv->sendMessage(message, flags);
+}
+
+/**
+ * Send a message to the contact identified by contactIdentifier() using account().
+ *
+ * Note that the return from this method isn't ordered in any sane way, meaning that
+ * messageSent() can be signalled either before or after the returned PendingSendMessage object
+ * finishes.
+ *
+ * \param parts The message parts.
+ * \param flags The message flags.
+ * \return A PendingSendMessage which will emit PendingSendMessage::finished
+ * once the reply is received and that can be used to check whether sending the
+ * message succeeded or not.
+ */
+PendingSendMessage *ContactMessenger::sendMessage(const MessageContentPartList &parts,
+ MessageSendingFlags flags)
+{
+ Message message(parts.bareParts());
+ return mPriv->sendMessage(message, flags);
+}
+
+/**
+ * \fn void ContactMessenger::messageSent(const Tp::Message &message,
+ * Tp::MessageSendingFlags flags, const QString &sentMessageToken,
+ * const Tp::TextChannelPtr &channel);
+ *
+ * Emitted whenever a text message on account() is sent to the contact
+ * identified by contactIdentifier().
+ *
+ * \param message The message sent.
+ * \param flags The flags of the message that was sent.
+ * \param sentMessageToken The token of the message that was sent.
+ * \param channel The channel from which the message was sent.
+ */
+
+/**
+ * \fn void ContactMessenger::messageReceived(const Tp::ReceivedMessage &message,
+ * const Tp::TextChannelPtr &channel);
+ *
+ * Emitted whenever a text message on account() is received from the contact
+ * identified by contactIdentifier().
+ *
+ * \param message The message received.
+ * \param channel The channel from which the message was received.
+ */
+
+} // Tp
diff --git a/TelepathyQt/contact-messenger.h b/TelepathyQt/contact-messenger.h
new file mode 100644
index 00000000..41b93b87
--- /dev/null
+++ b/TelepathyQt/contact-messenger.h
@@ -0,0 +1,78 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_contact_messenger_h_HEADER_GUARD_
+#define _TelepathyQt_contact_messenger_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/Message>
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+class PendingSendMessage;
+class MessageContentPartList;
+
+class TP_QT_EXPORT ContactMessenger : public QObject, public RefCounted
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(ContactMessenger)
+
+public:
+ static ContactMessengerPtr create(const AccountPtr &account, const ContactPtr &contact);
+ static ContactMessengerPtr create(const AccountPtr &account, const QString &contactIdentifier);
+
+ virtual ~ContactMessenger();
+
+ AccountPtr account() const;
+ QString contactIdentifier() const;
+
+ QList<TextChannelPtr> textChats() const;
+
+ PendingSendMessage *sendMessage(const QString &text,
+ ChannelTextMessageType type = ChannelTextMessageTypeNormal,
+ MessageSendingFlags flags = 0);
+ PendingSendMessage *sendMessage(const MessageContentPartList &parts,
+ MessageSendingFlags flags = 0);
+
+Q_SIGNALS:
+ void messageSent(const Tp::Message &message, Tp::MessageSendingFlags flags,
+ const QString &sentMessageToken, const Tp::TextChannelPtr &channel);
+ void messageReceived(const Tp::ReceivedMessage &message, const Tp::TextChannelPtr &channel);
+
+private:
+ TP_QT_NO_EXPORT ContactMessenger(const AccountPtr &account,
+ const QString &contactIdentifier);
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/contact-search-channel-internal.h b/TelepathyQt/contact-search-channel-internal.h
new file mode 100644
index 00000000..dee83147
--- /dev/null
+++ b/TelepathyQt/contact-search-channel-internal.h
@@ -0,0 +1,52 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_contact_search_channel_internal_h_HEADER_GUARD_
+#define _TelepathyQt_contact_search_channel_internal_h_HEADER_GUARD_
+
+#include <TelepathyQt/PendingOperation>
+
+#include <QDBusPendingCall>
+
+namespace Tp
+{
+
+class TP_QT_NO_EXPORT ContactSearchChannel::PendingSearch : public PendingOperation
+{
+ Q_OBJECT
+
+public:
+ PendingSearch(const ContactSearchChannelPtr &chan, QDBusPendingCall call);
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onSearchStateChanged(Tp::ChannelContactSearchState state, const QString &errorName,
+ const Tp::ContactSearchChannel::SearchStateChangeDetails &details);
+ TP_QT_NO_EXPORT void watcherFinished(QDBusPendingCallWatcher*);
+
+private:
+ bool mFinished;
+ QDBusError mError;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/contact-search-channel.cpp b/TelepathyQt/contact-search-channel.cpp
new file mode 100644
index 00000000..ece89fae
--- /dev/null
+++ b/TelepathyQt/contact-search-channel.cpp
@@ -0,0 +1,676 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/ContactSearchChannel>
+#include "TelepathyQt/contact-search-channel-internal.h"
+
+#include "TelepathyQt/_gen/contact-search-channel.moc.hpp"
+#include "TelepathyQt/_gen/contact-search-channel-internal.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/Connection>
+#include <TelepathyQt/ContactManager>
+#include <TelepathyQt/PendingContacts>
+#include <TelepathyQt/PendingFailure>
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT ContactSearchChannel::Private
+{
+ Private(ContactSearchChannel *parent,
+ const QVariantMap &immutableProperties);
+ ~Private();
+
+ static void introspectMain(Private *self);
+
+ void extractImmutableProperties(const QVariantMap &props);
+
+ void processSignalsQueue();
+ void processSearchStateChangeQueue();
+ void processSearchResultQueue();
+
+ struct SearchStateChangeInfo
+ {
+ SearchStateChangeInfo(uint state, const QString &errorName,
+ const Tp::ContactSearchChannel::SearchStateChangeDetails &details)
+ : state(state), errorName(errorName), details(details)
+ {
+ }
+
+ uint state;
+ QString errorName;
+ ContactSearchChannel::SearchStateChangeDetails details;
+ };
+
+ // Public object
+ ContactSearchChannel *parent;
+
+ QVariantMap immutableProperties;
+
+ Client::ChannelTypeContactSearchInterface *contactSearchInterface;
+ Client::DBus::PropertiesInterface *properties;
+
+ ReadinessHelper *readinessHelper;
+
+ // Introspection
+ uint searchState;
+ uint limit;
+ QStringList availableSearchKeys;
+ QString server;
+
+ QQueue<void (Private::*)()> signalsQueue;
+ QQueue<SearchStateChangeInfo> searchStateChangeQueue;
+ QQueue<ContactSearchResultMap> searchResultQueue;
+ bool processingSignalsQueue;
+};
+
+ContactSearchChannel::Private::Private(ContactSearchChannel *parent,
+ const QVariantMap &immutableProperties)
+ : parent(parent),
+ immutableProperties(immutableProperties),
+ contactSearchInterface(parent->interface<Client::ChannelTypeContactSearchInterface>()),
+ properties(parent->interface<Client::DBus::PropertiesInterface>()),
+ readinessHelper(parent->readinessHelper()),
+ searchState(ChannelContactSearchStateNotStarted),
+ limit(0),
+ processingSignalsQueue(false)
+{
+ ReadinessHelper::Introspectables introspectables;
+
+ ReadinessHelper::Introspectable introspectableCore(
+ QSet<uint>() << 0, // makesSenseForStatuses
+ Features() << Channel::FeatureCore, // dependsOnFeatures (core)
+ QStringList(), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &Private::introspectMain,
+ this);
+ introspectables[FeatureCore] = introspectableCore;
+
+ readinessHelper->addIntrospectables(introspectables);
+}
+
+ContactSearchChannel::Private::~Private()
+{
+}
+
+void ContactSearchChannel::Private::introspectMain(ContactSearchChannel::Private *self)
+{
+ /* we need to at least introspect SearchState here as it's not immutable */
+ self->parent->connect(self->contactSearchInterface,
+ SIGNAL(SearchStateChanged(uint,QString,QVariantMap)),
+ SLOT(onSearchStateChanged(uint,QString,QVariantMap)));
+ self->parent->connect(self->contactSearchInterface,
+ SIGNAL(SearchResultReceived(Tp::ContactSearchResultMap)),
+ SLOT(onSearchResultReceived(Tp::ContactSearchResultMap)));
+
+ QVariantMap props;
+ bool needIntrospectMainProps = false;
+ const unsigned numNames = 3;
+ const static QString names[numNames] = {
+ QLatin1String("Limit"),
+ QLatin1String("AvailableSearchKeys"),
+ QLatin1String("Server")
+ };
+ const static QString qualifiedNames[numNames] = {
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".Limit"),
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".AvailableSearchKeys"),
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".Server")
+ };
+ for (unsigned i = 0; i < numNames; ++i) {
+ const QString &qualified = qualifiedNames[i];
+ if (!self->immutableProperties.contains(qualified)) {
+ needIntrospectMainProps = true;
+ break;
+ }
+ props.insert(names[i], self->immutableProperties.value(qualified));
+ }
+
+ if (needIntrospectMainProps) {
+ QDBusPendingCallWatcher *watcher =
+ new QDBusPendingCallWatcher(
+ self->properties->GetAll(
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_CONTACT_SEARCH)),
+ self->parent);
+ self->parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotProperties(QDBusPendingCallWatcher*)));
+ } else {
+ self->extractImmutableProperties(props);
+
+ QDBusPendingCallWatcher *watcher =
+ new QDBusPendingCallWatcher(
+ self->properties->Get(
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_CONTACT_SEARCH),
+ QLatin1String("SearchState")),
+ self->parent);
+ self->parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotSearchState(QDBusPendingCallWatcher*)));
+ }
+}
+
+void ContactSearchChannel::Private::extractImmutableProperties(const QVariantMap &props)
+{
+ limit = qdbus_cast<uint>(props[QLatin1String("Limit")]);
+ availableSearchKeys = qdbus_cast<QStringList>(props[QLatin1String("AvailableSearchKeys")]);
+ server = qdbus_cast<QString>(props[QLatin1String("Server")]);
+}
+
+void ContactSearchChannel::Private::processSignalsQueue()
+{
+ if (processingSignalsQueue || signalsQueue.isEmpty()) {
+ return;
+ }
+
+ processingSignalsQueue = true;
+ (this->*(signalsQueue.dequeue()))();
+}
+
+void ContactSearchChannel::Private::processSearchStateChangeQueue()
+{
+ const SearchStateChangeInfo &info = searchStateChangeQueue.dequeue();
+
+ searchState = info.state;
+ emit parent->searchStateChanged(
+ static_cast<ChannelContactSearchState>(info.state), info.errorName,
+ SearchStateChangeDetails(info.details));
+
+ processingSignalsQueue = false;
+ processSignalsQueue();
+}
+
+void ContactSearchChannel::Private::processSearchResultQueue()
+{
+ const ContactSearchResultMap &result = searchResultQueue.first();
+ if (!result.isEmpty()) {
+ ContactManagerPtr manager = parent->connection()->contactManager();
+ PendingContacts *pendingContacts = manager->contactsForIdentifiers(
+ result.keys());
+ parent->connect(pendingContacts,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(gotSearchResultContacts(Tp::PendingOperation*)));
+ } else {
+ searchResultQueue.dequeue();
+
+ emit parent->searchResultReceived(SearchResult());
+
+ processingSignalsQueue = false;
+ processSignalsQueue();
+ }
+}
+
+struct TP_QT_NO_EXPORT ContactSearchChannel::SearchStateChangeDetails::Private : public QSharedData
+{
+ Private(const QVariantMap &details)
+ : details(details) {}
+
+ QVariantMap details;
+};
+
+ContactSearchChannel::SearchStateChangeDetails::SearchStateChangeDetails(const QVariantMap &details)
+ : mPriv(new Private(details))
+{
+}
+
+ContactSearchChannel::SearchStateChangeDetails::SearchStateChangeDetails()
+{
+}
+
+ContactSearchChannel::SearchStateChangeDetails::SearchStateChangeDetails(
+ const ContactSearchChannel::SearchStateChangeDetails &other)
+ : mPriv(other.mPriv)
+{
+}
+
+ContactSearchChannel::SearchStateChangeDetails::~SearchStateChangeDetails()
+{
+}
+
+ContactSearchChannel::SearchStateChangeDetails &ContactSearchChannel::SearchStateChangeDetails::operator=(
+ const ContactSearchChannel::SearchStateChangeDetails &other)
+{
+ this->mPriv = other.mPriv;
+ return *this;
+}
+
+QVariantMap ContactSearchChannel::SearchStateChangeDetails::allDetails() const
+{
+ return isValid() ? mPriv->details : QVariantMap();
+}
+
+ContactSearchChannel::PendingSearch::PendingSearch(const ContactSearchChannelPtr &channel,
+ QDBusPendingCall call)
+ : PendingOperation(channel),
+ mFinished(false)
+{
+ connect(channel.data(),
+ SIGNAL(searchStateChanged(Tp::ChannelContactSearchState, const QString &,
+ const Tp::ContactSearchChannel::SearchStateChangeDetails &)),
+ SLOT(onSearchStateChanged(Tp::ChannelContactSearchState, const QString &,
+ const Tp::ContactSearchChannel::SearchStateChangeDetails &)));
+ connect(new QDBusPendingCallWatcher(call),
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(watcherFinished(QDBusPendingCallWatcher*)));
+}
+
+void ContactSearchChannel::PendingSearch::onSearchStateChanged(
+ Tp::ChannelContactSearchState state, const QString &errorName,
+ const Tp::ContactSearchChannel::SearchStateChangeDetails &details)
+{
+ if (state != ChannelContactSearchStateNotStarted) {
+ if (mFinished) {
+ if (mError.isValid()) {
+ setFinishedWithError(mError);
+ } else {
+ setFinished();
+ }
+ }
+ mFinished = true;
+ }
+}
+
+void ContactSearchChannel::PendingSearch::watcherFinished(QDBusPendingCallWatcher *watcher)
+{
+ if (watcher->isError()) {
+ if (mFinished) {
+ setFinishedWithError(watcher->error());
+ } else {
+ mError = watcher->error();
+ }
+ } else {
+ if (mFinished) {
+ setFinished();
+ }
+ }
+ mFinished = true;
+
+ watcher->deleteLater();
+}
+
+/**
+ * \class ContactSearchChannel
+ * \ingroup clientchannel
+ * \headerfile TelepathyQt/contact-search-channel.h <TelepathyQt/ContactSearchChannel>
+ *
+ * \brief The ContactSearchChannel class represents a Telepathy channel of type
+ * ContactSearch.
+ */
+
+/**
+ * \class ContactSearchChannel::SearchStateChangeDetails
+ * \ingroup wrappers
+ * \headerfile TelepathyQt/contact-search-channel.h <TelepathyQt/ContactSearchChannel>
+ *
+ * \brief The ContactSearchChannel::SearchStateChangeDetails class provides
+ * a wrapper around the details for a search state change.
+ *
+ * \sa ContactSearchChannel
+ */
+
+/**
+ * Feature representing the core that needs to become ready to make the
+ * ContactSearchChannel object usable.
+ *
+ * Note that this feature must be enabled in order to use most
+ * ContactSearchChannel methods.
+ * See specific methods documentation for more details.
+ *
+ * When calling isReady(), becomeReady(), this feature is implicitly added
+ * to the requested features.
+ */
+const Feature ContactSearchChannel::FeatureCore = Feature(QLatin1String(ContactSearchChannel::staticMetaObject.className()), 0);
+
+/**
+ * Create a new ContactSearchChannel object.
+ *
+ * \param connection Connection owning this channel, and specifying the
+ * service.
+ * \param objectPath The channel object path.
+ * \param immutableProperties The channel immutable properties.
+ * \return A ContactSearchChannelPtr object pointing to the newly created
+ * ContactSearchChannel object.
+ */
+ContactSearchChannelPtr ContactSearchChannel::create(const ConnectionPtr &connection,
+ const QString &objectPath, const QVariantMap &immutableProperties)
+{
+ return ContactSearchChannelPtr(new ContactSearchChannel(connection, objectPath,
+ immutableProperties, ContactSearchChannel::FeatureCore));
+}
+
+/**
+ * Construct a new ContactSearchChannel object.
+ *
+ * \param connection Connection owning this channel, and specifying the
+ * service.
+ * \param objectPath The channel object path.
+ * \param immutableProperties The channel immutable properties.
+ * \param coreFeature The core feature of the channel type, if any. The corresponding introspectable should
+ * depend on ContactSearchChannel::FeatureCore.
+ */
+ContactSearchChannel::ContactSearchChannel(const ConnectionPtr &connection,
+ const QString &objectPath,
+ const QVariantMap &immutableProperties,
+ const Feature &coreFeature)
+ : Channel(connection, objectPath, immutableProperties, coreFeature),
+ mPriv(new Private(this, immutableProperties))
+{
+}
+
+/**
+ * Class destructor.
+ */
+ContactSearchChannel::~ContactSearchChannel()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the current search state of this channel.
+ *
+ * Change notification is via the searchStateChanged() signal.
+ *
+ * This method requires ContactSearchChannel::FeatureCore to be ready.
+ *
+ * \return The current search state as #ChannelContactSearchState.
+ * \sa searchStateChanged()
+ */
+ChannelContactSearchState ContactSearchChannel::searchState() const
+{
+ return static_cast<ChannelContactSearchState>(mPriv->searchState);
+}
+
+/**
+ * Return the maximum number of results that should be returned by calling search(), where
+ * 0 represents no limit.
+ *
+ * For example, if the terms passed to search() match Antonius, Bridget and Charles and
+ * this property is 2, the search service will only return Antonius and Bridget.
+ *
+ * This method requires ContactSearchChannel::FeatureCore to be ready.
+ *
+ * \return The maximum number of results, or 0 if there is no limit.
+ * \sa availableSearchKeys(), search()
+ */
+uint ContactSearchChannel::limit() const
+{
+ return mPriv->limit;
+}
+
+/**
+ * Return the set of search keys supported by this channel.
+ *
+ * Example values include [""] (for protocols where several address fields are implicitly searched)
+ * or ["x-n-given", "x-n-family", "nickname", "email"] (for XMPP XEP-0055, without extensibility via
+ * Data Forms).
+ *
+ * This method requires ContactSearchChannel::FeatureCore to be ready.
+ *
+ * \return The supported search keys.
+ * \sa limit(), search()
+ */
+QStringList ContactSearchChannel::availableSearchKeys() const
+{
+ return mPriv->availableSearchKeys;
+}
+
+/**
+ * Return the DNS name of the server being searched by this channel.
+ *
+ * This method requires ContactSearchChannel::FeatureCore to be ready.
+ *
+ * \return For protocols which support searching for contacts on multiple servers with different DNS
+ * names (like XMPP), the DNS name of the server being searched by this channel, e.g.
+ * "characters.shakespeare.lit". Otherwise, an empty string.
+ */
+QString ContactSearchChannel::server() const
+{
+ return mPriv->server;
+}
+
+/**
+ * Send a request to start a search for contacts on this connection.
+ *
+ * This may only be called while the searchState() is #ChannelContactSearchStateNotStarted;
+ * a valid search request will cause the searchStateChanged() signal to be emitted with the
+ * state #ChannelContactSearchStateInProgress.
+ *
+ * Search results are signalled by searchResultReceived().
+ *
+ * This method requires ContactSearchChannel::FeatureCore to be ready.
+ *
+ * This is an overloaded method for search(const ContactSearchMap &searchTerms).
+ *
+ * \param searchKey The search key.
+ * \param searchTerm The search term.
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the search has started.
+ * \sa searchState(), searchStateChanged(), searchResultReceived()
+ */
+PendingOperation *ContactSearchChannel::search(const QString &searchKey, const QString &searchTerm)
+{
+ ContactSearchMap searchTerms;
+ searchTerms.insert(searchKey, searchTerm);
+ return search(searchTerms);
+}
+
+/**
+ * Send a request to start a search for contacts on this connection.
+ *
+ * This may only be called while the searchState() is #ChannelContactSearchStateNotStarted;
+ * a valid search request will cause the searchStateChanged() signal to be emitted with the
+ * state #ChannelContactSearchStateInProgress.
+ *
+ * Search results are signalled by searchResultReceived().
+ *
+ * This method requires ContactSearchChannel::FeatureCore to be ready.
+ *
+ * \param terms The search terms.
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the search has started.
+ * \sa searchState(), searchStateChanged(), searchResultReceived()
+ */
+PendingOperation *ContactSearchChannel::search(const ContactSearchMap &terms)
+{
+ if (!isReady(FeatureCore)) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Channel not ready"),
+ ContactSearchChannelPtr(this));
+ }
+
+ if (searchState() != ChannelContactSearchStateNotStarted) {
+ warning() << "ContactSearchChannel::search called with "
+ "searchState() != ChannelContactSearchStateNotStarted. Doing nothing";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Search already started"),
+ ContactSearchChannelPtr(this));
+ }
+
+ return new PendingSearch(ContactSearchChannelPtr(this),
+ mPriv->contactSearchInterface->Search(terms));
+}
+
+/**
+ * Request that a search which searchState() is ChannelContactSearchStateMoreAvailable
+ * move back to state ChannelContactSearchStateInProgress and continue listing up to limit()
+ * more results.
+ */
+void ContactSearchChannel::continueSearch()
+{
+ if (!isReady(FeatureCore)) {
+ return;
+ }
+
+ if (searchState() != ChannelContactSearchStateMoreAvailable) {
+ warning() << "ContactSearchChannel::continueSearch called with "
+ "searchState() != ChannelContactSearchStateMoreAvailable. Doing nothing";
+ return;
+ }
+
+ (void) new PendingVoid(mPriv->contactSearchInterface->More(),
+ ContactSearchChannelPtr(this));
+}
+
+/**
+ * Stop the current search.
+ *
+ * This may not be called while the searchState() is #ChannelContactSearchStateNotStarted.
+ * If called while the searchState() is #ChannelContactSearchStateInProgress,
+ * searchStateChanged() will be emitted, with the state #ChannelContactSearchStateFailed and
+ * the error #TP_QT_ERROR_CANCELLED.
+ *
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the call has finished.
+ * \sa searchState(), searchStateChanged()
+ */
+void ContactSearchChannel::stopSearch()
+{
+ if (!isReady(FeatureCore)) {
+ return;
+ }
+
+ if (searchState() != ChannelContactSearchStateInProgress &&
+ searchState() != ChannelContactSearchStateMoreAvailable) {
+ warning() << "ContactSearchChannel::stopSearch called with "
+ "searchState() != ChannelContactSearchStateInProgress or "
+ "ChannelContactSearchStateMoreAvailable. Doing nothing";
+ return;
+ }
+
+ (void) new PendingVoid(mPriv->contactSearchInterface->Stop(),
+ ContactSearchChannelPtr(this));
+}
+
+void ContactSearchChannel::gotProperties(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QVariantMap> reply = *watcher;
+
+ if (!reply.isError()) {
+ QVariantMap props = reply.value();
+ mPriv->extractImmutableProperties(props);
+
+ mPriv->searchState = qdbus_cast<uint>(props[QLatin1String("SearchState")]);
+
+ debug() << "Got reply to Properties::GetAll(ContactSearchChannel)";
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureCore, true);
+ } else {
+ warning().nospace() << "Properties::GetAll(ContactSearchChannel) failed "
+ "with " << reply.error().name() << ": " << reply.error().message();
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureCore, false,
+ reply.error());
+ }
+
+ watcher->deleteLater();
+}
+
+void ContactSearchChannel::gotSearchState(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QVariant> reply = *watcher;
+
+ if (!reply.isError()) {
+ mPriv->searchState = qdbus_cast<uint>(reply.value());
+
+ debug() << "Got reply to Properties::Get(SearchState)";
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureCore, true);
+ } else {
+ warning().nospace() << "Properties::Get(SearchState) failed "
+ "with " << reply.error().name() << ": " << reply.error().message();
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureCore, false,
+ reply.error());
+ }
+
+ watcher->deleteLater();
+}
+
+void ContactSearchChannel::onSearchStateChanged(uint state, const QString &error,
+ const QVariantMap &details)
+{
+ mPriv->searchStateChangeQueue.enqueue(Private::SearchStateChangeInfo(state, error, details));
+ mPriv->signalsQueue.enqueue(&Private::processSearchStateChangeQueue);
+ mPriv->processSignalsQueue();
+}
+
+void ContactSearchChannel::onSearchResultReceived(const ContactSearchResultMap &result)
+{
+ mPriv->searchResultQueue.enqueue(result);
+ mPriv->signalsQueue.enqueue(&Private::processSearchResultQueue);
+ mPriv->processSignalsQueue();
+}
+
+void ContactSearchChannel::gotSearchResultContacts(PendingOperation *op)
+{
+ PendingContacts *pc = qobject_cast<PendingContacts *>(op);
+
+ const ContactSearchResultMap &result = mPriv->searchResultQueue.dequeue();
+
+ if (!pc->isValid()) {
+ warning().nospace() << "Getting search result contacts "
+ "failed with " << pc->errorName() << ":" <<
+ pc->errorMessage() << ". Ignoring search result";
+ mPriv->processingSignalsQueue = false;
+ mPriv->processSignalsQueue();
+ return;
+ }
+
+ const QList<ContactPtr> &contacts = pc->contacts();
+ Q_ASSERT(result.count() == contacts.count());
+
+ SearchResult ret;
+ uint i = 0;
+ for (ContactSearchResultMap::const_iterator it = result.constBegin();
+ it != result.constEnd();
+ ++it, ++i) {
+ ret.insert(contacts.at(i), Contact::InfoFields(it.value()));
+ }
+ emit searchResultReceived(ret);
+
+ mPriv->processingSignalsQueue = false;
+ mPriv->processSignalsQueue();
+}
+
+/**
+ * \fn void ContactSearchChannel::searchStateChanged(Tp::ChannelContactSearchState state,
+ * const QString &errorName,
+ * const Tp::ContactSearchChannel::SearchStateChangeDetails &details)
+ *
+ * Emitted when the value of searchState() changes.
+ *
+ * \param state The new state.
+ * \param errorName The name of the error if any.
+ * \param details The details for the state change.
+ * \sa searchState()
+ */
+
+/**
+ * \fn void ContactSearchChannel::searchResultReceived(
+ * const Tp::ContactSearchChannel::SearchResult &result)
+ *
+ * Emitted when a result for a search is received. It can be emitted multiple times
+ * until the searchState() goes to #ChannelContactSearchStateCompleted or
+ * #ChannelContactSearchStateFailed.
+ *
+ * \param result The search result.
+ * \sa searchState()
+ */
+
+} // Tp
diff --git a/TelepathyQt/contact-search-channel.h b/TelepathyQt/contact-search-channel.h
new file mode 100644
index 00000000..29c8424c
--- /dev/null
+++ b/TelepathyQt/contact-search-channel.h
@@ -0,0 +1,114 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_contact_search_channel_h_HEADER_GUARD_
+#define _TelepathyQt_contact_search_channel_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Channel>
+#include <TelepathyQt/Contact>
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+class TP_QT_EXPORT ContactSearchChannel : public Channel
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(ContactSearchChannel)
+
+public:
+ static const Feature FeatureCore;
+
+ class SearchStateChangeDetails
+ {
+ public:
+ SearchStateChangeDetails();
+ SearchStateChangeDetails(const SearchStateChangeDetails &other);
+ ~SearchStateChangeDetails();
+
+ bool isValid() const { return mPriv.constData() != 0; }
+
+ SearchStateChangeDetails &operator=(const SearchStateChangeDetails &other);
+
+ bool hasDebugMessage() const { return allDetails().contains(QLatin1String("debug-message")); }
+ QString debugMessage() const { return qdbus_cast<QString>(allDetails().value(QLatin1String("debug-message"))); }
+
+ QVariantMap allDetails() const;
+
+ private:
+ friend class ContactSearchChannel;
+
+ TP_QT_NO_EXPORT SearchStateChangeDetails(const QVariantMap &details);
+
+ struct Private;
+ friend struct Private;
+ QSharedDataPointer<Private> mPriv;
+ };
+
+ typedef QHash<ContactPtr, Contact::InfoFields> SearchResult;
+
+ static ContactSearchChannelPtr create(const ConnectionPtr &connection,
+ const QString &objectPath, const QVariantMap &immutableProperties);
+
+ virtual ~ContactSearchChannel();
+
+ ChannelContactSearchState searchState() const;
+ uint limit() const;
+ QStringList availableSearchKeys() const;
+ QString server() const;
+
+ PendingOperation *search(const QString &searchKey, const QString &searchTerm);
+ PendingOperation *search(const ContactSearchMap &searchTerms);
+ void continueSearch();
+ void stopSearch();
+
+Q_SIGNALS:
+ void searchStateChanged(Tp::ChannelContactSearchState state, const QString &errorName,
+ const Tp::ContactSearchChannel::SearchStateChangeDetails &details);
+ void searchResultReceived(const Tp::ContactSearchChannel::SearchResult &result);
+
+protected:
+ ContactSearchChannel(const ConnectionPtr &connection, const QString &objectPath,
+ const QVariantMap &immutableProperties, const Feature &coreFeature);
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void gotProperties(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void gotSearchState(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void onSearchStateChanged(uint state, const QString &error, const QVariantMap &details);
+ TP_QT_NO_EXPORT void onSearchResultReceived(const Tp::ContactSearchResultMap &result);
+ TP_QT_NO_EXPORT void gotSearchResultContacts(Tp::PendingOperation *op);
+
+private:
+ class PendingSearch;
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/contact.cpp b/TelepathyQt/contact.cpp
new file mode 100644
index 00000000..64e6601d
--- /dev/null
+++ b/TelepathyQt/contact.cpp
@@ -0,0 +1,1352 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/Contact>
+
+#include "TelepathyQt/_gen/contact.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/AvatarData>
+#include <TelepathyQt/Connection>
+#include <TelepathyQt/ConnectionCapabilities>
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/ContactCapabilities>
+#include <TelepathyQt/ContactManager>
+#include <TelepathyQt/LocationInfo>
+#include <TelepathyQt/PendingContactInfo>
+#include <TelepathyQt/PendingVoid>
+#include <TelepathyQt/Presence>
+#include <TelepathyQt/ReferencedHandles>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT Contact::Private
+{
+ Private(Contact *parent, ContactManager *manager,
+ const ReferencedHandles &handle)
+ : parent(parent),
+ manager(manager),
+ handle(handle),
+ caps(manager->supportedFeatures().contains(Contact::FeatureCapabilities) ?
+ ContactCapabilities(true) : ContactCapabilities(
+ manager->connection()->capabilities().allClassSpecs(), false)),
+ isContactInfoKnown(false), isAvatarTokenKnown(false),
+ subscriptionState(SubscriptionStateUnknown),
+ publishState(SubscriptionStateUnknown),
+ blocked(false)
+ {
+ }
+
+ void updateAvatarData();
+
+ Contact *parent;
+
+ QWeakPointer<ContactManager> manager;
+ ReferencedHandles handle;
+ QString id;
+
+ Features requestedFeatures;
+ Features actualFeatures;
+
+ QString alias;
+ Presence presence;
+ ContactCapabilities caps;
+ LocationInfo location;
+
+ bool isContactInfoKnown;
+ InfoFields info;
+
+ bool isAvatarTokenKnown;
+ QString avatarToken;
+ AvatarData avatarData;
+
+ SubscriptionState subscriptionState;
+ SubscriptionState publishState;
+ QString publishStateMessage;
+ bool blocked;
+
+ QSet<QString> groups;
+};
+
+void Contact::Private::updateAvatarData()
+{
+ /* If token is NULL, it means that CM doesn't know the token. In that case we
+ * have to request the avatar data to get the token. This happens with XMPP
+ * for offline contacts. We don't want to bypass the avatar cache, so we won't
+ * update avatar. */
+ if (avatarToken.isNull()) {
+ return;
+ }
+
+ /* If token is empty (""), it means the contact has no avatar. */
+ if (avatarToken.isEmpty()) {
+ debug() << "Contact" << parent->id() << "has no avatar";
+ avatarData = AvatarData();
+ emit parent->avatarDataChanged(avatarData);
+ return;
+ }
+
+ parent->manager()->requestContactAvatars(QList<ContactPtr>() << ContactPtr(parent));
+}
+
+struct TP_QT_NO_EXPORT Contact::InfoFields::Private : public QSharedData
+{
+ Private(const ContactInfoFieldList &allFields)
+ : allFields(allFields) {}
+
+ ContactInfoFieldList allFields;
+};
+
+/**
+ * \class Contact::InfoFields
+ * \ingroup clientconn
+ * \headerfile TelepathyQt/contact.h <TelepathyQt/Contact>
+ *
+ * \brief The Contact::InfoFields class represents the information of a
+ * Telepathy contact.
+ */
+
+/**
+ * Construct a info fields instance with the given fields. The instance will indicate that
+ * it is valid.
+ */
+Contact::InfoFields::InfoFields(const ContactInfoFieldList &allFields)
+ : mPriv(new Private(allFields))
+{
+}
+
+/**
+ * Constructs a new invalid InfoFields instance.
+ */
+Contact::InfoFields::InfoFields()
+{
+}
+
+/**
+ * Copy constructor.
+ */
+Contact::InfoFields::InfoFields(const Contact::InfoFields &other)
+ : mPriv(other.mPriv)
+{
+}
+
+/**
+ * Class destructor.
+ */
+Contact::InfoFields::~InfoFields()
+{
+}
+
+/**
+ * Assignment operator.
+ */
+Contact::InfoFields &Contact::InfoFields::operator=(const Contact::InfoFields &other)
+{
+ this->mPriv = other.mPriv;
+ return *this;
+}
+
+/**
+ * Return a list containing all fields whose name are \a name.
+ *
+ * \param name The name used to match the fields.
+ * \return A list of ContactInfoField objects.
+ */
+ContactInfoFieldList Contact::InfoFields::fields(const QString &name) const
+{
+ if (!isValid()) {
+ return ContactInfoFieldList();
+ }
+
+ ContactInfoFieldList ret;
+ foreach (const ContactInfoField &field, mPriv->allFields) {
+ if (field.fieldName == name) {
+ ret.append(field);
+ }
+ }
+ return ret;
+}
+
+/**
+ * Return a list containing all fields describing the contact information.
+ *
+ * \return The contact information as a list of ContactInfoField objects.
+ */
+ContactInfoFieldList Contact::InfoFields::allFields() const
+{
+ return isValid() ? mPriv->allFields : ContactInfoFieldList();
+}
+
+/**
+ * \class Contact
+ * \ingroup clientconn
+ * \headerfile TelepathyQt/contact.h <TelepathyQt/Contact>
+ *
+ * \brief The Contact class represents a Telepathy contact.
+ *
+ * The accessor functions on this object (id(), alias(), and so on) don't make any D-Bus calls;
+ * instead, they return/use values cached from a previous introspection run.
+ * The introspection process populates their values in the most efficient way possible based on what
+ * the service implements.
+ *
+ * To avoid unnecessary D-Bus traffic, some accessors only return valid
+ * information after specific features have been enabled.
+ * For instance, to retrieve the contact avatar token, it is necessary to
+ * enable the feature Contact::FeatureAvatarToken.
+ * See the individual methods descriptions for more details.
+ *
+ * Contact features can be enabled by constructing a ContactFactory and enabling
+ * the desired features, and passing it to AccountManager, Account or ClientRegistrar
+ * when creating them as appropriate. However, if a particular
+ * feature is only ever used in a specific circumstance, such as an user opening
+ * some settings dialog separate from the general view of the application,
+ * features can be later enabled as needed by calling ContactManager::upgradeContacts() with the
+ * additional features, and waiting for the resulting PendingOperation to finish.
+ *
+ * As an addition to accessors, signals are emitted to indicate that properties have
+ * changed, for example aliasChanged(), avatarTokenChanged(), etc.
+ *
+ * See \ref async_model, \ref shared_ptr
+ */
+
+/**
+ * Feature used in order to access contact alias info.
+ *
+ * See alias specific methods' documentation for more details.
+ *
+ * \sa alias(), aliasChanged()
+ */
+const Feature Contact::FeatureAlias = Feature(QLatin1String(Contact::staticMetaObject.className()), 0, false);
+
+/**
+ * Feature used in order to access contact avatar data info.
+ *
+ * Enabling this feature will also enable FeatureAvatarToken.
+ *
+ * See avatar data specific methods' documentation for more details.
+ *
+ * \sa avatarData(), avatarDataChanged()
+ */
+const Feature Contact::FeatureAvatarData = Feature(QLatin1String(Contact::staticMetaObject.className()), 1, false);
+
+/**
+ * Feature used in order to access contact avatar token info.
+ *
+ * See avatar token specific methods' documentation for more details.
+ *
+ * \sa isAvatarTokenKnown(), avatarToken(), avatarTokenChanged()
+ */
+const Feature Contact::FeatureAvatarToken = Feature(QLatin1String(Contact::staticMetaObject.className()), 2, false);
+
+/**
+ * Feature used in order to access contact capabilities info.
+ *
+ * See capabilities specific methods' documentation for more details.
+ *
+ * \sa capabilities(), capabilitiesChanged()
+ */
+const Feature Contact::FeatureCapabilities = Feature(QLatin1String(Contact::staticMetaObject.className()), 3, false);
+
+/**
+ * Feature used in order to access contact info fields.
+ *
+ * See info fields specific methods' documentation for more details.
+ *
+ * \sa infoFields(), infoFieldsChanged()
+ */
+const Feature Contact::FeatureInfo = Feature(QLatin1String(Contact::staticMetaObject.className()), 4, false);
+
+/**
+ * Feature used in order to access contact location info.
+ *
+ * See location specific methods' documentation for more details.
+ *
+ * \sa location(), locationUpdated()
+ */
+const Feature Contact::FeatureLocation = Feature(QLatin1String(Contact::staticMetaObject.className()), 5, false);
+
+/**
+ * Feature used in order to access contact presence info.
+ *
+ * See presence specific methods' documentation for more details.
+ *
+ * \sa presence(), presenceChanged()
+ */
+const Feature Contact::FeatureSimplePresence = Feature(QLatin1String(Contact::staticMetaObject.className()), 6, false);
+
+/**
+ * Feature used in order to access contact roster groups.
+ *
+ * See roster groups specific methods' documentation for more details.
+ *
+ * \sa groups(), addedToGroup(), removedFromGroup()
+ */
+const Feature Contact::FeatureRosterGroups = Feature(QLatin1String(Contact::staticMetaObject.className()), 7, false);
+
+/**
+ * Construct a new Contact object.
+ *
+ * \param manager ContactManager owning this contact.
+ * \param handle The contact handle.
+ * \param requestedFeatures The contact requested features.
+ * \param attributes The contact attributes.
+ */
+Contact::Contact(ContactManager *manager, const ReferencedHandles &handle,
+ const Features &requestedFeatures, const QVariantMap &attributes)
+ : Object(),
+ mPriv(new Private(this, manager, handle))
+{
+ mPriv->requestedFeatures.unite(requestedFeatures);
+ mPriv->id = qdbus_cast<QString>(attributes[
+ QLatin1String(TELEPATHY_INTERFACE_CONNECTION "/contact-id")]);
+}
+
+/**
+ * Class destructor.
+ */
+Contact::~Contact()
+{
+ debug() << "Contact" << id() << "destroyed";
+ delete mPriv;
+}
+
+/**
+ * Return the contact nanager owning this contact.
+ *
+ * \return A pointer to the ContactManager object.
+ */
+ContactManagerPtr Contact::manager() const
+{
+ return ContactManagerPtr(mPriv->manager);
+}
+
+/**
+ * Return the handle of this contact.
+ *
+ * \return The handle as a ReferencedHandles object.
+ */
+ReferencedHandles Contact::handle() const
+{
+ return mPriv->handle;
+}
+
+/**
+ * Return the identifier of this contact.
+ *
+ * \return The identifier.
+ */
+QString Contact::id() const
+{
+ return mPriv->id;
+}
+
+/**
+ * Return the features requested on this contact.
+ *
+ * \return The requested features as a set of Feature objects.
+ */
+Features Contact::requestedFeatures() const
+{
+ return mPriv->requestedFeatures;
+}
+
+/**
+ * Return the features that are actually enabled on this contact.
+ *
+ * \return The actual features as a set of Feature objects.
+ */
+Features Contact::actualFeatures() const
+{
+ return mPriv->actualFeatures;
+}
+
+/**
+ * Return the alias of this contact.
+ *
+ * Change notification is via the aliasChanged() signal.
+ *
+ * This method requires Contact::FeatureAlias to be ready.
+ *
+ * \return The alias.
+ */
+QString Contact::alias() const
+{
+ if (!mPriv->requestedFeatures.contains(FeatureAlias)) {
+ warning() << "Contact::alias() used on" << this
+ << "for which FeatureAlias hasn't been requested - returning id";
+ return id();
+ }
+
+ return mPriv->alias;
+}
+
+/**
+ * Return whether the avatar token of this contact is known.
+ *
+ * This method requires Contact::FeatureAvatarToken to be ready.
+ *
+ * \return \c true if the avatar token is known, \c false otherwise.
+ * \sa avatarToken()
+ */
+bool Contact::isAvatarTokenKnown() const
+{
+ if (!mPriv->requestedFeatures.contains(FeatureAvatarToken)) {
+ warning() << "Contact::isAvatarTokenKnown() used on" << this
+ << "for which FeatureAvatarToken hasn't been requested - returning false";
+ return false;
+ }
+
+ return mPriv->isAvatarTokenKnown;
+}
+
+/**
+ * Return the avatar token for this contact.
+ *
+ * Change notification is via the avatarTokenChanged() signal.
+ *
+ * This method requires Contact::FeatureAvatarToken to be ready.
+ *
+ * \return The avatar token.
+ * \sa isAvatarTokenKnown(), avatarTokenChanged(), avatarData()
+ */
+QString Contact::avatarToken() const
+{
+ if (!mPriv->requestedFeatures.contains(FeatureAvatarToken)) {
+ warning() << "Contact::avatarToken() used on" << this
+ << "for which FeatureAvatarToken hasn't been requested - returning \"\"";
+ return QString();
+ } else if (!isAvatarTokenKnown()) {
+ warning() << "Contact::avatarToken() used on" << this
+ << "for which the avatar token is not (yet) known - returning \"\"";
+ return QString();
+ }
+
+ return mPriv->avatarToken;
+}
+
+/**
+ * Return the actual avatar for this contact.
+ *
+ * Change notification is via the avatarDataChanged() signal.
+ *
+ * This method requires Contact::FeatureAvatarData to be ready.
+ *
+ * \return The avatar as an AvatarData object.
+ * \sa avatarDataChanged(), avatarToken()
+ */
+AvatarData Contact::avatarData() const
+{
+ if (!mPriv->requestedFeatures.contains(FeatureAvatarData)) {
+ warning() << "Contact::avatarData() used on" << this
+ << "for which FeatureAvatarData hasn't been requested - returning \"\"";
+ return AvatarData();
+ }
+
+ return mPriv->avatarData;
+}
+
+/**
+ * Start a request to retrieve the avatar for this contact.
+ *
+ * Force the request of the avatar data. This method returns directly, emitting
+ * avatarTokenChanged() and avatarDataChanged() signals once the token and data are
+ * fetched from the server.
+ *
+ * This is only useful if the avatar token is unknown; see isAvatarTokenKnown().
+ * It happens in the case of offline XMPP contacts, because the server does not
+ * send the token for them and an explicit request of the avatar data is needed.
+ *
+ * This method requires Contact::FeatureAvatarData to be ready.
+ *
+ * \sa avatarData(), avatarDataChanged(), avatarToken(), avatarTokenChanged()
+ */
+void Contact::requestAvatarData()
+{
+ if (!mPriv->requestedFeatures.contains(FeatureAvatarData)) {
+ warning() << "Contact::requestAvatarData() used on" << this
+ << "for which FeatureAvatarData hasn't been requested - returning \"\"";
+ return;
+ }
+
+ return manager()->requestContactAvatars(QList<ContactPtr>() << ContactPtr(this));
+}
+
+/**
+ * Return the actual presence of this contact.
+ *
+ * Change notification is via the presenceChanged() signal.
+ *
+ * This method requires Contact::FeatureSimplePresence to be ready.
+ *
+ * \return The presence as a Presence object.
+ */
+Presence Contact::presence() const
+{
+ if (!mPriv->requestedFeatures.contains(FeatureSimplePresence)) {
+ warning() << "Contact::presence() used on" << this
+ << "for which FeatureSimplePresence hasn't been requested - returning Unknown";
+ return Presence();
+ }
+
+ return mPriv->presence;
+}
+
+/**
+ * Return the capabilities for this contact.
+ *
+ * User interfaces can use this information to show or hide UI components.
+ *
+ * If ContactManager::supportedFeatures() contains Contact::FeatureCapabilities,
+ * the returned object will be a ContactCapabilities object, where
+ * CapabilitiesBase::isSpecificToContact() will be \c true; if that feature
+ * isn't present, this returned object is the subset of
+ * Contact::manager()::connection()::capabilities()
+ * and CapabilitiesBase::isSpecificToContact() will be \c false.
+ *
+ * Change notification is via the capabilitiesChanged() signal.
+ *
+ * This method requires Contact::FeatureCapabilities to be ready.
+ *
+ * @return An object representing the contact capabilities.
+ */
+ContactCapabilities Contact::capabilities() const
+{
+ if (!mPriv->requestedFeatures.contains(FeatureCapabilities)) {
+ warning() << "Contact::capabilities() used on" << this
+ << "for which FeatureCapabilities hasn't been requested - returning 0";
+ return ContactCapabilities(false);
+ }
+
+ return mPriv->caps;
+}
+
+/**
+ * Return the location for this contact.
+ *
+ * Change notification is via the locationUpdated() signal.
+ *
+ * This method requires Contact::FeatureLocation to be ready.
+ *
+ * \return The contact location as a LocationInfo object.
+ */
+LocationInfo Contact::location() const
+{
+ if (!mPriv->requestedFeatures.contains(FeatureLocation)) {
+ warning() << "Contact::location() used on" << this
+ << "for which FeatureLocation hasn't been requested - returning 0";
+ return LocationInfo();
+ }
+
+ return mPriv->location;
+}
+
+/**
+ * Return whether the info card for this contact has been received.
+ *
+ * With some protocols (notably XMPP) information is not pushed from the server
+ * and must be requested explicitely using refreshInfo() or requestInfo(). This
+ * method can be used to know if the information is received from the server
+ * or if an explicit request is needed.
+ *
+ * This method requires Contacat::FeatureInfo to be ready.
+ *
+ * \return \c true if the information is known; \c false otherwise.
+ */
+bool Contact::isContactInfoKnown() const
+{
+ if (!mPriv->requestedFeatures.contains(FeatureInfo)) {
+ warning() << "Contact::isContactInfoKnown() used on" << this
+ << "for which FeatureInfo hasn't been requested - returning false";
+ return false;
+ }
+
+ return mPriv->isContactInfoKnown;
+}
+
+/**
+ * Return the information for this contact.
+ *
+ * Note that this method only return cached information. In order to refresh the
+ * information use refreshInfo().
+ *
+ * Change notification is via the infoFieldsChanged() signal.
+ *
+ * This method requires Contact::FeatureInfo to be ready.
+ *
+ * \return The contact info as a Contact::InfoFields object.
+ */
+Contact::InfoFields Contact::infoFields() const
+{
+ if (!mPriv->requestedFeatures.contains(FeatureInfo)) {
+ warning() << "Contact::infoFields() used on" << this
+ << "for which FeatureInfo hasn't been requested - returning empty "
+ "InfoFields";
+ return InfoFields();
+ }
+
+ return mPriv->info;
+}
+
+/**
+ * Refresh information for the given contact.
+ *
+ * Once the information is retrieved infoFieldsChanged() will be emitted.
+ *
+ * This method requires Contact::FeatureInfo to be ready.
+ *
+ * \return A PendingOperation, which will emit PendingOperation::finished
+ * when the call has finished.
+ * \sa infoFieldsChanged()
+ */
+PendingOperation *Contact::refreshInfo()
+{
+ ConnectionPtr conn = manager()->connection();
+ if (!mPriv->requestedFeatures.contains(FeatureInfo)) {
+ warning() << "Contact::refreshInfo() used on" << this
+ << "for which FeatureInfo hasn't been requested - failing";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("FeatureInfo needs to be ready in order to "
+ "use this method"),
+ ContactPtr(this));
+ }
+
+ return manager()->refreshContactInfo(QList<ContactPtr>() << ContactPtr(this));
+}
+
+/**
+ * Start a request to retrieve the information for this contact.
+ *
+ * This method is useful for UIs that don't care about notification of changes
+ * in the contact information but want to show the contact information
+ * (e.g. right-click on a contact and show the contact info).
+ *
+ * \return A PendingContactInfo, which will emit PendingContactInfo::finished
+ * when the information has been retrieved or an error occurred.
+ */
+PendingContactInfo *Contact::requestInfo()
+{
+ return new PendingContactInfo(ContactPtr(this));
+}
+
+/**
+ * Return whether the presence subscription state of this contact is known.
+ *
+ * \return \c true if the presence subscription state is known, \c false otherwise.
+ * \sa subscriptionState(), isSubscriptionRejected()
+ */
+bool Contact::isSubscriptionStateKnown() const
+{
+ return mPriv->subscriptionState != SubscriptionStateUnknown;
+}
+
+/**
+ * Return whether a request to see this contact's presence was denied.
+ *
+ * \return \c if the a request to see the presence subscription was denied, \c false otherwise.
+ * \sa isSubscriptionStateKnown(), subscriptionState()
+ */
+bool Contact::isSubscriptionRejected() const
+{
+ return mPriv->subscriptionState == SubscriptionStateRemovedRemotely;
+}
+
+/**
+ * Return the presence subscription state of this contact (i.e. whether the local user can retrieve
+ * information about this contact's presence).
+ *
+ * \return The presence subscription state as Contact::PresenceState.
+ * \sa isSubscriptionStateKnown(), isSubscriptionRejected()
+ */
+Contact::PresenceState Contact::subscriptionState() const
+{
+ return subscriptionStateToPresenceState(mPriv->subscriptionState);
+}
+
+/**
+ * Return whether the presence publish state of this contact is known.
+ *
+ * \return \c true if the presence publish state is known, \c false otherwise.
+ * \sa publishState(), isPublishCancelled()
+ */
+bool Contact::isPublishStateKnown() const
+{
+ return mPriv->publishState != SubscriptionStateUnknown;
+}
+
+/**
+ * Return whether a request to publish presence information to this contact was cancelled.
+ *
+ * \return \c true if a request to publish presence information was cancelled,
+ * \c false otherwise.
+ * \sa isPublishStateKnown(), publishState()
+ */
+bool Contact::isPublishCancelled() const
+{
+ return mPriv->publishState == SubscriptionStateRemovedRemotely;
+}
+
+/**
+ * Return the presence publish state of this contact (i.e. whether this contact can retrieve
+ * information about the local user's presence).
+ *
+ * \return The presence publish state as Contact::PresenceState.
+ * \sa isSubscriptionStateKnown(), isSubscriptionRejected()
+ */
+Contact::PresenceState Contact::publishState() const
+{
+ return subscriptionStateToPresenceState(mPriv->publishState);
+}
+
+/**
+ * If the publishState() is Contact::PresenceStateAsk, return an optional message that was sent
+ * by the contact asking to receive the local user's presence; omitted if none was given.
+ *
+ * \return The message that was sent by the contact asking to receive the local user's presence.
+ * \sa publishState()
+ */
+QString Contact::publishStateMessage() const
+{
+ return mPriv->publishStateMessage;
+}
+
+/**
+ * Start a request that this contact allow the local user to subscribe to their presence (i.e. that
+ * this contact's subscribe attribute becomes Contact::PresenceStateYes)
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the request has been made.
+ * \sa subscriptionState(), removePresenceSubscription()
+ */
+PendingOperation *Contact::requestPresenceSubscription(const QString &message)
+{
+ return manager()->requestPresenceSubscription(QList<ContactPtr>() << ContactPtr(this), message);
+}
+
+/**
+ * Start a request for the local user to stop receiving presence from this contact.
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the request has been made.
+ * \sa subscriptionState(), requestPresenceSubscription()
+ */
+PendingOperation *Contact::removePresenceSubscription(const QString &message)
+{
+ return manager()->removePresenceSubscription(QList<ContactPtr>() << ContactPtr(this), message);
+}
+
+/**
+ * Start a request to authorize this contact's request to see the local user presence
+ * (i.e. that this contact publish attribute becomes Contact::PresenceStateYes).
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the request has been made.
+ * \sa publishState(), removePresencePublication()
+ */
+PendingOperation *Contact::authorizePresencePublication(const QString &message)
+{
+ return manager()->authorizePresencePublication(QList<ContactPtr>() << ContactPtr(this), message);
+}
+
+/**
+ * Start a request for the local user to stop sending presence to this contact.
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the request has been made.
+ * \sa publishState(), authorizePresencePublication()
+ */
+PendingOperation *Contact::removePresencePublication(const QString &message)
+{
+ return manager()->removePresencePublication(QList<ContactPtr>() << ContactPtr(this), message);
+}
+
+/**
+ * Return whether this contact is blocked.
+ *
+ * Change notification is via the blockStatusChanged() signal.
+ *
+ * \return \c true if blocked, \c false otherwise.
+ * \sa block()
+ */
+bool Contact::isBlocked() const
+{
+ return mPriv->blocked;
+}
+
+/**
+ * \deprecated Use block() instead.
+ */
+PendingOperation *Contact::block(bool value)
+{
+ return value ? manager()->blockContacts(QList<ContactPtr>() << ContactPtr(this))
+ : manager()->unblockContacts(QList<ContactPtr>() << ContactPtr(this));
+}
+
+/**
+ * Block this contact. Blocked contacts cannot send messages to the user;
+ * depending on the protocol, blocking a contact may have other effects.
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when an attempt has been made to take the requested action.
+ * \sa blockAndReportAbuse(), unblock()
+ */
+PendingOperation *Contact::block()
+{
+ return manager()->blockContacts(QList<ContactPtr>() << ContactPtr(this));
+}
+
+/**
+ * Block this contact and additionally report abusive behaviour
+ * to the server.
+ *
+ * If reporting abusive behaviour is not supported by the protocol,
+ * this method has the same effect as block().
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when an attempt has been made to take the requested action.
+ * \sa ContactManager::canReportAbuse(), block(), unblock()
+ */
+PendingOperation *Contact::blockAndReportAbuse()
+{
+ return manager()->blockContactsAndReportAbuse(QList<ContactPtr>() << ContactPtr(this));
+}
+
+/**
+ * Unblock this contact.
+ *
+ * This method requires Connection::FeatureRoster to be ready.
+ *
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when an attempt has been made to take the requested action.
+ * \sa block(), blockAndReportAbuse()
+ */
+PendingOperation *Contact::unblock()
+{
+ return manager()->unblockContacts(QList<ContactPtr>() << ContactPtr(this));
+}
+
+/**
+ * Return the names of the user-defined roster groups to which the contact
+ * belongs.
+ *
+ * Change notification is via the addedToGroup() and removedFromGroup() signals.
+ *
+ * This method requires Connection::FeatureRosterGroups to be ready.
+ *
+ * \return A list of user-defined contact list groups names.
+ * \sa addToGroup(), removedFromGroup()
+ */
+QStringList Contact::groups() const
+{
+ return mPriv->groups.toList();
+}
+
+/**
+ * Attempt to add the contact to the user-defined contact list
+ * group named \a group.
+ *
+ * This method requires Connection::FeatureRosterGroups to be ready.
+ *
+ * \param group The group name.
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when an attempt has been made to to add the contact to the user-defined contact
+ * list group.
+ * \sa groups(), removeFromGroup()
+ */
+PendingOperation *Contact::addToGroup(const QString &group)
+{
+ return manager()->addContactsToGroup(group, QList<ContactPtr>() << ContactPtr(this));
+}
+
+/**
+ * Attempt to remove the contact from the user-defined contact list
+ * group named \a group.
+ *
+ * This method requires Connection::FeatureRosterGroups to be ready.
+ *
+ * \param group The group name.
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when an attempt has been made to to remote the contact to the user-defined contact
+ * list group.
+ * \sa groups(), addToGroup()
+ */
+PendingOperation *Contact::removeFromGroup(const QString &group)
+{
+ return manager()->removeContactsFromGroup(group, QList<ContactPtr>() << ContactPtr(this));
+}
+
+void Contact::augment(const Features &requestedFeatures, const QVariantMap &attributes)
+{
+ mPriv->requestedFeatures.unite(requestedFeatures);
+
+ mPriv->id = qdbus_cast<QString>(attributes[
+ QLatin1String(TELEPATHY_INTERFACE_CONNECTION "/contact-id")]);
+
+ if (attributes.contains(TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_LIST +
+ QLatin1String("/subscribe"))) {
+ uint subscriptionState = qdbus_cast<uint>(attributes.value(
+ TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_LIST + QLatin1String("/subscribe")));
+ setSubscriptionState((SubscriptionState) subscriptionState);
+ }
+
+ if (attributes.contains(TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_LIST +
+ QLatin1String("/publish"))) {
+ uint publishState = qdbus_cast<uint>(attributes.value(
+ TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_LIST + QLatin1String("/publish")));
+ QString publishRequest = qdbus_cast<QString>(attributes.value(
+ TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_LIST + QLatin1String("/publish-request")));
+ setPublishState((SubscriptionState) publishState, publishRequest);
+ }
+
+ foreach (const Feature &feature, requestedFeatures) {
+ QString maybeAlias;
+ SimplePresence maybePresence;
+ RequestableChannelClassList maybeCaps;
+ QVariantMap maybeLocation;
+ ContactInfoFieldList maybeInfo;
+
+ if (feature == FeatureAlias) {
+ maybeAlias = qdbus_cast<QString>(attributes.value(
+ QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_ALIASING "/alias")));
+
+ if (!maybeAlias.isEmpty()) {
+ receiveAlias(maybeAlias);
+ } else if (mPriv->alias.isEmpty()) {
+ mPriv->alias = mPriv->id;
+ }
+ } else if (feature == FeatureAvatarData) {
+ if (manager()->supportedFeatures().contains(FeatureAvatarData)) {
+ mPriv->actualFeatures.insert(FeatureAvatarData);
+ mPriv->updateAvatarData();
+ }
+ } else if (feature == FeatureAvatarToken) {
+ if (attributes.contains(
+ QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_AVATARS "/token"))) {
+ receiveAvatarToken(qdbus_cast<QString>(attributes.value(
+ QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_AVATARS "/token"))));
+ } else {
+ if (manager()->supportedFeatures().contains(FeatureAvatarToken)) {
+ // AvatarToken being supported but not included in the mapping indicates
+ // that the avatar token is not known - however, the feature is working fine
+ mPriv->actualFeatures.insert(FeatureAvatarToken);
+ }
+ // In either case, the avatar token can't be known
+ mPriv->isAvatarTokenKnown = false;
+ mPriv->avatarToken = QLatin1String("");
+ }
+ } else if (feature == FeatureCapabilities) {
+ maybeCaps = qdbus_cast<RequestableChannelClassList>(attributes.value(
+ QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_CONTACT_CAPABILITIES "/capabilities")));
+
+ if (!maybeCaps.isEmpty()) {
+ receiveCapabilities(maybeCaps);
+ } else {
+ if (manager()->supportedFeatures().contains(FeatureCapabilities) &&
+ mPriv->requestedFeatures.contains(FeatureCapabilities)) {
+ // Capabilities being supported but not updated in the
+ // mapping indicates that the capabilities is not known -
+ // however, the feature is working fine.
+ mPriv->actualFeatures.insert(FeatureCapabilities);
+ }
+ }
+ } else if (feature == FeatureInfo) {
+ maybeInfo = qdbus_cast<ContactInfoFieldList>(attributes.value(
+ QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_CONTACT_INFO "/info")));
+
+ if (!maybeInfo.isEmpty()) {
+ receiveInfo(maybeInfo);
+ } else {
+ if (manager()->supportedFeatures().contains(FeatureInfo) &&
+ mPriv->requestedFeatures.contains(FeatureInfo)) {
+ // Info being supported but not updated in the
+ // mapping indicates that the info is not known -
+ // however, the feature is working fine
+ mPriv->actualFeatures.insert(FeatureInfo);
+ }
+ }
+ } else if (feature == FeatureLocation) {
+ maybeLocation = qdbus_cast<QVariantMap>(attributes.value(
+ QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_LOCATION "/location")));
+
+ if (!maybeLocation.isEmpty()) {
+ receiveLocation(maybeLocation);
+ } else {
+ if (manager()->supportedFeatures().contains(FeatureLocation) &&
+ mPriv->requestedFeatures.contains(FeatureLocation)) {
+ // Location being supported but not updated in the
+ // mapping indicates that the location is not known -
+ // however, the feature is working fine
+ mPriv->actualFeatures.insert(FeatureLocation);
+ }
+ }
+ } else if (feature == FeatureSimplePresence) {
+ maybePresence = qdbus_cast<SimplePresence>(attributes.value(
+ QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE "/presence")));
+
+ if (!maybePresence.status.isEmpty()) {
+ receiveSimplePresence(maybePresence);
+ } else {
+ mPriv->presence.setStatus(ConnectionPresenceTypeUnknown,
+ QLatin1String("unknown"), QLatin1String(""));
+ }
+ } else if (feature == FeatureRosterGroups) {
+ QStringList groups = qdbus_cast<QStringList>(attributes.value(
+ TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_GROUPS + QLatin1String("/groups")));
+ mPriv->groups = groups.toSet();
+ } else {
+ warning() << "Unknown feature" << feature << "encountered when augmenting Contact";
+ }
+ }
+}
+
+void Contact::receiveAlias(const QString &alias)
+{
+ if (!mPriv->requestedFeatures.contains(FeatureAlias)) {
+ return;
+ }
+
+ mPriv->actualFeatures.insert(FeatureAlias);
+
+ if (mPriv->alias != alias) {
+ mPriv->alias = alias;
+ emit aliasChanged(alias);
+ }
+}
+
+void Contact::receiveAvatarToken(const QString &token)
+{
+ setAvatarToken(token);
+
+ if (mPriv->actualFeatures.contains(FeatureAvatarData)) {
+ mPriv->updateAvatarData();
+ }
+}
+
+void Contact::setAvatarToken(const QString &token)
+{
+ if (!mPriv->requestedFeatures.contains(FeatureAvatarToken)) {
+ return;
+ }
+
+ mPriv->actualFeatures.insert(FeatureAvatarToken);
+
+ if (!mPriv->isAvatarTokenKnown || mPriv->avatarToken != token) {
+ mPriv->isAvatarTokenKnown = true;
+ mPriv->avatarToken = token;
+ emit avatarTokenChanged(mPriv->avatarToken);
+ }
+}
+
+void Contact::receiveAvatarData(const AvatarData &avatar)
+{
+ if (mPriv->avatarData.fileName != avatar.fileName) {
+ mPriv->avatarData = avatar;
+ emit avatarDataChanged(mPriv->avatarData);
+ }
+}
+
+void Contact::receiveSimplePresence(const SimplePresence &presence)
+{
+ if (!mPriv->requestedFeatures.contains(FeatureSimplePresence)) {
+ return;
+ }
+
+ mPriv->actualFeatures.insert(FeatureSimplePresence);
+
+ if (mPriv->presence.status() != presence.status ||
+ mPriv->presence.statusMessage() != presence.statusMessage) {
+ mPriv->presence.setStatus(presence);
+ emit presenceChanged(mPriv->presence);
+ }
+}
+
+void Contact::receiveCapabilities(const RequestableChannelClassList &caps)
+{
+ if (!mPriv->requestedFeatures.contains(FeatureCapabilities)) {
+ return;
+ }
+
+ mPriv->actualFeatures.insert(FeatureCapabilities);
+
+ if (mPriv->caps.allClassSpecs().bareClasses() != caps) {
+ mPriv->caps.updateRequestableChannelClasses(caps);
+ emit capabilitiesChanged(mPriv->caps);
+ }
+}
+
+void Contact::receiveLocation(const QVariantMap &location)
+{
+ if (!mPriv->requestedFeatures.contains(FeatureLocation)) {
+ return;
+ }
+
+ mPriv->actualFeatures.insert(FeatureLocation);
+
+ if (mPriv->location.allDetails() != location) {
+ mPriv->location.updateData(location);
+ emit locationUpdated(mPriv->location);
+ }
+}
+
+void Contact::receiveInfo(const ContactInfoFieldList &info)
+{
+ if (!mPriv->requestedFeatures.contains(FeatureInfo)) {
+ return;
+ }
+
+ mPriv->actualFeatures.insert(FeatureInfo);
+ mPriv->isContactInfoKnown = true;
+
+ if (mPriv->info.allFields() != info) {
+ mPriv->info = InfoFields(info);
+ emit infoFieldsChanged(mPriv->info);
+ }
+}
+
+Contact::PresenceState Contact::subscriptionStateToPresenceState(uint subscriptionState)
+{
+ switch (subscriptionState) {
+ case SubscriptionStateAsk:
+ return PresenceStateAsk;
+ case SubscriptionStateYes:
+ return PresenceStateYes;
+ default:
+ return PresenceStateNo;
+ }
+}
+
+void Contact::setSubscriptionState(SubscriptionState state)
+{
+ if (mPriv->subscriptionState == state) {
+ return;
+ }
+
+ mPriv->subscriptionState = state;
+
+ // FIXME (API/ABI break) remove signal with details
+ emit subscriptionStateChanged(subscriptionStateToPresenceState(state),
+ Channel::GroupMemberChangeDetails());
+
+ emit subscriptionStateChanged(subscriptionStateToPresenceState(state));
+}
+
+void Contact::setPublishState(SubscriptionState state, const QString &message)
+{
+ if (mPriv->publishState == state && mPriv->publishStateMessage == message) {
+ return;
+ }
+
+ mPriv->publishState = state;
+ mPriv->publishStateMessage = message;
+
+ // FIXME (API/ABI break) remove signal with details
+ QVariantMap detailsMap;
+ detailsMap.insert(QLatin1String("message"), message);
+ emit publishStateChanged(subscriptionStateToPresenceState(state),
+ Channel::GroupMemberChangeDetails(ContactPtr(), detailsMap));
+
+ emit publishStateChanged(subscriptionStateToPresenceState(state), message);
+}
+
+void Contact::setBlocked(bool value)
+{
+ if (mPriv->blocked == value) {
+ return;
+ }
+
+ mPriv->blocked = value;
+
+ // FIXME (API/ABI break) remove signal with details
+ emit blockStatusChanged(value, Channel::GroupMemberChangeDetails());
+
+ emit blockStatusChanged(value);
+}
+
+void Contact::setAddedToGroup(const QString &group)
+{
+ if (!mPriv->groups.contains(group)) {
+ mPriv->groups.insert(group);
+ emit addedToGroup(group);
+ }
+}
+
+void Contact::setRemovedFromGroup(const QString &group)
+{
+ if (mPriv->groups.remove(group)) {
+ emit removedFromGroup(group);
+ }
+}
+
+/**
+ * \fn void Contact::aliasChanged(const QString &alias)
+ *
+ * Emitted when the value of alias() changes.
+ *
+ * \param alias The new alias of this contact.
+ * \sa alias()
+ */
+
+/**
+ * \fn void Contact::avatarTokenChanged(const QString &avatarToken)
+ *
+ * Emitted when the value of avatarToken() changes.
+ *
+ * \param avatarToken The new avatar token of this contact.
+ * \sa avatarToken()
+ */
+
+/**
+ * \fn void Contact::avatarDataChanged(const QString &avatarToken)
+ *
+ * Emitted when the value of avatarData() changes.
+ *
+ * \param avatarData The new avatar of this contact.
+ * \sa avatarData()
+ */
+
+/**
+ * \fn void Contact::presenceChanged(const Tp::Presence &presence)
+ *
+ * Emitted when the value of presence() changes.
+ *
+ * \param presence The new presence of this contact.
+ * \sa presence()
+ */
+
+/**
+ * \fn void Contact::capabilitiesChanged(const Tp::ContactCapabilities &caps)
+ *
+ * Emitted when the value of capabilities() changes.
+ *
+ * \param caps The new capabilities of this contact.
+ * \sa capabilities()
+ */
+
+/**
+ * \fn void Contact::locationUpdated(const Tp::LocationInfo &location)
+ *
+ * Emitted when the value of location() changes.
+ *
+ * \param caps The new location of this contact.
+ * \sa location()
+ */
+
+/**
+ * \fn void Contact::infoFieldsChanged(const Tp::Contact::InfoFields &infoFields)
+ *
+ * Emitted when the value of infoFields() changes.
+ *
+ * \param InfoFields The new info of this contact.
+ * \sa infoFields()
+ */
+
+/**
+ * \fn void Contact::subscriptionStateChanged(Tp::Contact::PresenceState state)
+ *
+ * Emitted when the value of subscriptionState() changes.
+ *
+ * \param state The new subscription state of this contact.
+ * \sa subscriptionState()
+ */
+
+/**
+ * \fn void Contact::subscriptionStateChanged(Tp::Contact::PresenceState state,
+ * const Tp::Channel::GroupMemberChangeDetails &details)
+ *
+ * \deprecated Use subscriptionStateChanged(Tp::Contact::PresenceState state) instead.
+ */
+
+/**
+ * \fn void Contact::publishStateChanged(Tp::Contact::PresenceState state, const QString &message)
+ *
+ * Emitted when the value of publishState() changes.
+ *
+ * \param state The new publish state of this contact.
+ * \sa publishState()
+ */
+
+/**
+ * \fn void Contact::publishStateChanged(Tp::Contact::PresenceState state,
+ * const Tp::Channel::GroupMemberChangeDetails &details)
+ *
+ * \deprecated Use publishStateChanged(Tp::Contact::PresenceState state, const QString &message) instead.
+ */
+
+/**
+ * \fn void Contact::blockStatusChanged(bool blocked)
+ *
+ * Emitted when the value of isBlocked() changes.
+ *
+ * \param status The new block status of this contact.
+ * \sa isBlocked()
+ */
+
+/**
+ * \fn void Contact::blockStatusChanged(bool blocked,
+ * const Tp::Channel::GroupMemberChangeDetails &details)
+ *
+ * \deprecated Use blockStatusChanged(bool blocked) instead.
+ */
+
+/**
+ * \fn void Contact::addedToGroup(const QString &group)
+ *
+ * Emitted when this contact is added to \a group of the contact list.
+ *
+ * \param group The group name.
+ * \sa groups(), removedFromGroup()
+ */
+
+/**
+ * \fn void Contact::removedFromGroup(const QString &group)
+ *
+ * Emitted when this contact is removed from \a group of the contact list.
+ *
+ * \param group The group name.
+ * \sa groups(), addedToGroup()
+ */
+
+void Contact::connectNotify(const char *signalName)
+{
+ if (qstrcmp(signalName, SIGNAL(subscriptionStateChanged(Tp::Contact::PresenceState,Tp::Channel::GroupMemberChangeDetails))) == 0) {
+ warning() << "Connecting to deprecated signal subscriptionStateChanged(Tp::Contact::PresenceState,Tp::Channel::GroupMemberChangeDetails)";
+ } else if (qstrcmp(signalName, SIGNAL(publishStateChanged(Tp::Contact::PresenceState,Tp::Channel::GroupMemberChangeDetails))) == 0) {
+ warning() << "Connecting to deprecated signal publishStateChanged(Tp::Contact::PresenceState,Tp::Channel::GroupMemberChangeDetails)";
+ } else if (qstrcmp(signalName, SIGNAL(blockStatusChanged(bool,Tp::Channel::GroupMemberChangeDetails))) == 0) {
+ warning() << "Connecting to deprecated signal blockStatusChanged(bool,Tp::Channel::GroupMemberChangeDetails)";
+ }
+}
+
+} // Tp
diff --git a/TelepathyQt/contact.h b/TelepathyQt/contact.h
new file mode 100644
index 00000000..4d50f1e8
--- /dev/null
+++ b/TelepathyQt/contact.h
@@ -0,0 +1,246 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_contact_h_HEADER_GUARD_
+#define _TelepathyQt_contact_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Channel>
+#include <TelepathyQt/Feature>
+#include <TelepathyQt/Object>
+#include <TelepathyQt/Types>
+
+#include <QSet>
+#include <QVariantMap>
+
+namespace Tp
+{
+
+struct AvatarData;
+class ContactCapabilities;
+class LocationInfo;
+class ContactManager;
+class PendingContactInfo;
+class PendingOperation;
+class Presence;
+class ReferencedHandles;
+
+class TP_QT_EXPORT Contact : public Object
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(Contact)
+
+public:
+ static const Feature FeatureAlias;
+ static const Feature FeatureAvatarData;
+ static const Feature FeatureAvatarToken;
+ static const Feature FeatureCapabilities;
+ static const Feature FeatureInfo;
+ static const Feature FeatureLocation;
+ static const Feature FeatureSimplePresence;
+
+ enum PresenceState {
+ PresenceStateNo,
+ PresenceStateAsk,
+ PresenceStateYes
+ };
+
+ class InfoFields
+ {
+ public:
+ InfoFields();
+ InfoFields(const ContactInfoFieldList &fields);
+ InfoFields(const InfoFields &other);
+ ~InfoFields();
+
+ bool isValid() const { return mPriv.constData() != 0; }
+
+ InfoFields &operator=(const InfoFields &other);
+
+ ContactInfoFieldList fields(const QString &name) const;
+
+ ContactInfoFieldList allFields() const;
+
+ private:
+ struct Private;
+ friend struct Private;
+ QSharedDataPointer<Private> mPriv;
+ };
+
+ ~Contact();
+
+ ContactManagerPtr manager() const;
+
+ ReferencedHandles handle() const;
+
+ // TODO filter: exact, prefix, substring match
+ QString id() const;
+
+ Features requestedFeatures() const;
+ Features actualFeatures() const;
+
+ // TODO filter: exact, prefix, substring match
+ QString alias() const;
+
+ bool isAvatarTokenKnown() const;
+ QString avatarToken() const;
+ AvatarData avatarData() const;
+ void requestAvatarData();
+
+ /*
+ * TODO filter:
+ * - exact match of presence().type(), presence().status()
+ * - ANY 1 of a number of presence types/statuses
+ * - presence().type() greater or less than a set value
+ * - have/don't have presence().message() AND exact/prefix/substring
+ */
+ Presence presence() const;
+
+ // TODO filter: the same as Account filtering by caps
+ ContactCapabilities capabilities() const;
+
+ // TODO filter: is it available, how accurate, are they near me
+ LocationInfo location() const;
+
+ // TODO filter: having a specific field, having ANY field,
+ // (field: exact, contents: exact/prefix/substring)
+ bool isContactInfoKnown() const;
+ InfoFields infoFields() const;
+ PendingOperation *refreshInfo();
+ PendingContactInfo *requestInfo();
+
+ /*
+ * Filters on exact values of these, but also the "in your contact list at all or not" usecase
+ */
+ bool isSubscriptionStateKnown() const;
+ bool isSubscriptionRejected() const;
+ PresenceState subscriptionState() const;
+ bool isPublishStateKnown() const;
+ bool isPublishCancelled() const;
+ PresenceState publishState() const;
+ QString publishStateMessage() const;
+
+ PendingOperation *requestPresenceSubscription(const QString &message = QString());
+ PendingOperation *removePresenceSubscription(const QString &message = QString());
+ PendingOperation *authorizePresencePublication(const QString &message = QString());
+ PendingOperation *removePresencePublication(const QString &message = QString());
+
+ /*
+ * Filter on being blocked or not
+ */
+ bool isBlocked() const;
+ TP_QT_DEPRECATED PendingOperation *block(bool value);
+ PendingOperation *block();
+ PendingOperation *blockAndReportAbuse();
+ PendingOperation *unblock();
+
+ /*
+ * Filter on the groups they're in - to show a specific group only
+ *
+ * Also prefix/substring match on ANY of the groups of the contact
+ */
+ QStringList groups() const;
+ PendingOperation *addToGroup(const QString &group);
+ PendingOperation *removeFromGroup(const QString &group);
+
+Q_SIGNALS:
+ void aliasChanged(const QString &alias);
+
+ void avatarTokenChanged(const QString &avatarToken);
+ void avatarDataChanged(const Tp::AvatarData &avatarData);
+
+ void presenceChanged(const Tp::Presence &presence);
+
+ void capabilitiesChanged(const Tp::ContactCapabilities &caps);
+
+ void locationUpdated(const Tp::LocationInfo &location);
+
+ void infoFieldsChanged(const Tp::Contact::InfoFields &infoFields);
+
+ void subscriptionStateChanged(Tp::Contact::PresenceState state);
+ // deprecated
+ void subscriptionStateChanged(Tp::Contact::PresenceState state,
+ const Tp::Channel::GroupMemberChangeDetails &details);
+
+ void publishStateChanged(Tp::Contact::PresenceState state, const QString &message);
+ // deprecated
+ void publishStateChanged(Tp::Contact::PresenceState state,
+ const Tp::Channel::GroupMemberChangeDetails &details);
+
+ void blockStatusChanged(bool blocked);
+ // deprecated
+ void blockStatusChanged(bool blocked, const Tp::Channel::GroupMemberChangeDetails &details);
+
+ void addedToGroup(const QString &group);
+ void removedFromGroup(const QString &group);
+
+ // TODO: consider how the Renaming interface should work and map to Contacts
+ // I guess it would be something like:
+ // void renamedTo(Tp::ContactPtr)
+ // with that contact getting the same features requested as the current one. Or would we rather
+ // want to signal that change right away with a handle?
+
+protected:
+ Contact(ContactManager *manager, const ReferencedHandles &handle,
+ const Features &requestedFeatures, const QVariantMap &attributes);
+
+ virtual void augment(const Features &requestedFeatures, const QVariantMap &attributes);
+
+ // FIXME: (API/ABI break) Remove connectNotify
+ void connectNotify(const char *);
+
+private:
+ static const Feature FeatureRosterGroups;
+
+ TP_QT_NO_EXPORT void receiveAlias(const QString &alias);
+ TP_QT_NO_EXPORT void receiveAvatarToken(const QString &avatarToken);
+ TP_QT_NO_EXPORT void setAvatarToken(const QString &token);
+ TP_QT_NO_EXPORT void receiveAvatarData(const AvatarData &);
+ TP_QT_NO_EXPORT void receiveSimplePresence(const SimplePresence &presence);
+ TP_QT_NO_EXPORT void receiveCapabilities(const RequestableChannelClassList &caps);
+ TP_QT_NO_EXPORT void receiveLocation(const QVariantMap &location);
+ TP_QT_NO_EXPORT void receiveInfo(const ContactInfoFieldList &info);
+
+ TP_QT_NO_EXPORT static PresenceState subscriptionStateToPresenceState(uint subscriptionState);
+ TP_QT_NO_EXPORT void setSubscriptionState(SubscriptionState state);
+ TP_QT_NO_EXPORT void setPublishState(SubscriptionState state, const QString &message = QString());
+ TP_QT_NO_EXPORT void setBlocked(bool value);
+
+ TP_QT_NO_EXPORT void setAddedToGroup(const QString &group);
+ TP_QT_NO_EXPORT void setRemovedFromGroup(const QString &group);
+
+ struct Private;
+ friend class Connection;
+ friend class ContactFactory;
+ friend class ContactManager;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+Q_DECLARE_METATYPE(Tp::Contact::InfoFields);
+
+#endif
diff --git a/TelepathyQt/dbus-daemon.xml b/TelepathyQt/dbus-daemon.xml
new file mode 100644
index 00000000..675b879c
--- /dev/null
+++ b/TelepathyQt/dbus-daemon.xml
@@ -0,0 +1,80 @@
+<tp:spec
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+
+ <tp:title>D-Bus Daemon</tp:title>
+
+ <node name="/DBus_Daemon">
+ <interface name="org.freedesktop.DBus">
+ <method name="Hello">
+ <arg direction="out" type="s"/>
+ </method>
+ <method name="RequestName">
+ <arg direction="in" type="s" name="Name"/>
+ <arg direction="in" type="u" name="Flags"/>
+ <arg direction="out" type="u"/>
+ </method>
+ <method name="ReleaseName">
+ <arg direction="in" type="s" name="Name"/>
+ <arg direction="out" type="u"/>
+ </method>
+ <method name="StartServiceByName">
+ <arg direction="in" type="s" name="Service"/>
+ <arg direction="in" type="u" name="Flags"/>
+ <arg direction="out" type="u"/>
+ </method>
+ <method name="NameHasOwner">
+ <arg direction="in" type="s" name="Name_To_Check"/>
+ <arg direction="out" type="b"/>
+ </method>
+ <method name="ListNames">
+ <arg direction="out" type="as"/>
+ </method>
+ <method name="ListActivatableNames">
+ <arg direction="out" type="as"/>
+ </method>
+ <method name="AddMatch">
+ <arg direction="in" type="s" name="Rule"/>
+ </method>
+ <method name="RemoveMatch">
+ <arg direction="in" type="s" name="Rule"/>
+ </method>
+ <method name="GetNameOwner">
+ <arg direction="in" type="s" name="Name"/>
+ <arg direction="out" type="s"/>
+ </method>
+ <method name="ListQueuedOwners">
+ <arg direction="in" type="s" name="Name"/>
+ <arg direction="out" type="as"/>
+ </method>
+ <method name="GetConnectionUnixUser">
+ <arg direction="in" type="s" name="Connection_Name"/>
+ <arg direction="out" type="u"/>
+ </method>
+ <method name="GetConnectionUnixProcessID">
+ <arg direction="in" type="s" name="Connection_Name"/>
+ <arg direction="out" type="u"/>
+ </method>
+ <method name="GetConnectionSELinuxSecurityContext">
+ <arg direction="in" type="s" name="Connection_Name"/>
+ <arg direction="out" type="ay"/>
+ </method>
+ <method name="ReloadConfig">
+ </method>
+ <method name="GetId">
+ <arg direction="out" type="s"/>
+ </method>
+ <signal name="NameOwnerChanged">
+ <arg type="s" name="Name"/>
+ <arg type="s" name="Old_Owner"/>
+ <arg type="s" name="New_Owner"/>
+ </signal>
+ <signal name="NameLost">
+ <arg type="s" name="Name"/>
+ </signal>
+ <signal name="NameAcquired">
+ <arg type="s" name="Name"/>
+ </signal>
+ </interface>
+ </node>
+
+</tp:spec>
diff --git a/TelepathyQt/dbus-introspectable.xml b/TelepathyQt/dbus-introspectable.xml
new file mode 100644
index 00000000..4617f407
--- /dev/null
+++ b/TelepathyQt/dbus-introspectable.xml
@@ -0,0 +1,16 @@
+<tp:spec
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+
+ <tp:title>D-Bus introspectable object</tp:title>
+
+ <node name="/Introspectable">
+ <interface name="org.freedesktop.DBus.Introspectable">
+
+ <method name="Introspect">
+ <arg direction="out" type="s" name="XML_Data"/>
+ </method>
+
+ </interface>
+ </node>
+
+</tp:spec>
diff --git a/TelepathyQt/dbus-peer.xml b/TelepathyQt/dbus-peer.xml
new file mode 100644
index 00000000..54b25a28
--- /dev/null
+++ b/TelepathyQt/dbus-peer.xml
@@ -0,0 +1,19 @@
+<tp:spec
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+
+ <tp:title>D-Bus peer</tp:title>
+
+ <node name="/Peer">
+ <interface name="org.freedesktop.DBus.Peer" tp:implement-service="no">
+
+ <method name="Ping">
+ </method>
+
+ <method name="GetMachineId">
+ <arg direction="out" type="s" name="Machine_UUID"/>
+ </method>
+
+ </interface>
+ </node>
+
+</tp:spec>
diff --git a/TelepathyQt/dbus-properties.xml b/TelepathyQt/dbus-properties.xml
new file mode 100644
index 00000000..e76b3981
--- /dev/null
+++ b/TelepathyQt/dbus-properties.xml
@@ -0,0 +1,29 @@
+<tp:spec
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+
+ <tp:title>D-Bus Properties</tp:title>
+
+ <node name="/Properties">
+ <interface name="org.freedesktop.DBus.Properties">
+
+ <method name="Get">
+ <arg direction="in" type="s" name="Interface_Name"/>
+ <arg direction="in" type="s" name="Property_Name"/>
+ <arg direction="out" type="v" name="Value"/>
+ </method>
+
+ <method name="Set">
+ <arg direction="in" type="s" name="Interface_Name"/>
+ <arg direction="in" type="s" name="Property_Name"/>
+ <arg direction="in" type="v" name="Value"/>
+ </method>
+
+ <method name="GetAll">
+ <arg direction="in" type="s" name="Interface_Name"/>
+ <arg direction="out" type="a{sv}" name="Properties"/>
+ </method>
+
+ </interface>
+ </node>
+
+</tp:spec>
diff --git a/TelepathyQt/dbus-proxy-factory-internal.h b/TelepathyQt/dbus-proxy-factory-internal.h
new file mode 100644
index 00000000..d5fbdbb8
--- /dev/null
+++ b/TelepathyQt/dbus-proxy-factory-internal.h
@@ -0,0 +1,58 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 BUILDING_TP_QT
+#error "This file is a TpQt4 internal header not to be included by applications"
+#endif
+
+#include <QObject>
+#include <QPair>
+#include <QString>
+
+#include <TelepathyQt/SharedPtr>
+
+namespace Tp
+{
+
+class DBusProxy;
+
+class TP_QT_NO_EXPORT DBusProxyFactory::Cache : public QObject
+{
+ Q_OBJECT
+
+public:
+ typedef QPair<QString /* serviceName */, QString /* objectPath */> Key;
+
+ Cache();
+ ~Cache();
+
+ DBusProxyPtr get(const Key &key) const;
+ void put(const DBusProxyPtr &proxy);
+
+private Q_SLOTS:
+ void onProxyInvalidated(Tp::DBusProxy *proxy); // The error itself is not interesting
+
+private:
+ QHash<Key, QWeakPointer<DBusProxy> > proxies;
+};
+
+}
diff --git a/TelepathyQt/dbus-proxy-factory.cpp b/TelepathyQt/dbus-proxy-factory.cpp
new file mode 100644
index 00000000..7020c4bc
--- /dev/null
+++ b/TelepathyQt/dbus-proxy-factory.cpp
@@ -0,0 +1,295 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/DBusProxyFactory>
+#include "TelepathyQt/dbus-proxy-factory-internal.h"
+
+#include "TelepathyQt/_gen/dbus-proxy-factory.moc.hpp"
+#include "TelepathyQt/_gen/dbus-proxy-factory-internal.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/DBusProxy>
+#include <TelepathyQt/ReadyObject>
+#include <TelepathyQt/PendingReady>
+
+#include <QDBusConnection>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT DBusProxyFactory::Private
+{
+ Private(const QDBusConnection &bus)
+ : bus(bus),
+ cache(new Cache)
+ {
+ }
+
+ ~Private()
+ {
+ delete cache;
+ }
+
+ QDBusConnection bus;
+ Cache *cache;
+};
+
+/**
+ * \class DBusProxyFactory
+ * \ingroup utils
+ * \headerfile TelepathyQt/dbus-proxy-factory.h <TelepathyQt/DBusProxyFactory>
+ *
+ * \brief The DBusProxyFactory class is a base class for all D-Bus proxy factory
+ * classes. Handles proxy caching and making them ready as appropriate.
+ */
+
+/**
+ * Construct a new DBusProxyFactory object.
+ *
+ * The intention for storing the bus here is that it generally doesn't make sense to construct
+ * proxies for multiple buses in the same context. Allowing that would lead to more complex keying
+ * needs in the cache, as well.
+ *
+ * \param bus The D-Bus bus connection for the objects constructed using this factory.
+ */
+DBusProxyFactory::DBusProxyFactory(const QDBusConnection &bus)
+ : mPriv(new Private(bus))
+{
+}
+
+/**
+ * Class destructor.
+ */
+DBusProxyFactory::~DBusProxyFactory()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the D-Bus connection all of the proxies from this factory communicate with.
+ *
+ * \return A QDBusConnection object.
+ */
+const QDBusConnection &DBusProxyFactory::dbusConnection() const
+{
+ return mPriv->bus;
+}
+
+/**
+ * Return a cached proxy with the given \a busName and \a objectPath.
+ *
+ * If a proxy has not been previously put into the cache by nowHaveProxy for those identifying
+ * attributes, or a previously cached proxy has since been invalidated and/or destroyed, a \c Null
+ * shared pointer is returned instead.
+ *
+ * \param busName Bus name of the proxy to return.
+ * \param objectPath Object path of the proxy to return.
+ * \return A pointer to the DBusProxy object, if any.
+ */
+DBusProxyPtr DBusProxyFactory::cachedProxy(const QString &busName,
+ const QString &objectPath) const
+{
+ QString finalName = finalBusNameFrom(busName);
+ return mPriv->cache->get(Cache::Key(finalName, objectPath));
+}
+
+/**
+ * Should be called by subclasses when they have a proxy, be it a newly-constructed one or one from
+ * the cache.
+ *
+ * This function will then do the rest of the factory work, including caching the proxy if it's not
+ * cached already, doing any initialPrepare()/readyPrepare() work if appropriate, and making the
+ * features from featuresFor() ready if they aren't already.
+ *
+ * The returned PendingReady only finishes when the initialPrepare() and readyPrepare() operations
+ * for the proxy has completed, and the requested features have all been made ready (or found unable
+ * to be made ready). Note that this might have happened already before calling this function, if
+ * the proxy was not a newly created one, but was looked up from the cache. DBusProxyFactory handles
+ * the necessary subleties for this to work.
+ *
+ * Access to the proxy instance is allowed as soon as this method returns through
+ * PendingReady::proxy(), if the proxy is needed in a context where it's not required to be ready.
+ *
+ * \param proxy The proxy which the factory should now make sure is prepared and made ready.
+ * \return A PendingReady operation which will emit PendingReady::finished
+ * when the proxy is usable.
+ */
+PendingReady *DBusProxyFactory::nowHaveProxy(const DBusProxyPtr &proxy) const
+{
+ Q_ASSERT(!proxy.isNull());
+
+ mPriv->cache->put(proxy);
+ return new PendingReady(SharedPtr<DBusProxyFactory>((DBusProxyFactory*) this),
+ proxy, featuresFor(proxy));
+}
+
+/**
+ * \fn QString DBusProxyFactory::finalBusNameFrom(const QString &uniqueOrWellKnown) const
+ *
+ * "Normalize" a bus name according to the rules for the proxy class to construct.
+ *
+ * Should be implemented by subclasses to transform the application-specified name \a
+ * uniqueOrWellKnown to whatever the proxy constructed for that name would have in its
+ * DBusProxy::busName() in the end.
+ *
+ * For StatelessDBusProxy sub-classes this should mostly be an identity transform, while for
+ * StatefulDBusProxy sub-classes StatefulDBusProxy::uniqueNameFrom() or an equivalent thereof should
+ * be used in most cases.
+ *
+ * If this is not implemented correctly, caching won't work properly.
+ *
+ * \param uniqueOrWellKnown Any valid D-Bus service name, either unique or well-known.
+ * \return Whatever that name would turn to, when a proxy is constructed for it.
+ */
+
+/**
+ * Allows subclasses to do arbitrary manipulation on the proxy before it is attempted to be made
+ * ready.
+ *
+ * If a non-\c NULL operation is returned, the completion of that operation is waited for before
+ * starting to make the object ready whenever nowHaveProxy() is called the first time around for a
+ * given proxy.
+ *
+ * \todo FIXME actually implement this... :) Currently just a vtable placeholder.
+ * \param proxy The just-constructed proxy to be prepared.
+ * \return \c NULL ie. nothing to do.
+ */
+PendingOperation *DBusProxyFactory::initialPrepare(const DBusProxyPtr &proxy) const
+{
+ // Nothing we could think about needs doing
+ return NULL;
+}
+
+/**
+ * Allows subclasses to do arbitrary manipulation on the proxy after it has been made ready.
+ *
+ * If a non-\c NULL operation is returned, the completion of that operation is waited for before
+ * signaling that the object is ready for use after ReadyObject::becomeReady() for it has finished
+ * whenever nowHaveProxy() is called the first time around for a given proxy.
+ *
+ * \todo FIXME actually implement this... :) Currently just a vtable placeholder.
+ * \param proxy The just-readified proxy to be prepared.
+ * \return \c NULL ie. nothing to do.
+ */
+PendingOperation *DBusProxyFactory::readyPrepare(const DBusProxyPtr &proxy) const
+{
+ // Nothing we could think about needs doing
+ return NULL;
+}
+
+/**
+ * \fn Features DBusProxyFactory::featuresFor(const SharedPtr<RefCounted> &proxy) const
+ *
+ * Return the features which should be made ready on a given proxy.
+ *
+ * This can be used to implement instance-specific features based on arbitrary criteria.
+ * FixedFeatureFactory implements this as a fixed set of features independent of the instance,
+ * however.
+ *
+ * It should be noted that if an empty set of features is returned, ReadyObject::becomeReady() is
+ * not called at all. In other words, any "core feature" is not automatically added to the requested
+ * features. This is to enable setting a factory to not make proxies ready at all, which is useful
+ * eg. in the case of account editing UIs which aren't interested in the state of Connection objects
+ * for the Account objects they're editing.
+ *
+ * \param proxy The proxy on which the returned features will be made ready.
+ * \return A list of Feature objects.
+ */
+
+DBusProxyFactory::Cache::Cache()
+{
+}
+
+DBusProxyFactory::Cache::~Cache()
+{
+}
+
+DBusProxyPtr DBusProxyFactory::Cache::get(const Key &key) const
+{
+ DBusProxyPtr proxy(proxies.value(key));
+
+ if (proxy.isNull() || !proxy->isValid()) {
+ // Weak pointer invalidated or proxy invalidated during this mainloop iteration and we still
+ // haven't got the invalidated() signal for it
+ return DBusProxyPtr();
+ }
+
+ return proxy;
+}
+
+void DBusProxyFactory::Cache::put(const DBusProxyPtr &proxy)
+{
+ if (proxy->busName().isEmpty()) {
+ debug() << "Not inserting proxy" << proxy.data() << "with no bus name to factory cache";
+ return;
+ } else if (!proxy->isValid()) {
+ debug() << "Not inserting to factory cache invalid proxy - proxy is for" <<
+ proxy->busName() << ',' << proxy->objectPath();
+ return;
+ }
+
+ Key key(proxy->busName(), proxy->objectPath());
+
+ DBusProxyPtr existingProxy(proxies.value(key));
+ if (!existingProxy || existingProxy != proxy) {
+ // Disconnect the invalidated signal from the proxy we're replacing, so it won't uselessly
+ // cause the new (hopefully valid) proxy to be dropped from the cache if it arrives late.
+ //
+ // The window in which this makes a difference is very slim but existent; namely, somebody
+ // must request a proxy from the factory in the same mainloop iteration as an otherwise
+ // matching proxy has invalidated itself. The invalidation signal would be delivered and
+ // processed only during the next mainloop iteration.
+ if (existingProxy) {
+ Q_ASSERT(!existingProxy->isValid());
+ existingProxy->disconnect(
+ SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
+ this,
+ SLOT(onProxyInvalidated(Tp::DBusProxy*)));
+
+ debug() << "Replacing invalidated proxy" << existingProxy.data() << "in cache for name"
+ << existingProxy->busName() << ',' << existingProxy->objectPath();
+ }
+
+ connect(proxy.data(),
+ SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
+ SLOT(onProxyInvalidated(Tp::DBusProxy*)));
+
+ debug() << "Inserting to factory cache proxy for" << key;
+ proxies.insert(key, QWeakPointer<DBusProxy>(proxy.data()));
+ }
+}
+
+void DBusProxyFactory::Cache::onProxyInvalidated(Tp::DBusProxy *proxy)
+{
+ Key key(proxy->busName(), proxy->objectPath());
+
+ // Not having it would indicate invalidated() signaled twice for the same proxy, or us having
+ // connected to two proxies with the same key, neither of which should happen
+ Q_ASSERT(proxies.contains(key));
+
+ debug() << "Removing from factory cache invalidated proxy for" << key;
+
+ proxies.remove(key);
+}
+
+}
diff --git a/TelepathyQt/dbus-proxy-factory.h b/TelepathyQt/dbus-proxy-factory.h
new file mode 100644
index 00000000..1a7eaea7
--- /dev/null
+++ b/TelepathyQt/dbus-proxy-factory.h
@@ -0,0 +1,85 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_dbus_proxy_factory_h_HEADER_GUARD_
+#define _TelepathyQt_dbus_proxy_factory_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Global>
+#include <TelepathyQt/SharedPtr>
+#include <TelepathyQt/Types>
+
+// For Q_DISABLE_COPY
+#include <QtGlobal>
+
+#include <QString>
+
+class QDBusConnection;
+
+namespace Tp
+{
+
+class Features;
+class PendingReady;
+class PendingOperation;
+
+class TP_QT_EXPORT DBusProxyFactory : public QObject, public RefCounted
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(DBusProxyFactory)
+
+public:
+ virtual ~DBusProxyFactory();
+
+ const QDBusConnection &dbusConnection() const;
+
+protected:
+ DBusProxyFactory(const QDBusConnection &bus);
+
+ DBusProxyPtr cachedProxy(const QString &busName, const QString &objectPath) const;
+
+ PendingReady *nowHaveProxy(const DBusProxyPtr &proxy) const;
+
+ // I don't want this to be non-pure virtual, because I want ALL subclasses to have to think
+ // about whether or not they need to uniquefy the name or not. If a subclass doesn't implement
+ // this while it should, matching with the cache for future requests and invalidation breaks.
+ virtual QString finalBusNameFrom(const QString &uniqueOrWellKnown) const = 0;
+
+ virtual PendingOperation *initialPrepare(const DBusProxyPtr &proxy) const;
+ virtual PendingOperation *readyPrepare(const DBusProxyPtr &proxy) const;
+
+ virtual Features featuresFor(const DBusProxyPtr &proxy) const = 0;
+
+private:
+ class Cache;
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/dbus-proxy.cpp b/TelepathyQt/dbus-proxy.cpp
new file mode 100644
index 00000000..1496c454
--- /dev/null
+++ b/TelepathyQt/dbus-proxy.cpp
@@ -0,0 +1,393 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/DBusProxy>
+
+#include "TelepathyQt/_gen/dbus-proxy.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/Constants>
+
+#include <QDBusConnection>
+#include <QDBusConnectionInterface>
+#include <QDBusError>
+#include <QDBusServiceWatcher>
+#include <QTimer>
+
+namespace Tp
+{
+
+// ==== DBusProxy ======================================================
+
+// Features in TpProxy but not here:
+// * tracking which interfaces we have (in tpqt, subclasses do that)
+// * being Introspectable, a Peer and a Properties implementation
+// * disconnecting from signals when invalidated (probably has to be in the
+// generated code)
+// * making methods always raise an error when called after invalidated
+// (has to be in the generated code)
+
+struct TP_QT_NO_EXPORT DBusProxy::Private
+{
+ Private(const QDBusConnection &dbusConnection, const QString &busName,
+ const QString &objectPath);
+
+ QDBusConnection dbusConnection;
+ QString busName;
+ QString objectPath;
+ QString invalidationReason;
+ QString invalidationMessage;
+};
+
+DBusProxy::Private::Private(const QDBusConnection &dbusConnection,
+ const QString &busName, const QString &objectPath)
+ : dbusConnection(dbusConnection),
+ busName(busName),
+ objectPath(objectPath)
+{
+ debug() << "Creating new DBusProxy";
+}
+
+/**
+ * \class DBusProxy
+ * \ingroup clientproxies
+ * \headerfile TelepathyQt/dbus-proxy.h <TelepathyQt/DBusProxy>
+ *
+ * \brief The DBusProxy class is a base class representing a remote object available over D-Bus.
+ *
+ * All Telepathy-Qt4 client convenience classes that wrap Telepathy interfaces
+ * inherit from this class in order to provide basic D-Bus interface
+ * information.
+ */
+
+/**
+ * Construct a new DBusProxy object.
+ *
+ * \param dbusConnection QDBusConnection to use.
+ * \param busName D-Bus bus name of the service that provides the remote object.
+ * \param objectPath The object path.
+ * \param featureCore The object core feature.
+ */
+DBusProxy::DBusProxy(const QDBusConnection &dbusConnection,
+ const QString &busName, const QString &objectPath, const Feature &featureCore)
+ : Object(),
+ ReadyObject(this, featureCore),
+ mPriv(new Private(dbusConnection, busName, objectPath))
+{
+ if (!dbusConnection.isConnected()) {
+ invalidate(QLatin1String(TELEPATHY_ERROR_DISCONNECTED),
+ QLatin1String("DBus connection disconnected"));
+ }
+}
+
+/**
+ * Class destructor.
+ */
+DBusProxy::~DBusProxy()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the D-Bus connection through which the remote object is
+ * accessed.
+ *
+ * \return A QDBusConnection object.
+ */
+QDBusConnection DBusProxy::dbusConnection() const
+{
+ return mPriv->dbusConnection;
+}
+
+/**
+ * Return the D-Bus object path of the remote object within the service.
+ *
+ * \return The D-Bus object path.
+ */
+QString DBusProxy::objectPath() const
+{
+ return mPriv->objectPath;
+}
+
+/**
+ * Return the D-Bus bus name (either a unique name or a well-known
+ * name) of the service that provides the remote object.
+ *
+ * \return The D-Bus bus name.
+ */
+QString DBusProxy::busName() const
+{
+ return mPriv->busName;
+}
+
+/**
+ * Sets the D-Bus bus name. This is used by subclasses after converting
+ * well-known names to unique names.
+ *
+ * \param busName The D-Bus bus name to set.
+ */
+void DBusProxy::setBusName(const QString &busName)
+{
+ mPriv->busName = busName;
+}
+
+/**
+ * Return whether this proxy is still valid (has not emitted invalidated()).
+ *
+ * \return \c true if still valid, \c false otherwise.
+ */
+bool DBusProxy::isValid() const
+{
+ return mPriv->invalidationReason.isEmpty();
+}
+
+/**
+ * Return the error name indicating the reason this proxy became invalid.
+ *
+ * \return A D-Bus error name, or QString() if this object is still valid.
+ */
+QString DBusProxy::invalidationReason() const
+{
+ return mPriv->invalidationReason;
+}
+
+/**
+ * Return a debugging message indicating the reason this proxy became invalid.
+ *
+ * \return A debugging message, or QString() if this object is still valid.
+ */
+QString DBusProxy::invalidationMessage() const
+{
+ return mPriv->invalidationMessage;
+}
+
+/**
+ * Called by subclasses when the DBusProxy should become invalid.
+ *
+ * This method takes care of setting the invalidationReason,
+ * invalidationMessage, and emitting the invalidated signal.
+ *
+ * \param reason A D-Bus error name (a string in a subset of ASCII,
+ * prefixed with a reversed domain name)
+ * \param message A debugging message associated with the error
+ */
+void DBusProxy::invalidate(const QString &reason, const QString &message)
+{
+ if (!isValid()) {
+ debug().nospace() << "Already invalidated by "
+ << mPriv->invalidationReason
+ << ", not replacing with " << reason
+ << " \"" << message << "\"";
+ return;
+ }
+
+ Q_ASSERT(!reason.isEmpty());
+
+ debug().nospace() << "proxy invalidated: " << reason
+ << ": " << message;
+
+ mPriv->invalidationReason = reason;
+ mPriv->invalidationMessage = message;
+
+ Q_ASSERT(!isValid());
+
+ // Defer emitting the invalidated signal until we next
+ // return to the mainloop.
+ QTimer::singleShot(0, this, SLOT(emitInvalidated()));
+}
+
+void DBusProxy::invalidate(const QDBusError &error)
+{
+ invalidate(error.name(), error.message());
+}
+
+void DBusProxy::emitInvalidated()
+{
+ Q_ASSERT(!isValid());
+
+ emit invalidated(this, mPriv->invalidationReason, mPriv->invalidationMessage);
+}
+
+/**
+ * \fn void DBusProxy::invalidated(Tp::DBusProxy *proxy,
+ * const QString &errorName, const QString &errorMessage)
+ *
+ * Emitted when this object is no longer usable.
+ *
+ * After this signal is emitted, any D-Bus method calls on the object
+ * will fail, but it may be possible to retrieve information that has
+ * already been retrieved and cached.
+ *
+ * \param proxy This proxy.
+ * \param errorName The name of a D-Bus error describing the reason for the invalidation.
+ * \param errorMessage A debugging message associated with the error.
+ */
+
+// ==== StatefulDBusProxy ==============================================
+
+struct TP_QT_NO_EXPORT StatefulDBusProxy::Private
+{
+ Private(const QString &originalName)
+ : originalName(originalName) {}
+
+ QString originalName;
+};
+
+/**
+ * \class StatefulDBusProxy
+ * \ingroup clientproxies
+ * \headerfile TelepathyQt/dbus-proxy.h <TelepathyQt/StatefulDBusProxy>
+ *
+ * \brief The StatefulDBusProxy class is a base class representing a remote object whose API is
+ * stateful.
+ *
+ * These objects do not remain useful if the service providing them exits or
+ * crashes, so they emit invalidated() if this happens.
+ *
+ * Examples include the Connection and Channel classes.
+ */
+
+/**
+ * Construct a new StatefulDBusProxy object.
+ *
+ * \param dbusConnection QDBusConnection to use.
+ * \param busName D-Bus bus name of the service that provides the remote object.
+ * \param objectPath The object path.
+ * \param featureCore The object core feature.
+ */
+StatefulDBusProxy::StatefulDBusProxy(const QDBusConnection &dbusConnection,
+ const QString &busName, const QString &objectPath, const Feature &featureCore)
+ : DBusProxy(dbusConnection, busName, objectPath, featureCore),
+ mPriv(new Private(busName))
+{
+ QDBusServiceWatcher *serviceWatcher = new QDBusServiceWatcher(busName,
+ dbusConnection, QDBusServiceWatcher::WatchForUnregistration, this);
+ connect(serviceWatcher,
+ SIGNAL(serviceOwnerChanged(QString,QString,QString)),
+ SLOT(onServiceOwnerChanged(QString,QString,QString)));
+
+ QString error, message;
+ QString uniqueName = uniqueNameFrom(dbusConnection, busName, error, message);
+
+ if (uniqueName.isEmpty()) {
+ invalidate(error, message);
+ return;
+ }
+
+ setBusName(uniqueName);
+}
+
+/**
+ * Class destructor.
+ */
+StatefulDBusProxy::~StatefulDBusProxy()
+{
+ delete mPriv;
+}
+
+QString StatefulDBusProxy::uniqueNameFrom(const QDBusConnection &bus, const QString &name)
+{
+ QString error, message;
+ QString uniqueName = uniqueNameFrom(bus, name, error, message);
+ if (uniqueName.isEmpty()) {
+ warning() << "StatefulDBusProxy::uniqueNameFrom(): Failed to get unique name of" << name;
+ warning() << " error:" << error << "message:" << message;
+ }
+
+ return uniqueName;
+}
+
+QString StatefulDBusProxy::uniqueNameFrom(const QDBusConnection &bus, const QString &name,
+ QString &error, QString &message)
+{
+ if (name.startsWith(QLatin1String(":"))) {
+ return name;
+ }
+
+ // For a stateful interface, it makes no sense to follow name-owner
+ // changes, so we want to bind to the unique name.
+ QDBusReply<QString> reply = bus.interface()->serviceOwner(name);
+ if (reply.isValid()) {
+ return reply.value();
+ } else {
+ error = reply.error().name();
+ message = reply.error().message();
+ return QString();
+ }
+}
+
+void StatefulDBusProxy::onServiceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner)
+{
+ // We only want to invalidate this object if it is not already invalidated,
+ // and its (not any other object's) name owner changed signal is emitted.
+ if (isValid() && name == mPriv->originalName && newOwner.isEmpty()) {
+ invalidate(TP_QT_DBUS_ERROR_NAME_HAS_NO_OWNER,
+ QLatin1String("Name owner lost (service crashed?)"));
+ }
+}
+
+// ==== StatelessDBusProxy =============================================
+
+/**
+ * \class StatelessDBusProxy
+ * \ingroup clientproxies
+ * \headerfile TelepathyQt/dbus-proxy.h <TelepathyQt/DBusProxy>
+ *
+ * \brief The StatelessDBusProxy class is a base class representing a remote object whose API is
+ * basically stateless.
+ *
+ * These objects can remain valid even if the service providing them exits
+ * and is restarted.
+ *
+ * Examples include the AccountManager, Account and ConnectionManager.
+ */
+
+/**
+ * Construct a new StatelessDBusProxy object.
+ *
+ * \param dbusConnection QDBusConnection to use.
+ * \param busName D-Bus bus name of the service that provides the remote object.
+ * \param objectPath The object path.
+ * \param featureCore The object core feature.
+ */
+StatelessDBusProxy::StatelessDBusProxy(const QDBusConnection &dbusConnection,
+ const QString &busName, const QString &objectPath, const Feature &featureCore)
+ : DBusProxy(dbusConnection, busName, objectPath, featureCore),
+ mPriv(0)
+{
+ if (busName.startsWith(QLatin1String(":"))) {
+ warning() <<
+ "Using StatelessDBusProxy for a unique name does not make sense";
+ }
+}
+
+/**
+ * Class destructor.
+ */
+StatelessDBusProxy::~StatelessDBusProxy()
+{
+}
+
+} // Tp
diff --git a/TelepathyQt/dbus-proxy.h b/TelepathyQt/dbus-proxy.h
new file mode 100644
index 00000000..0121d722
--- /dev/null
+++ b/TelepathyQt/dbus-proxy.h
@@ -0,0 +1,122 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_dbus_proxy_h_HEADER_GUARD_
+#define _TelepathyQt_dbus_proxy_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Global>
+#include <TelepathyQt/Object>
+#include <TelepathyQt/ReadyObject>
+
+class QDBusConnection;
+class QDBusError;
+
+namespace Tp
+{
+
+class TestBackdoors;
+
+class TP_QT_EXPORT DBusProxy : public Object, public ReadyObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(DBusProxy)
+
+public:
+ DBusProxy(const QDBusConnection &dbusConnection, const QString &busName,
+ const QString &objectPath, const Feature &featureCore);
+ virtual ~DBusProxy();
+
+ QDBusConnection dbusConnection() const;
+ QString busName() const;
+ QString objectPath() const;
+
+ bool isValid() const;
+ QString invalidationReason() const;
+ QString invalidationMessage() const;
+
+Q_SIGNALS:
+ void invalidated(Tp::DBusProxy *proxy,
+ const QString &errorName, const QString &errorMessage);
+
+protected:
+ void setBusName(const QString &busName);
+ void invalidate(const QString &reason, const QString &message);
+ void invalidate(const QDBusError &error);
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void emitInvalidated();
+
+private:
+ friend class TestBackdoors;
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+class TP_QT_EXPORT StatelessDBusProxy : public DBusProxy
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(StatelessDBusProxy)
+
+public:
+ StatelessDBusProxy(const QDBusConnection &dbusConnection,
+ const QString &busName, const QString &objectPath, const Feature &featureCore);
+ virtual ~StatelessDBusProxy();
+
+private:
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+class TP_QT_EXPORT StatefulDBusProxy : public DBusProxy
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(StatefulDBusProxy)
+
+public:
+ StatefulDBusProxy(const QDBusConnection &dbusConnection,
+ const QString &busName, const QString &objectPath, const Feature &featureCore);
+ virtual ~StatefulDBusProxy();
+
+ static QString uniqueNameFrom(const QDBusConnection &bus, const QString &wellKnownOrUnique);
+ static QString uniqueNameFrom(const QDBusConnection &bus, const QString &wellKnownOrUnique,
+ QString &error, QString &message);
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onServiceOwnerChanged(const QString &name, const QString &oldOwner,
+ const QString &newOwner);
+
+private:
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/dbus.cpp b/TelepathyQt/dbus.cpp
new file mode 100644
index 00000000..c146414e
--- /dev/null
+++ b/TelepathyQt/dbus.cpp
@@ -0,0 +1,26 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/DBus>
+
+#include "TelepathyQt/_gen/cli-dbus-body.hpp"
+#include "TelepathyQt/_gen/cli-dbus.moc.hpp"
diff --git a/TelepathyQt/dbus.h b/TelepathyQt/dbus.h
new file mode 100644
index 00000000..6e3e7176
--- /dev/null
+++ b/TelepathyQt/dbus.h
@@ -0,0 +1,54 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_dbus_h_HEADER_GUARD_
+#define _TelepathyQt_dbus_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+/**
+ * \addtogroup clientsideproxies Client-side proxies
+ *
+ * Proxy objects representing remote service objects accessed via D-Bus.
+ *
+ * In addition to providing direct access to methods, signals and properties
+ * exported by the remote objects, some of these proxies offer features like
+ * automatic inspection of remote object capabilities, property tracking,
+ * backwards compatibility helpers for older services and other utilities.
+ */
+
+/**
+ * \defgroup clientdbus Generic D-Bus proxies
+ * \ingroup clientsideproxies
+ *
+ * Proxy objects representing well-known generic D-Bus interfaces on remote
+ * objects. Note that QDBus already has QDBusConnectionInterface for accessing
+ * the bus daemon, so in the parts where there is an overlap in the
+ * functionality, using the QDBus proxy should be given consideration instead
+ * of blindly using the proxy provided here.
+ */
+
+#include <TelepathyQt/_gen/cli-dbus.h>
+
+#endif
diff --git a/TelepathyQt/dbus.xml b/TelepathyQt/dbus.xml
new file mode 100644
index 00000000..99af441c
--- /dev/null
+++ b/TelepathyQt/dbus.xml
@@ -0,0 +1,12 @@
+<tp:spec
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<tp:title>D-Bus interfaces</tp:title>
+
+<xi:include href="dbus-daemon.xml"/>
+<xi:include href="dbus-introspectable.xml"/>
+<xi:include href="dbus-peer.xml"/>
+<xi:include href="dbus-properties.xml"/>
+
+</tp:spec>
diff --git a/TelepathyQt/debug-internal.h b/TelepathyQt/debug-internal.h
new file mode 100644
index 00000000..c0076016
--- /dev/null
+++ b/TelepathyQt/debug-internal.h
@@ -0,0 +1,170 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_debug_HEADER_GUARD_
+#define _TelepathyQt_debug_HEADER_GUARD_
+
+#include <QDebug>
+
+namespace Tp
+{
+
+class TP_QT_EXPORT Debug
+{
+public:
+ inline Debug() : debug(0) { }
+ inline Debug(QtMsgType type) : type(type), debug(new QDebug(&msg)) { }
+ inline Debug(const Debug &a) : type(a.type), debug(a.debug ? new QDebug(&msg) : 0)
+ {
+ if (debug) {
+ (*debug) << qPrintable(a.msg);
+ }
+ }
+
+ inline Debug &operator=(const Debug &a)
+ {
+ if (this != &a) {
+ type = a.type;
+ delete debug;
+ debug = 0;
+
+ if (a.debug) {
+ debug = new QDebug(&msg);
+ (*debug) << qPrintable(a.msg);
+ }
+ }
+
+ return *this;
+ }
+
+ inline ~Debug()
+ {
+ if (!msg.isEmpty()) {
+ invokeDebugCallback();
+ }
+ delete debug;
+ }
+
+ inline Debug &space()
+ {
+ if (debug) {
+ debug->space();
+ }
+
+ return *this;
+ }
+
+ inline Debug &nospace()
+ {
+ if (debug) {
+ debug->nospace();
+ }
+
+ return *this;
+ }
+
+ inline Debug &maybeSpace()
+ {
+ if (debug) {
+ debug->maybeSpace();
+ }
+
+ return *this;
+ }
+
+ template <typename T>
+ inline Debug &operator<<(T a)
+ {
+ if (debug) {
+ (*debug) << a;
+ }
+
+ return *this;
+ }
+
+private:
+
+ QString msg;
+ QtMsgType type;
+ QDebug *debug;
+
+ void invokeDebugCallback();
+};
+
+// The telepathy-farsight Qt 4 binding links to these - they're not API outside
+// this source tarball, but they *are* ABI
+TP_QT_EXPORT Debug enabledDebug();
+TP_QT_EXPORT Debug enabledWarning();
+
+#ifdef ENABLE_DEBUG
+
+inline Debug debug()
+{
+ return enabledDebug();
+}
+
+inline Debug warning()
+{
+ return enabledWarning();
+}
+
+#else /* #ifdef ENABLE_DEBUG */
+
+struct NoDebug
+{
+ template <typename T>
+ NoDebug& operator<<(const T&)
+ {
+ return *this;
+ }
+
+ NoDebug& space()
+ {
+ return *this;
+ }
+
+ NoDebug& nospace()
+ {
+ return *this;
+ }
+
+ NoDebug& maybeSpace()
+ {
+ return *this;
+ }
+};
+
+inline NoDebug debug()
+{
+ return NoDebug();
+}
+
+inline NoDebug warning()
+{
+ return NoDebug();
+}
+
+#endif /* #ifdef ENABLE_DEBUG */
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/debug.cpp b/TelepathyQt/debug.cpp
new file mode 100644
index 00000000..783b41d9
--- /dev/null
+++ b/TelepathyQt/debug.cpp
@@ -0,0 +1,187 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define IN_TP_QT_HEADER
+#include "debug.h"
+#include "debug-internal.h"
+
+#include "config-version.h"
+
+/**
+ * \defgroup debug Common debug support
+ *
+ * TelepathyQt has an internal mechanism for displaying debugging output. It
+ * uses the Qt4 debugging subsystem, so if you want to redirect the messages,
+ * use qInstallMsgHandler() from &lt;QtGlobal&gt;.
+ *
+ * Debugging output is divided into two categories: normal debug output and
+ * warning messages. Normal debug output results in the normal operation of the
+ * library, warning messages are output only when something goes wrong. Each
+ * category can be invidually enabled.
+ */
+
+namespace Tp
+{
+
+/**
+ * \fn void enableDebug(bool enable)
+ * \ingroup debug
+ *
+ * Enable or disable normal debug output from the library. If the library is not
+ * compiled with debug support enabled, this has no effect; no output is
+ * produced in any case.
+ *
+ * The default is <code>false</code> ie. no debug output.
+ *
+ * \param enable Whether debug output should be enabled or not.
+ */
+
+/**
+ * \fn void enableWarnings(bool enable)
+ * \ingroup debug
+ *
+ * Enable or disable warning output from the library. If the library is not
+ * compiled with debug support enabled, this has no effect; no output is
+ * produced in any case.
+ *
+ * The default is <code>true</code> ie. warning output enabled.
+ *
+ * \param enable Whether warnings should be enabled or not.
+ */
+
+/**
+ * \typedef DebugCallback
+ * \ingroup debug
+ *
+ * \code
+ * typedef QDebug (*DebugCallback)(const QString &libraryName,
+ * const QString &libraryVersion,
+ * QtMsgType type,
+ * const QString &msg)
+ * \endcode
+ */
+
+/**
+ * \fn void setDebugCallback(DebugCallback cb)
+ * \ingroup debug
+ *
+ * Set the callback method that will handle the debug output.
+ *
+ * If \p cb is NULL this method will set the defaultDebugCallback instead.
+ * The default callback function will print the output using default Qt debug
+ * system.
+ *
+ * \param cb A function pointer to the callback method or NULL.
+ * \sa DebugCallback
+ */
+
+#ifdef ENABLE_DEBUG
+
+namespace
+{
+bool debugEnabled = false;
+bool warningsEnabled = true;
+DebugCallback debugCallback = NULL;
+}
+
+void enableDebug(bool enable)
+{
+ debugEnabled = enable;
+}
+
+void enableWarnings(bool enable)
+{
+ warningsEnabled = enable;
+}
+
+void setDebugCallback(DebugCallback cb)
+{
+ debugCallback = cb;
+}
+
+Debug enabledDebug()
+{
+ if (debugEnabled) {
+ return Debug(QtDebugMsg);
+ } else {
+ return Debug();
+ }
+}
+
+Debug enabledWarning()
+{
+ if (warningsEnabled) {
+ return Debug(QtWarningMsg);
+ } else {
+ return Debug();
+ }
+}
+
+void Debug::invokeDebugCallback()
+{
+ if (debugCallback) {
+ debugCallback(QLatin1String("tp-qt4"), QLatin1String(PACKAGE_VERSION), type, msg);
+ } else {
+ switch (type) {
+ case QtDebugMsg:
+ qDebug() << "tp-qt4 " PACKAGE_VERSION " DEBUG:" << qPrintable(msg);
+ break;
+ case QtWarningMsg:
+ qWarning() << "tp-qt4 " PACKAGE_VERSION " WARN:" << qPrintable(msg);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+#else /* !defined(ENABLE_DEBUG) */
+
+void enableDebug(bool enable)
+{
+}
+
+void enableWarnings(bool enable)
+{
+}
+
+void setDebugCallback(DebugCallback cb)
+{
+}
+
+Debug enabledDebug()
+{
+ return Debug();
+}
+
+Debug enabledWarning()
+{
+ return Debug();
+}
+
+void Debug::invokeDebugCallback()
+{
+}
+
+#endif /* !defined(ENABLE_DEBUG) */
+
+} // Tp
diff --git a/TelepathyQt/debug.h b/TelepathyQt/debug.h
new file mode 100644
index 00000000..a6ed0d72
--- /dev/null
+++ b/TelepathyQt/debug.h
@@ -0,0 +1,46 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_debug_h_HEADER_GUARD_
+#define _TelepathyQt_debug_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Global>
+
+namespace Tp
+{
+
+TP_QT_EXPORT void enableDebug(bool enable);
+TP_QT_EXPORT void enableWarnings(bool enable);
+
+typedef void (*DebugCallback)(const QString &libraryName,
+ const QString &libraryVersion,
+ QtMsgType type,
+ const QString &msg);
+TP_QT_EXPORT void setDebugCallback(DebugCallback cb);
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/examples.dox b/TelepathyQt/examples.dox
new file mode 100644
index 00000000..5f592702
--- /dev/null
+++ b/TelepathyQt/examples.dox
@@ -0,0 +1,154 @@
+/*
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * \page accounts_example Accounts Example
+ *
+ * \li \subpage accounts_example_account_item_cpp
+ * \li \subpage accounts_example_account_item_h
+ * \li \subpage accounts_example_accounts_window_cpp
+ * \li \subpage accounts_example_accounts_window_h
+ * \li \subpage accounts_example_main
+ */
+
+/**
+ * \page accounts_example_account_item_cpp accounts/account-item.cpp
+ * \include examples/accounts/account-item.cpp
+ */
+
+/**
+ * \page accounts_example_account_item_h accounts/account-item.h
+ * \include examples/accounts/account-item.h
+ */
+
+/**
+ * \page accounts_example_accounts_window_cpp accounts/accounts-window.cpp
+ * \include examples/accounts/accounts-window.cpp
+ */
+
+/**
+ * \page accounts_example_accounts_window_h accounts/accounts-window.h
+ * \include examples/accounts/accounts-window.h
+ */
+
+/**
+ * \page accounts_example_main accounts/main.cpp
+ * \include examples/accounts/main.cpp
+ */
+
+/**
+ * \page contact_messenger_example Contact Messenger Example
+ *
+ * \li \subpage contact_messenger_example_sender_cpp
+ * \li \subpage contact_messenger_example_sender_h
+ */
+
+/**
+ * \page contact_messenger_example_sender_cpp contact-messenger/sender.cpp
+ * \include examples/contact-messenger/sender.cpp
+ */
+
+/**
+ * \page contact_messenger_example_sender_h contact-messenger/sender.h
+ * \include examples/contact-messenger/sender.h
+ */
+
+/**
+ * \page protocols_example Protocols Example
+ *
+ * \li \subpage protocols_example_main
+ * \li \subpage protocols_example_cm_wrapper_cpp
+ * \li \subpage protocols_example_cm_wrapper_h
+ * \li \subpage protocols_example_protocols_cpp
+ * \li \subpage protocols_example_protocols_h
+ */
+
+/**
+ * \page protocols_example_main protocols/main.cpp
+ * \include examples/protocols/main.cpp
+ */
+
+/**
+ * \page protocols_example_cm_wrapper_cpp protocols/cm-wrapper.cpp
+ * \include examples/protocols/cm-wrapper.cpp
+ */
+
+/**
+ * \page protocols_example_cm_wrapper_h protocols/cm-wrapper.h
+ * \include examples/protocols/cm-wrapper.h
+ */
+
+/**
+ * \page protocols_example_protocols_cpp protocols/protocols.cpp
+ * \include examples/protocols/protocols.cpp
+ */
+
+/**
+ * \page protocols_example_protocols_h protocols/protocols.h
+ * \include examples/protocols/protocols.h
+ */
+
+/**
+ * \page roster_example Roster Example
+ *
+ * \li \subpage roster_example_main
+ * \li \subpage roster_example_roster_item_cpp
+ * \li \subpage roster_example_roster_item_h
+ * \li \subpage roster_example_roster_widget_cpp
+ * \li \subpage roster_example_roster_widget_h
+ * \li \subpage roster_example_roster_window_cpp
+ * \li \subpage roster_example_roster_window_h
+ */
+
+/**
+ * \page roster_example_main roster/main.cpp
+ * \include examples/roster/main.cpp
+ */
+
+/**
+ * \page roster_example_roster_item_cpp roster/roster-item.cpp
+ * \include examples/roster/roster-item.cpp
+ */
+
+/**
+ * \page roster_example_roster_item_h roster/roster-item.h
+ * \include examples/roster/roster-item.h
+ */
+
+/**
+ * \page roster_example_roster_widget_cpp roster/roster-widget.cpp
+ * \include examples/roster/roster-widget.cpp
+ */
+
+/**
+ * \page roster_example_roster_widget_h roster/roster-widget.h
+ * \include examples/roster/roster-widget.h
+ */
+
+/**
+ * \page roster_example_roster_window_cpp roster/roster-window.cpp
+ * \include examples/roster/roster-window.cpp
+ */
+
+/**
+ * \page roster_example_roster_window_h roster/roster-window.h
+ * \include examples/roster/roster-window.h
+ */
diff --git a/TelepathyQt/fake-handler-manager-internal.cpp b/TelepathyQt/fake-handler-manager-internal.cpp
new file mode 100644
index 00000000..e18ab425
--- /dev/null
+++ b/TelepathyQt/fake-handler-manager-internal.cpp
@@ -0,0 +1,165 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 "TelepathyQt/fake-handler-manager-internal.h"
+
+#include "TelepathyQt/_gen/fake-handler-manager-internal.moc.hpp"
+
+#include <TelepathyQt/Channel>
+
+namespace Tp
+{
+
+FakeHandler::FakeHandler(const QDBusConnection &bus)
+ : QObject(),
+ mBus(bus)
+{
+}
+
+FakeHandler::~FakeHandler()
+{
+}
+
+ObjectPathList FakeHandler::handledChannels() const
+{
+ ObjectPathList ret;
+ foreach (const Channel *channel, mChannels) {
+ ret << QDBusObjectPath(channel->objectPath());
+ }
+ return ret;
+}
+
+void FakeHandler::registerChannel(const ChannelPtr &channel)
+{
+ if (mChannels.contains(channel.data())) {
+ return;
+ }
+
+ mChannels.insert(channel.data());
+ connect(channel.data(),
+ SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
+ SLOT(onChannelInvalidated(Tp::DBusProxy*)));
+ connect(channel.data(),
+ SIGNAL(destroyed(QObject*)),
+ SLOT(onChannelDestroyed(QObject*)));
+}
+
+void FakeHandler::onChannelInvalidated(DBusProxy *channel)
+{
+ disconnect(channel, SIGNAL(destroyed(QObject*)), this, SLOT(onChannelDestroyed(QObject*)));
+ onChannelDestroyed(channel);
+}
+
+void FakeHandler::onChannelDestroyed(QObject *obj)
+{
+ Channel *channel = reinterpret_cast<Channel*>(obj);
+
+ Q_ASSERT(mChannels.contains(channel));
+
+ mChannels.remove(channel);
+
+ if (mChannels.isEmpty()) {
+ // emit invalidated here instead of relying on QObject::destroyed as FakeHandlerManager
+ // may reuse this fake handler if FakeHandlerManager::registerChannel is called before the
+ // slot from QObject::destroyed is invoked (deleteLater()).
+ emit invalidated(this);
+ deleteLater();
+ }
+}
+
+FakeHandlerManager *FakeHandlerManager::mInstance = 0;
+
+FakeHandlerManager *FakeHandlerManager::instance()
+{
+ if (!mInstance) {
+ mInstance = new FakeHandlerManager();
+ }
+ return mInstance;
+}
+
+FakeHandlerManager::FakeHandlerManager()
+ : QObject()
+{
+}
+
+FakeHandlerManager::~FakeHandlerManager()
+{
+ mInstance = 0;
+}
+
+ObjectPathList FakeHandlerManager::handledChannels(const QDBusConnection &bus) const
+{
+ QPair<QString, QString> busUniqueId(bus.name(), bus.baseService());
+ if (mFakeHandlers.contains(busUniqueId)) {
+ FakeHandler *fakeHandler = mFakeHandlers.value(busUniqueId);
+ return fakeHandler->handledChannels();
+ }
+ return ObjectPathList();
+}
+
+void FakeHandlerManager::registerClientRegistrar(const ClientRegistrarPtr &cr)
+{
+ QDBusConnection bus(cr->dbusConnection());
+ QPair<QString, QString> busUniqueId(bus.name(), bus.baseService());
+ // keep one registrar around per bus so at least the handlers registered by it will be
+ // around until all channels on that bus gets invalidated/destroyed
+ if (!mClientRegistrars.contains(busUniqueId)) {
+ mClientRegistrars.insert(busUniqueId, cr);
+ }
+}
+
+void FakeHandlerManager::registerChannels(const QList<ChannelPtr> &channels)
+{
+ foreach (const ChannelPtr &channel, channels) {
+ QDBusConnection bus(channel->dbusConnection());
+ QPair<QString, QString> busUniqueId(bus.name(), bus.baseService());
+ FakeHandler *fakeHandler = mFakeHandlers.value(busUniqueId);
+ if (!fakeHandler) {
+ fakeHandler = new FakeHandler(bus);
+ mFakeHandlers.insert(busUniqueId, fakeHandler);
+ connect(fakeHandler,
+ SIGNAL(invalidated(Tp::FakeHandler*)),
+ SLOT(onFakeHandlerInvalidated(Tp::FakeHandler*)));
+ }
+ fakeHandler->registerChannel(channel);
+ }
+}
+
+void FakeHandlerManager::onFakeHandlerInvalidated(FakeHandler *fakeHandler)
+{
+ QDBusConnection bus(fakeHandler->dbusConnection());
+ QPair<QString, QString> busUniqueId(bus.name(), bus.baseService());
+ mFakeHandlers.remove(busUniqueId);
+
+ // all channels for the bus represented by busUniqueId were already destroyed/invalidated,
+ // we can now free the CR (thus the handlers registered by it) registered for that bus
+ mClientRegistrars.remove(busUniqueId);
+
+ if (mFakeHandlers.isEmpty()) {
+ // set mInstance to 0 here as we don't want instance() to return a already
+ // deleted instance
+ mInstance = 0;
+ deleteLater();
+ }
+}
+
+}
diff --git a/TelepathyQt/fake-handler-manager-internal.h b/TelepathyQt/fake-handler-manager-internal.h
new file mode 100644
index 00000000..356bac95
--- /dev/null
+++ b/TelepathyQt/fake-handler-manager-internal.h
@@ -0,0 +1,90 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_fake_handler_manager_internal_h_HEADER_GUARD_
+#define _TelepathyQt_fake_handler_manager_internal_h_HEADER_GUARD_
+
+#include <QObject>
+
+#include <TelepathyQt/ClientRegistrar>
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+class FakeHandler : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(FakeHandler)
+
+public:
+ FakeHandler(const QDBusConnection &bus);
+ ~FakeHandler();
+
+ QDBusConnection dbusConnection() const { return mBus; }
+ ObjectPathList handledChannels() const;
+ void registerChannel(const ChannelPtr &channel);
+
+Q_SIGNALS:
+ void invalidated(Tp::FakeHandler *self);
+
+private Q_SLOTS:
+ void onChannelInvalidated(Tp::DBusProxy *channel);
+ void onChannelDestroyed(QObject *channel);
+
+private:
+ QDBusConnection mBus;
+ QSet<Channel*> mChannels;
+};
+
+class FakeHandlerManager : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(FakeHandlerManager)
+
+public:
+ static FakeHandlerManager *instance();
+
+ ~FakeHandlerManager();
+
+ ObjectPathList handledChannels(const QDBusConnection &bus) const;
+ void registerClientRegistrar(const ClientRegistrarPtr &cr);
+ void registerChannels(const QList<ChannelPtr> &channels);
+
+private Q_SLOTS:
+ void onFakeHandlerInvalidated(Tp::FakeHandler *fakeHandler);
+
+private:
+ FakeHandlerManager();
+
+ static FakeHandlerManager *mInstance;
+ QHash<QPair<QString, QString>, ClientRegistrarPtr> mClientRegistrars;
+ QHash<QPair<QString, QString>, FakeHandler *> mFakeHandlers;
+};
+
+#endif // DOXYGEN_SHOULD_SKIP_THIS
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/feature.cpp b/TelepathyQt/feature.cpp
new file mode 100644
index 00000000..1c442e9c
--- /dev/null
+++ b/TelepathyQt/feature.cpp
@@ -0,0 +1,89 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/Feature>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT Feature::Private : public QSharedData
+{
+ Private(bool critical) : critical(critical) {}
+
+ bool critical;
+};
+
+/**
+ * \class Feature
+ * \ingroup utils
+ * \headerfile TelepathyQt/feature.h <TelepathyQt/Feature>
+ *
+ * \brief The Feature class represents a feature that can be enabled
+ * on demand.
+ */
+
+Feature::Feature()
+ : QPair<QString, uint>()
+{
+}
+
+Feature::Feature(const QString &className, uint id, bool critical)
+ : QPair<QString, uint>(className, id),
+ mPriv(new Private(critical))
+{
+}
+
+Feature::Feature(const Feature &other)
+ : QPair<QString, uint>(other.first, other.second),
+ mPriv(other.mPriv)
+{
+}
+
+Feature::~Feature()
+{
+}
+
+Feature &Feature::operator=(const Feature &other)
+{
+ *this = other;
+ this->mPriv = other.mPriv;
+ return *this;
+}
+
+bool Feature::isCritical() const
+{
+ if (!isValid()) {
+ return false;
+ }
+
+ return mPriv->critical;
+}
+
+/**
+ * \class Features
+ * \ingroup utils
+ * \headerfile TelepathyQt/feature.h <TelepathyQt/Features>
+ *
+ * \brief The Features class represents a list of Feature.
+ */
+
+} // Tp
diff --git a/TelepathyQt/feature.h b/TelepathyQt/feature.h
new file mode 100644
index 00000000..07ab3a55
--- /dev/null
+++ b/TelepathyQt/feature.h
@@ -0,0 +1,94 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_feature_h_HEADER_GUARD_
+#define _TelepathyQt_feature_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Global>
+
+#include <QMetaType>
+#include <QPair>
+#include <QSet>
+#include <QSharedDataPointer>
+#include <QString>
+
+namespace Tp
+{
+
+class TP_QT_EXPORT Feature : public QPair<QString, uint>
+{
+public:
+ Feature();
+ Feature(const QString &className, uint id, bool critical = false);
+ Feature(const Feature &other);
+ ~Feature();
+
+ bool isValid() const { return mPriv.constData() != 0; }
+
+ Feature &operator=(const Feature &other);
+
+ bool isCritical() const;
+
+private:
+ struct Private;
+ friend struct Private;
+ QSharedDataPointer<Private> mPriv;
+};
+
+class TP_QT_EXPORT Features : public QSet<Feature>
+{
+public:
+ Features() { }
+ Features(const Feature &feature) { insert(feature); }
+ Features(const QSet<Feature> &s) : QSet<Feature>(s) { }
+};
+
+inline Features operator|(const Feature &feature1, const Feature &feature2)
+{
+ return Features() << feature1 << feature2;
+}
+
+inline Features operator|(const Features &features, const Feature &feature)
+{
+ return Features(features) << feature;
+}
+
+inline uint qHash(const Features &features)
+{
+ int ret = 0;
+ Q_FOREACH (const Feature &feature, features) {
+ int h = qHash(feature);
+ ret ^= h;
+ }
+ return ret;
+}
+
+} // Tp
+
+Q_DECLARE_METATYPE(Tp::Feature);
+Q_DECLARE_METATYPE(Tp::Features);
+
+#endif
diff --git a/TelepathyQt/file-transfer-channel-creation-properties.cpp b/TelepathyQt/file-transfer-channel-creation-properties.cpp
new file mode 100644
index 00000000..e7232b05
--- /dev/null
+++ b/TelepathyQt/file-transfer-channel-creation-properties.cpp
@@ -0,0 +1,433 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/FileTransferChannelCreationProperties>
+
+#include <TelepathyQt/Global>
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <QSharedData>
+#include <QFileInfo>
+#include <QUrl>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT FileTransferChannelCreationProperties::Private : public QSharedData
+{
+ Private(const QString &suggestedFileName, const QString &contentType,
+ qulonglong size)
+ : contentType(contentType),
+ size(size),
+ contentHashType(FileHashTypeNone)
+ {
+ QFileInfo fileInfo(suggestedFileName);
+ this->suggestedFileName = fileInfo.fileName();
+ }
+
+ Private(const QString &path, const QString &contentType)
+ : contentType(contentType),
+ contentHashType(FileHashTypeNone)
+ {
+ QFileInfo fileInfo(path);
+
+ if (fileInfo.exists()) {
+ // Set mandatory parameters
+ suggestedFileName = fileInfo.fileName();
+ size = fileInfo.size();
+ QUrl fileUri = QUrl::fromLocalFile(fileInfo.canonicalFilePath());
+ uri = fileUri.toString();
+
+ // Set optional parameters
+ lastModificationTime = fileInfo.lastModified();
+ } else {
+ warning() << path << "is not a local file.";
+ }
+ }
+
+ /* mandatory parameters */
+ QString suggestedFileName;
+ QString contentType;
+ qulonglong size;
+
+ /* optional parameters */
+ FileHashType contentHashType;
+ QString contentHash;
+ QString description;
+ QDateTime lastModificationTime;
+ QString uri;
+};
+
+/**
+ * \class FileTransferChannelCreationProperties
+ * \ingroup clientchannel
+ * \headerfile TelepathyQt/file-transfer-channel-creation-properties.h <TelepathyQt/FileTransferChannelCreationProperties>
+ *
+ * \brief The FileTransferChannelCreationProperties class represents the
+ * properties of a file transfer channel request.
+ */
+
+/**
+ * Create an invalid FileTransferChannelCreationProperties.
+ */
+FileTransferChannelCreationProperties::FileTransferChannelCreationProperties()
+{
+}
+
+/**
+ * Create a FileTransferChannelCreationProperties.
+ *
+ * If \a suggestedFileName or \a contentType are empty or if \a size is equal to
+ * zero, the channel request will fail.
+ * \a suggestedFileName will be cleaned of any path.
+ *
+ * \param suggestedFileName The name of the file on the sender's side. This is
+ * therefore given as a suggested filename for the
+ * receiver.
+ * \param contentType The content type (MIME) of the file.
+ * \param size The size of the content of the file.
+ * \sa setUri()
+ */
+FileTransferChannelCreationProperties::FileTransferChannelCreationProperties(
+ const QString &suggestedFileName, const QString &contentType,
+ qulonglong size)
+ : mPriv(new Private(suggestedFileName, contentType, size))
+{
+}
+
+/**
+ * Create a FileTransferChannelCreationProperties.
+ *
+ * This constructor accepts the path to a local file and sets the properties
+ * that can be deducted from the file.
+ * If \a path is not a local file the FileTransferChannelCreationProperties
+ * will be invalid.
+ *
+ * \param path The path to the local file to be sent.
+ */
+FileTransferChannelCreationProperties::FileTransferChannelCreationProperties(
+ const QString &path, const QString &contentType)
+ : mPriv(new Private(path, contentType))
+{
+ if (mPriv->suggestedFileName.isEmpty()) {
+ mPriv = QSharedDataPointer<Private>(NULL);
+ }
+}
+
+/**
+ * Copy constructor.
+ */
+FileTransferChannelCreationProperties::FileTransferChannelCreationProperties(
+ const FileTransferChannelCreationProperties &other)
+ : mPriv(other.mPriv)
+{
+}
+
+/**
+ * Class destructor.
+ */
+FileTransferChannelCreationProperties::~FileTransferChannelCreationProperties()
+{
+}
+
+FileTransferChannelCreationProperties &FileTransferChannelCreationProperties::operator=(
+ const FileTransferChannelCreationProperties &other)
+{
+ this->mPriv = other.mPriv;
+ return *this;
+}
+
+bool FileTransferChannelCreationProperties::operator==(
+ const FileTransferChannelCreationProperties &other) const
+{
+ return mPriv == other.mPriv;
+}
+
+/**
+ * Set the content hash of the file and its type for the request.
+ *
+ * \param contentHashType The type of content hash.
+ * \param contentHash The hash of the file, of type \a contentHashType.
+ * \return This FileTransferChannelCreationProperties.
+ * \sa hasContentHash(), contentHash(), contentHashType()
+ */
+FileTransferChannelCreationProperties &FileTransferChannelCreationProperties::setContentHash(
+ FileHashType contentHashType, const QString &contentHash)
+{
+ if (!isValid()) {
+ // there is no point in updating content hash if not valid, as we miss filename, content
+ // type and size
+ return *this;
+ }
+
+ mPriv->contentHashType = contentHashType;
+ mPriv->contentHash = contentHash;
+ return *this;
+}
+
+/**
+ * Set a description of the file for the request.
+ *
+ * \param description The description of the file.
+ * \return This FileTransferChannelCreationProperties.
+ * \sa hasDescription(), description()
+ */
+FileTransferChannelCreationProperties &FileTransferChannelCreationProperties::setDescription(
+ const QString &description)
+{
+ if (!isValid()) {
+ // there is no point in updating description if not valid, as we miss filename, content
+ // type and size
+ return *this;
+ }
+
+ mPriv->description = description;
+ return *this;
+}
+
+/**
+ * Set the last modification time of the file for the request.
+ *
+ * \param lastModificationTime The last modification time of the file.
+ * \return This FileTransferChannelCreationProperties.
+ * \sa hasLastModificationTime(), lastModificationTime()
+ */
+FileTransferChannelCreationProperties &FileTransferChannelCreationProperties::setLastModificationTime(
+ const QDateTime &lastModificationTime)
+{
+ if (!isValid()) {
+ // there is no point in updating last modification time if not valid, as we miss filename,
+ // content type and size
+ return *this;
+ }
+
+ mPriv->lastModificationTime = lastModificationTime;
+ return *this;
+}
+
+/**
+ * Set the URI of the file for the request.
+ *
+ * \param uri The URI of the file.
+ * \return This FileTransferChannelCreationProperties.
+ * \sa uri()
+ */
+FileTransferChannelCreationProperties &FileTransferChannelCreationProperties::setUri(
+ const QString &uri)
+{
+ if (!isValid()) {
+ // there is no point in updating uri if not valid, as we miss filename, content
+ // type and size
+ return *this;
+ }
+
+ mPriv->uri = uri;
+ return *this;
+}
+
+/**
+ * Return the suggested file name for the request.
+ * If the suggested file name is empty, the channel request will fail.
+ *
+ * \return The suggested file name for the request.
+ */
+QString FileTransferChannelCreationProperties::suggestedFileName() const
+{
+ if (!isValid()) {
+ return QString();
+ }
+
+ return mPriv->suggestedFileName;
+}
+
+/**
+ * Return the content type (MIME) of the file for the request.
+ * If the content type is empty, the channel request will fail.
+ *
+ * \return The content type of the file.
+ */
+QString FileTransferChannelCreationProperties::contentType() const
+{
+ if (!isValid()) {
+ return QString();
+ }
+
+ return mPriv->contentType;
+}
+
+/**
+ * Return the size of the contents of the file for the request.
+ * If size is zero, the channel request will fail.
+ *
+ * \return The size of the contents of file.
+ */
+qulonglong FileTransferChannelCreationProperties::size() const
+{
+ if (!isValid()) {
+ return 0;
+ }
+
+ return mPriv->size;
+}
+
+/**
+ * Return whether the request will have a content hash.
+ *
+ * \return \c true whether it will have a content hash, \c false otherwise.
+ * \sa contentHash(), contentHashType(), setContentHash()
+ */
+bool FileTransferChannelCreationProperties::hasContentHash() const
+{
+ if (!isValid()) {
+ return false;
+ }
+
+ return (mPriv->contentHashType != FileHashTypeNone);
+}
+
+/**
+ * Return the type of the content hash for the request.
+ *
+ * \return The type of the content hash.
+ * \sa hasContentHash(), contentHash(), setContentHash()
+ */
+FileHashType FileTransferChannelCreationProperties::contentHashType() const
+{
+ if (!isValid()) {
+ return FileHashTypeNone;
+ }
+
+ return mPriv->contentHashType;
+}
+
+/**
+ * Return the content hash of the file for the request.
+ *
+ * \return The hash of the contents of the file transfer, of type returned by
+ * contentHashType().
+ * \sa hasContentHash(), contentHashType(), setContentHash()
+ */
+QString FileTransferChannelCreationProperties::contentHash() const
+{
+ if (!isValid()) {
+ return QString();
+ }
+
+ return mPriv->contentHash;
+}
+
+/**
+ * Return whether the request will have a descriprion.
+ *
+ * \return \c true whether it will have description, \c false otherwise.
+ * \sa description(), setDescription()
+ */
+bool FileTransferChannelCreationProperties::hasDescription() const
+{
+ if (!isValid()) {
+ return false;
+ }
+
+ return (!mPriv->description.isEmpty());
+}
+
+/**
+ * Return the description of the file for the request.
+ *
+ * \return The description of the file.
+ * \sa hasDescription(), setDescription()
+ */
+QString FileTransferChannelCreationProperties::description() const
+{
+ if (!isValid()) {
+ return QString();
+ }
+
+ return mPriv->description;
+}
+
+/**
+ * Return whether the request will have a last modification time.
+ *
+ * \return \c true whether it will have a last modification time, \c false
+ * otherwise.
+ * \sa lastModificationTime(), setLastModificationTime()
+ */
+bool FileTransferChannelCreationProperties::hasLastModificationTime() const
+{
+ if (!isValid()) {
+ return false;
+ }
+
+ return (mPriv->lastModificationTime.isValid());
+}
+
+/**
+ * Return the last modification time of the file for the request.
+ *
+ * \return The last modification time of the file.
+ * \sa hasLastModificationTime(), setLastModificationTime()
+ */
+QDateTime FileTransferChannelCreationProperties::lastModificationTime() const
+{
+ if (!isValid()) {
+ return QDateTime();
+ }
+
+ return mPriv->lastModificationTime;
+}
+
+/**
+ * Return whether the request will have an URI.
+ *
+ * \return \c true whether it will have URI, \c false otherwise.
+ * \sa uri(), setUri()
+ */
+bool FileTransferChannelCreationProperties::hasUri() const
+{
+ if (!isValid()) {
+ return false;
+ }
+
+ return (!mPriv->uri.isEmpty());
+}
+
+/**
+ * Return the URI of the file for the request.
+ * If the URI property is empty and the file transfer is handled by an handler
+ * that is not this process, then it won't be able to initiate the file
+ * transfer.
+ *
+ * \return The URI of the file.
+ * \sa setUri()
+ */
+QString FileTransferChannelCreationProperties::uri() const
+{
+ if (!isValid()) {
+ return QString();
+ }
+
+ return mPriv->uri;
+}
+
+} // Tp
diff --git a/TelepathyQt/file-transfer-channel-creation-properties.h b/TelepathyQt/file-transfer-channel-creation-properties.h
new file mode 100644
index 00000000..a3904b4c
--- /dev/null
+++ b/TelepathyQt/file-transfer-channel-creation-properties.h
@@ -0,0 +1,96 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_file_transfer_channel_creation_properties_h_HEADER_GUARD_
+#define _TelepathyQt_file_transfer_channel_creation_properties_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/Global>
+
+#include <QDateTime>
+#include <QMetaType>
+#include <QSharedDataPointer>
+#include <QString>
+
+namespace Tp
+{
+
+class TP_QT_EXPORT FileTransferChannelCreationProperties
+{
+public:
+ FileTransferChannelCreationProperties();
+ FileTransferChannelCreationProperties(const QString &suggestedFileName,
+ const QString &contentType, qulonglong size);
+ FileTransferChannelCreationProperties(const QString &path,
+ const QString &contentType);
+ FileTransferChannelCreationProperties(
+ const FileTransferChannelCreationProperties &other);
+ ~FileTransferChannelCreationProperties();
+
+ bool isValid() const { return mPriv.constData() != 0; }
+
+ FileTransferChannelCreationProperties &operator=(
+ const FileTransferChannelCreationProperties &other);
+ bool operator==(const FileTransferChannelCreationProperties &other) const;
+
+ FileTransferChannelCreationProperties &setContentHash(
+ FileHashType contentHashType, const QString &contentHash);
+ FileTransferChannelCreationProperties &setDescription(
+ const QString &description);
+ FileTransferChannelCreationProperties &setLastModificationTime(
+ const QDateTime &lastModificationTime);
+ FileTransferChannelCreationProperties &setUri(const QString &uri);
+
+ /* mandatory parameters */
+ QString suggestedFileName() const;
+ QString contentType() const;
+ qulonglong size() const;
+
+ /* optional parameters */
+ bool hasContentHash() const;
+ FileHashType contentHashType() const;
+ QString contentHash() const;
+
+ bool hasDescription() const;
+ QString description() const;
+
+ bool hasLastModificationTime() const;
+ QDateTime lastModificationTime() const;
+
+ bool hasUri() const;
+ QString uri() const;
+
+private:
+ struct Private;
+ friend struct Private;
+ QSharedDataPointer<Private> mPriv;
+};
+
+} // Tp
+
+Q_DECLARE_METATYPE(Tp::FileTransferChannelCreationProperties);
+
+#endif
diff --git a/TelepathyQt/file-transfer-channel.cpp b/TelepathyQt/file-transfer-channel.cpp
new file mode 100644
index 00000000..e81618fa
--- /dev/null
+++ b/TelepathyQt/file-transfer-channel.cpp
@@ -0,0 +1,703 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/FileTransferChannel>
+
+#include "TelepathyQt/_gen/file-transfer-channel.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/Connection>
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT FileTransferChannel::Private
+{
+ Private(FileTransferChannel *parent);
+ ~Private();
+
+ static void introspectProperties(Private *self);
+
+ void extractProperties(const QVariantMap &props);
+
+ // Public object
+ FileTransferChannel *parent;
+
+ Client::ChannelTypeFileTransferInterface *fileTransferInterface;
+ Client::DBus::PropertiesInterface *properties;
+
+ ReadinessHelper *readinessHelper;
+
+ // Introspection
+ uint pendingState;
+ uint pendingStateReason;
+ uint state;
+ uint stateReason;
+ QString contentType;
+ QString fileName;
+ QString uri;
+ QString contentHash;
+ QString description;
+ QDateTime lastModificationTime;
+ FileHashType contentHashType;
+ qulonglong initialOffset;
+ qulonglong size;
+ qulonglong transferredBytes;
+ SupportedSocketMap availableSocketTypes;
+
+ bool connected;
+ bool finished;
+};
+
+FileTransferChannel::Private::Private(FileTransferChannel *parent)
+ : parent(parent),
+ fileTransferInterface(parent->interface<Client::ChannelTypeFileTransferInterface>()),
+ properties(parent->interface<Client::DBus::PropertiesInterface>()),
+ readinessHelper(parent->readinessHelper()),
+ pendingState(FileTransferStateNone),
+ pendingStateReason(FileTransferStateChangeReasonNone),
+ state(pendingState),
+ stateReason(pendingStateReason),
+ contentHashType(FileHashTypeNone),
+ initialOffset(0),
+ size(0),
+ transferredBytes(0),
+ connected(false),
+ finished(false)
+{
+ parent->connect(fileTransferInterface,
+ SIGNAL(InitialOffsetDefined(qulonglong)),
+ SLOT(onInitialOffsetDefined(qulonglong)));
+ parent->connect(fileTransferInterface,
+ SIGNAL(FileTransferStateChanged(uint,uint)),
+ SLOT(onStateChanged(uint,uint)));
+ parent->connect(fileTransferInterface,
+ SIGNAL(TransferredBytesChanged(qulonglong)),
+ SLOT(onTransferredBytesChanged(qulonglong)));
+
+ ReadinessHelper::Introspectables introspectables;
+
+ ReadinessHelper::Introspectable introspectableCore(
+ QSet<uint>() << 0, // makesSenseForStatuses
+ Features() << Channel::FeatureCore, // dependsOnFeatures (core)
+ QStringList(), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &Private::introspectProperties,
+ this);
+ introspectables[FeatureCore] = introspectableCore;
+
+ readinessHelper->addIntrospectables(introspectables);
+}
+
+FileTransferChannel::Private::~Private()
+{
+}
+
+void FileTransferChannel::Private::introspectProperties(
+ FileTransferChannel::Private *self)
+{
+ QDBusPendingCallWatcher *watcher =
+ new QDBusPendingCallWatcher(
+ self->properties->GetAll(
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_FILE_TRANSFER)),
+ self->parent);
+ self->parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotProperties(QDBusPendingCallWatcher*)));
+}
+
+void FileTransferChannel::Private::extractProperties(const QVariantMap &props)
+{
+ pendingState = state = qdbus_cast<uint>(props[QLatin1String("State")]);
+ contentType = qdbus_cast<QString>(props[QLatin1String("ContentType")]);
+ fileName = qdbus_cast<QString>(props[QLatin1String("Filename")]);
+ uri = qdbus_cast<QString>(props[QLatin1String("URI")]);
+ contentHash = qdbus_cast<QString>(props[QLatin1String("ContentHash")]);
+ description = qdbus_cast<QString>(props[QLatin1String("Description")]);
+ lastModificationTime.setTime_t((uint) qdbus_cast<qulonglong>(props[QLatin1String("Date")]));
+ contentHashType = (FileHashType) qdbus_cast<uint>(props[QLatin1String("ContentHashType")]);
+ initialOffset = qdbus_cast<qulonglong>(props[QLatin1String("InitialOffset")]);
+ size = qdbus_cast<qulonglong>(props[QLatin1String("Size")]);
+ transferredBytes = qdbus_cast<qulonglong>(props[QLatin1String("TransferredBytes")]);
+ availableSocketTypes = qdbus_cast<SupportedSocketMap>(props[QLatin1String("AvailableSocketTypes")]);
+}
+
+/**
+ * \class FileTransferChannel
+ * \ingroup clientchannel
+ * \headerfile TelepathyQt/file-transfer-channel.h <TelepathyQt/FileTransferChannel>
+ *
+ * \brief The FileTransferChannel class represents a Telepathy channel of type
+ * FileTransfer.
+ *
+ * For more specialized file transfer classes, please refer to
+ * OutgoingFileTransferChannel and IncomingFileTransferChannel.
+ *
+ * For more details, please refer to \telepathy_spec.
+ *
+ * See \ref async_model, \ref shared_ptr
+ */
+
+/**
+ * Feature representing the core that needs to become ready to make the
+ * FileTransferChannel object usable.
+ *
+ * Note that this feature must be enabled in order to use most
+ * FileTransferChannel methods.
+ * See specific methods documentation for more details.
+ *
+ * When calling isReady(), becomeReady(), this feature is implicitly added
+ * to the requested features.
+ */
+const Feature FileTransferChannel::FeatureCore = Feature(QLatin1String(FileTransferChannel::staticMetaObject.className()), 0);
+
+/**
+ * Create a new FileTransferChannel object.
+ *
+ * \param connection Connection owning this channel, and specifying the
+ * service.
+ * \param objectPath The channel object path.
+ * \param immutableProperties The channel immutable properties.
+ * \return A FileTransferChannelPtr object pointing to the newly created
+ * FileTransferChannel object.
+ */
+FileTransferChannelPtr FileTransferChannel::create(const ConnectionPtr &connection,
+ const QString &objectPath, const QVariantMap &immutableProperties)
+{
+ return FileTransferChannelPtr(new FileTransferChannel(connection, objectPath,
+ immutableProperties, FileTransferChannel::FeatureCore));
+}
+
+/**
+ * Construct a new FileTransferChannel object.
+ *
+ * \param connection Connection owning this channel, and specifying the
+ * service.
+ * \param objectPath The channel object path.
+ * \param immutableProperties The channel immutable properties.
+ * \param coreFeature The core feature of the channel type, if any. The corresponding introspectable should
+ * depend on FileTransferChannel::FeatureCore.
+ */
+FileTransferChannel::FileTransferChannel(const ConnectionPtr &connection,
+ const QString &objectPath,
+ const QVariantMap &immutableProperties,
+ const Feature &coreFeature)
+ : Channel(connection, objectPath, immutableProperties, coreFeature),
+ mPriv(new Private(this))
+{
+}
+
+/**
+ * Class destructor.
+ */
+FileTransferChannel::~FileTransferChannel()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the state of the file transfer as described by #FileTransferState.
+ *
+ * Change notification is via the stateChanged() signal.
+ *
+ * This method requires FileTransferChannel::FeatureCore to be ready.
+ *
+ * \return The state as #FileTransferState.
+ * \sa stateReason()
+ */
+FileTransferState FileTransferChannel::state() const
+{
+ if (!isReady(FeatureCore)) {
+ warning() << "FileTransferChannel::FeatureCore must be ready before "
+ "calling state";
+ }
+
+ return (FileTransferState) mPriv->state;
+}
+
+/**
+ * Return the reason for the state change as described by the #FileTransferStateChangeReason.
+ *
+ * Change notification is via the stateChanged() signal.
+ *
+ * This method requires FileTransferChannel::FeatureCore to be ready.
+ *
+ * \return The state reason as #FileTransferStateChangeReason.
+ * \sa state()
+ */
+FileTransferStateChangeReason FileTransferChannel::stateReason() const
+{
+ if (!isReady(FeatureCore)) {
+ warning() << "FileTransferChannel::FeatureCore must be ready before "
+ "calling stateReason";
+ }
+
+ return (FileTransferStateChangeReason) mPriv->stateReason;
+}
+
+/**
+ * Return the name of the file on the sender's side. This is given as
+ * a suggested filename for the receiver.
+ *
+ * This property should be the basename of the file being sent. For example, if
+ * the sender sends the file /home/user/monkey.pdf then this property should be
+ * set to monkey.pdf.
+ *
+ * This property cannot change once the channel has been created.
+ *
+ * This method requires FileTransferChannel::FeatureCore to be ready.
+ *
+ * \return The suggested filename for the receiver.
+ */
+QString FileTransferChannel::fileName() const
+{
+ if (!isReady(FeatureCore)) {
+ warning() << "FileTransferChannel::FeatureCore must be ready before "
+ "calling fileName";
+ }
+
+ return mPriv->fileName;
+}
+
+/**
+ * Return the file's MIME type.
+ *
+ * This property cannot change once the channel has been created.
+ *
+ * This method requires FileTransferChannel::FeatureCore to be ready.
+ *
+ * \return The file's MIME type.
+ */
+QString FileTransferChannel::contentType() const
+{
+ if (!isReady(FeatureCore)) {
+ warning() << "FileTransferChannel::FeatureCore must be ready before "
+ "calling contentType";
+ }
+
+ return mPriv->contentType;
+}
+
+/**
+ * Return the size of the file.
+ *
+ * Note that the size is not guaranteed to be exactly right for
+ * incoming files. This is merely a hint and should not be used to know when the
+ * transfer finished.
+ *
+ * For unknown sizes the return value can be UINT64_MAX.
+ *
+ * This property cannot change once the channel has been created.
+ *
+ * This method requires FileTransferChannel::FeatureCore to be ready.
+ *
+ * \return The file size.
+ */
+qulonglong FileTransferChannel::size() const
+{
+ if (!isReady(FeatureCore)) {
+ warning() << "FileTransferChannel::FeatureCore must be ready before "
+ "calling size";
+ }
+
+ return mPriv->size;
+}
+
+/**
+ * Return the URI of the file.
+ *
+ * On outgoing file transfers, this property cannot change after the channel
+ * is requested. For incoming file transfers, this property may be set by the
+ * channel handler before calling AcceptFile to inform observers where the
+ * incoming file will be saved. When the URI property is set, the signal
+ * IncomingFileTransferChannel::uriDefined() is emitted.
+ *
+ * This method requires FileTransferChannel::FeatureCore to be ready.
+ *
+ * \return The file uri.
+ * \sa IncomingFileTransferChannel::uriDefined()
+ */
+QString FileTransferChannel::uri() const
+{
+ if (!isReady(FeatureCore)) {
+ warning() << "FileTransferChannel::FeatureCore must be ready before "
+ "calling uri";
+ }
+
+ return mPriv->uri;
+}
+
+/**
+ * Return the type of the contentHash().
+ *
+ * This method requires FileTransferChannel::FeatureCore to be ready.
+ *
+ * \return The content hash type as #FileHashType.
+ * \sa contentHash()
+ */
+FileHashType FileTransferChannel::contentHashType() const
+{
+ if (!isReady(FeatureCore)) {
+ warning() << "FileTransferChannel::FeatureCore must be ready before "
+ "calling contentHashType";
+ }
+
+ return mPriv->contentHashType;
+}
+
+/**
+ * Return the hash of the contents of the file transfer, of type described in
+ * the value of the contentHashType().
+ *
+ * Its value MUST correspond to the appropriate type of the contentHashType().
+ * If the contentHashType() is set to #FileHashTypeNone, then the
+ * returned value is an empty string.
+ *
+ * This method requires FileTransferChannel::FeatureCore to be ready.
+ *
+ * \return The hash of the contents.
+ * \sa contentHashType()
+ */
+QString FileTransferChannel::contentHash() const
+{
+ if (!isReady(FeatureCore)) {
+ warning() << "FileTransferChannel::FeatureCore must be ready before "
+ "calling contentHash";
+ }
+
+ if (mPriv->contentHashType == FileHashTypeNone) {
+ return QString();
+ }
+
+ return mPriv->contentHash;
+}
+
+/**
+ * Return the description of the file transfer.
+ *
+ * This property cannot change once the channel has been created.
+ *
+ * This method requires FileTransferChannel::FeatureCore to be ready.
+ *
+ * \return The description.
+ */
+QString FileTransferChannel::description() const
+{
+ if (!isReady(FeatureCore)) {
+ warning() << "FileTransferChannel::FeatureCore must be ready before "
+ "calling description";
+ }
+
+ return mPriv->description;
+}
+
+/**
+ * Return the last modification time of the file being transferred. This cannot
+ * change once the channel has been created.
+ *
+ * This method requires FileTransferChannel::FeatureCore to be ready.
+ *
+ * \return The file modification time as QDateTime.
+ */
+QDateTime FileTransferChannel::lastModificationTime() const
+{
+ if (!isReady(FeatureCore)) {
+ warning() << "FileTransferChannel::FeatureCore must be ready before "
+ "calling lastModificationTime";
+ }
+
+ return mPriv->lastModificationTime;
+}
+
+/**
+ * Return the offset in bytes from which the file will be sent.
+ *
+ * This method requires FileTransferChannel::FeatureCore to be ready.
+ *
+ * \return The offset in bytes.
+ * \sa initialOffsetDefined()
+ */
+qulonglong FileTransferChannel::initialOffset() const
+{
+ if (!isReady(FeatureCore)) {
+ warning() << "FileTransferChannel::FeatureCore must be ready before "
+ "calling initialOffset";
+ }
+
+ return mPriv->initialOffset;
+}
+
+/**
+ * Return the number of bytes that have been transferred.
+ *
+ * Change notification is via the transferredBytesChanged() signal.
+ *
+ * This method requires FileTransferChannel::FeatureCore to be ready.
+ *
+ * \return The number of bytes.
+ * \sa transferredBytesChanged()
+ */
+qulonglong FileTransferChannel::transferredBytes() const
+{
+ if (!isReady(FeatureCore)) {
+ warning() << "FileTransferChannel::FeatureCore must be ready before "
+ "calling transferredBytes";
+ }
+
+ return mPriv->transferredBytes;
+}
+
+/**
+ * Return a mapping from address types (members of #SocketAddressType) to arrays
+ * of access-control type (members of #SocketAccessControl) that the CM
+ * supports for sockets with that address type.
+ *
+ * For simplicity, if a CM supports offering a particular type of file transfer,
+ * it is assumed to support accepting it. All CMs support at least
+ * SocketAddressTypeIPv4.
+ *
+ * This method requires FileTransferChannel::FeatureCore to be ready.
+ *
+ * \return The available socket types as a map from address types to arrays of access-control type.
+ */
+SupportedSocketMap FileTransferChannel::availableSocketTypes() const
+{
+ if (!isReady(FeatureCore)) {
+ warning() << "FileTransferChannel::FeatureCore must be ready before "
+ "calling availableSocketTypes";
+ }
+
+ return mPriv->availableSocketTypes;
+}
+
+/**
+ * Cancel a file transfer.
+ *
+ * \return A PendingOperation object which will emit PendingOperation::finished
+ * when the call has finished.
+ */
+PendingOperation *FileTransferChannel::cancel()
+{
+ return requestClose();
+}
+
+/**
+ * Protected virtual method called when the state becomes
+ * #FileTransferStateOpen.
+ *
+ * Specialized classes should reimplement this method and call setConnected()
+ * when the connection is established.
+ *
+ * \sa setConnected()
+ */
+void FileTransferChannel::connectToHost()
+{
+ // do nothing
+}
+
+/**
+ * Return whether a connection has been established.
+ *
+ * \return \c true if the connections has been established, \c false otherwise.
+ * \sa setConnected()
+ */
+bool FileTransferChannel::isConnected() const
+{
+ return mPriv->connected;
+}
+
+/**
+ * Indicate whether a connection has been established.
+ *
+ * Specialized classes that reimplement connectToHost() must call this method
+ * once the connection has been established or setFinished() if an error
+ * occurred.
+ *
+ * \sa isConnected(), connectToHost(), setFinished()
+ */
+void FileTransferChannel::setConnected()
+{
+ mPriv->connected = true;
+}
+
+/**
+ * Return whether sending/receiving has finished.
+ *
+ * \return \c true if sending/receiving has finished, \c false otherwise.
+ */
+bool FileTransferChannel::isFinished() const
+{
+ return mPriv->finished;
+}
+
+/**
+ * Protected virtual method called when an error occurred and the transfer
+ * should finish.
+ *
+ * Specialized classes should reimplement this method and close the IO devices
+ * and do all the needed cleanup.
+ *
+ * Note that for specialized classes that reimplement connectToHost() and set
+ * isConnected() to true, the state will not change to
+ * #FileTransferStateCompleted once the state change is received.
+ *
+ * When finished sending/receiving the specialized class MUST call this method
+ * and then the state will change to the latest pending state.
+ */
+void FileTransferChannel::setFinished()
+{
+ mPriv->finished = true;
+
+ // do the actual state change, in case we are in
+ // FileTransferStateCompleted pendingState
+ changeState();
+}
+
+void FileTransferChannel::gotProperties(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QVariantMap> reply = *watcher;
+
+ if (!reply.isError()) {
+ QVariantMap props = reply.value();
+ mPriv->extractProperties(props);
+ debug() << "Got reply to Properties::GetAll(FileTransferChannel)";
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureCore, true);
+ }
+ else {
+ warning().nospace() << "Properties::GetAll(FileTransferChannel) failed "
+ "with " << reply.error().name() << ": " << reply.error().message();
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureCore, false,
+ reply.error());
+ }
+}
+
+void FileTransferChannel::changeState()
+{
+ if (mPriv->state == mPriv->pendingState) {
+ return;
+ }
+
+ mPriv->state = mPriv->pendingState;
+ mPriv->stateReason = mPriv->pendingStateReason;
+ emit stateChanged((FileTransferState) mPriv->state,
+ (FileTransferStateChangeReason) mPriv->stateReason);
+}
+
+void FileTransferChannel::onStateChanged(uint state, uint stateReason)
+{
+ if (state == (uint) mPriv->pendingState) {
+ return;
+ }
+
+ debug() << "File transfer state changed to" << state <<
+ "with reason" << stateReason;
+ mPriv->pendingState = (FileTransferState) state;
+ mPriv->pendingStateReason = (FileTransferStateChangeReason) stateReason;
+
+ switch (state) {
+ case FileTransferStateOpen:
+ // try to connect to host, for handlers this
+ // connect to host, as the user called Accept/ProvideFile
+ // and have the host addr, for observers this will do nothing and
+ // everything will keep working
+ connectToHost();
+ changeState();
+ break;
+ case FileTransferStateCompleted:
+ //iIf already finished sending/receiving, just change the state,
+ // if not completed will only be set when:
+ // IncomingChannel:
+ // - The input socket closes
+ // OutgoingChannel:
+ // - Input EOF is reached or the output socket is closed
+ //
+ // we also check for connected as observers will never be connected
+ // and finished will never be set, but we need to work anyway.
+ if (mPriv->finished || !mPriv->connected) {
+ changeState();
+ }
+ break;
+ case FileTransferStateCancelled:
+ // if already finished sending/receiving, just change the state,
+ // if not finish now and change the state
+ if (!mPriv->finished) {
+ setFinished();
+ } else {
+ changeState();
+ }
+ break;
+ default:
+ changeState();
+ break;
+ }
+}
+
+void FileTransferChannel::onInitialOffsetDefined(qulonglong initialOffset)
+{
+ mPriv->initialOffset = initialOffset;
+ emit initialOffsetDefined(initialOffset);
+}
+
+void FileTransferChannel::onTransferredBytesChanged(qulonglong count)
+{
+ mPriv->transferredBytes = count;
+ emit transferredBytesChanged(count);
+}
+
+void FileTransferChannel::onUriDefined(const QString &uri)
+{
+ mPriv->uri = uri;
+ // Signal is emitted only by IncomingFileTransferChannels
+}
+
+/**
+ * \fn void FileTransferChannel::stateChanged(Tp::FileTransferState state,
+ * Tp::FileTransferStateChangeReason reason)
+ *
+ * Emitted when the value of state() changes.
+ *
+ * \param state The new state of this file transfer channel.
+ * \param reason The reason for the change of state.
+ * \sa state()
+ */
+
+/**
+ * \fn void FileTransferChannel::initialOffsetDefined(qulonglong initialOffset)
+ *
+ * Emitted when the initial offset for the file transfer is
+ * defined.
+ *
+ * \param initialOffset The new initial offset for the file transfer.
+ * \sa initialOffset()
+ */
+
+/**
+ * \fn void FileTransferChannel::transferredBytesChanged(qulonglong count);
+ *
+ * Emitted when the value of transferredBytes() changes.
+ *
+ * \param count The new number of bytes transferred.
+ * \sa transferredBytes()
+ */
+
+} // Tp
diff --git a/TelepathyQt/file-transfer-channel.h b/TelepathyQt/file-transfer-channel.h
new file mode 100644
index 00000000..a904b840
--- /dev/null
+++ b/TelepathyQt/file-transfer-channel.h
@@ -0,0 +1,108 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_file_transfer_channel_h_HEADER_GUARD_
+#define _TelepathyQt_file_transfer_channel_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Channel>
+
+namespace Tp
+{
+
+class TP_QT_EXPORT FileTransferChannel : public Channel
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(FileTransferChannel)
+
+public:
+ static const Feature FeatureCore;
+
+ static FileTransferChannelPtr create(const ConnectionPtr &connection,
+ const QString &objectPath, const QVariantMap &immutableProperties);
+
+ virtual ~FileTransferChannel();
+
+ FileTransferState state() const;
+ FileTransferStateChangeReason stateReason() const;
+
+ QString fileName() const;
+ QString contentType() const;
+ qulonglong size() const;
+ QString uri() const;
+
+ FileHashType contentHashType() const;
+ QString contentHash() const;
+
+ QString description() const;
+
+ QDateTime lastModificationTime() const;
+
+ qulonglong initialOffset() const;
+
+ qulonglong transferredBytes() const;
+
+ PendingOperation *cancel();
+
+Q_SIGNALS:
+ void stateChanged(Tp::FileTransferState state,
+ Tp::FileTransferStateChangeReason reason);
+ void initialOffsetDefined(qulonglong initialOffset);
+ void transferredBytesChanged(qulonglong count);
+
+protected:
+ FileTransferChannel(const ConnectionPtr &connection, const QString &objectPath,
+ const QVariantMap &immutableProperties,
+ const Feature &coreFeature = FileTransferChannel::FeatureCore);
+
+ SupportedSocketMap availableSocketTypes() const;
+
+ virtual void connectToHost();
+ bool isConnected() const;
+ void setConnected();
+
+ bool isFinished() const;
+ virtual void setFinished();
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void gotProperties(QDBusPendingCallWatcher *watcher);
+
+ TP_QT_NO_EXPORT void changeState();
+ TP_QT_NO_EXPORT void onStateChanged(uint state, uint stateReason);
+ TP_QT_NO_EXPORT void onInitialOffsetDefined(qulonglong initialOffset);
+ TP_QT_NO_EXPORT void onTransferredBytesChanged(qulonglong count);
+
+protected Q_SLOTS:
+ TP_QT_NO_EXPORT void onUriDefined(const QString &uri);
+
+private:
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/filter.dox b/TelepathyQt/filter.dox
new file mode 100644
index 00000000..23aaaab1
--- /dev/null
+++ b/TelepathyQt/filter.dox
@@ -0,0 +1,30 @@
+/*
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * \class Tp::Filter
+ * \ingroup utils
+ * \headerfile TelepathyQt/filter.h <TelepathyQt/Filter>
+ *
+ * \brief The Filter class provides a base class to be used by specialized
+ * filters such as GenericCapabilityFilter, GenericPropertyFilter, etc.
+ */
diff --git a/TelepathyQt/filter.h b/TelepathyQt/filter.h
new file mode 100644
index 00000000..33a5a8ca
--- /dev/null
+++ b/TelepathyQt/filter.h
@@ -0,0 +1,63 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_filter_h_HEADER_GUARD_
+#define _TelepathyQt_filter_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/SharedPtr>
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+template <class T>
+class Filter : public RefCounted
+{
+ Q_DISABLE_COPY(Filter)
+
+public:
+ virtual ~Filter() {}
+
+ virtual bool isValid() const { return false; }
+
+ virtual bool matches(const SharedPtr<T> &t) const
+ {
+ Q_UNUSED(t);
+
+ return false;
+ }
+
+protected:
+ Filter() {}
+
+private:
+ struct Private;
+ Private *mPriv; // Just a placeholder really
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/fixed-feature-factory.cpp b/TelepathyQt/fixed-feature-factory.cpp
new file mode 100644
index 00000000..3c285c0c
--- /dev/null
+++ b/TelepathyQt/fixed-feature-factory.cpp
@@ -0,0 +1,116 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/FixedFeatureFactory>
+
+#include "TelepathyQt/_gen/fixed-feature-factory.moc.hpp"
+
+#include <TelepathyQt/Feature>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT FixedFeatureFactory::Private
+{
+ Features features;
+};
+
+/**
+ * \class FixedFeatureFactory
+ * \ingroup utils
+ * \headerfile TelepathyQt/fixed-feature-factory.h <TelepathyQt/FixedFeatureFactory>
+ *
+ * \brief The FixedFeatureFactory class is a base class for all D-Bus proxy
+ * factories which want the same set of features for all constructed proxies.
+ */
+
+/**
+ * Class constructor.
+ *
+ * The intention for storing the bus here is that it generally doesn't make sense to construct
+ * proxies for multiple buses in the same context. Allowing that would lead to more complex keying
+ * needs in the cache, as well.
+ *
+ * \param bus The D-Bus bus connection for the objects constructed using this factory.
+ */
+FixedFeatureFactory::FixedFeatureFactory(const QDBusConnection &bus)
+ : DBusProxyFactory(bus), mPriv(new Private)
+{
+}
+
+/**
+ * Class destructor.
+ */
+FixedFeatureFactory::~FixedFeatureFactory()
+{
+ delete mPriv;
+}
+
+/**
+ * Gets the features this factory will make ready on constructed proxies.
+ *
+ * \return The set of features.
+ */
+Features FixedFeatureFactory::features() const
+{
+ return mPriv->features;
+}
+
+/**
+ * Adds a single feature this factory will make ready on further constructed proxies.
+ *
+ * No feature removal is provided, to guard against uncooperative modules removing features other
+ * modules have set and depend on.
+ *
+ * \param feature The feature to add.
+ */
+void FixedFeatureFactory::addFeature(const Feature &feature)
+{
+ addFeatures(Features(feature));
+}
+
+/**
+ * Adds a set of features this factory will make ready on further constructed proxies.
+ *
+ * No feature removal is provided, to guard against uncooperative modules removing features other
+ * modules have set and depend on.
+ *
+ * \param features The features to add.
+ */
+void FixedFeatureFactory::addFeatures(const Features &features)
+{
+ mPriv->features.unite(features);
+}
+
+/**
+ * Fixed implementation of the per-proxy feature getter.
+ *
+ * \return features(), irrespective of the actual \a proxy.
+ */
+Features FixedFeatureFactory::featuresFor(const DBusProxyPtr &proxy) const
+{
+ Q_UNUSED(proxy);
+
+ return features();
+}
+
+}
diff --git a/TelepathyQt/fixed-feature-factory.h b/TelepathyQt/fixed-feature-factory.h
new file mode 100644
index 00000000..62672c7c
--- /dev/null
+++ b/TelepathyQt/fixed-feature-factory.h
@@ -0,0 +1,69 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_fixed_feature_factory_h_HEADER_GUARD_
+#define _TelepathyQt_fixed_feature_factory_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Global>
+#include <TelepathyQt/SharedPtr>
+
+#include <TelepathyQt/DBusProxyFactory>
+
+class QDBusConnection;
+
+namespace Tp
+{
+
+class Feature;
+class Features;
+
+class TP_QT_EXPORT FixedFeatureFactory : public DBusProxyFactory
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(FixedFeatureFactory)
+
+public:
+ virtual ~FixedFeatureFactory();
+
+ Features features() const;
+
+ void addFeature(const Feature &feature);
+ void addFeatures(const Features &features);
+
+protected:
+ FixedFeatureFactory(const QDBusConnection &bus);
+
+ virtual Features featuresFor(const DBusProxyPtr &proxy) const;
+
+private:
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/future-channel-dispatcher.xml b/TelepathyQt/future-channel-dispatcher.xml
new file mode 100644
index 00000000..0e7f67c5
--- /dev/null
+++ b/TelepathyQt/future-channel-dispatcher.xml
@@ -0,0 +1,20 @@
+<tp:spec
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<tp:title>Channel Dispatcher extensions from the future</tp:title>
+
+<xi:include href="../spec/Channel_Dispatcher_Interface_Messages.xml"/>
+
+<tp:generic-types>
+ <tp:external-type name="Message_Part" type="a{sv}" from="Telepathy specification"/>
+ <tp:mapping name="Message_Part" array-name="Message_Part_List" array-depth="2">
+ <tp:member name="Key" type="s"/>
+ <tp:member name="Value" type="v"/>
+ </tp:mapping>
+</tp:generic-types>
+
+<xi:include href="../spec/generic-types.xml"/>
+<xi:include href="../spec/errors.xml"/>
+
+</tp:spec>
diff --git a/TelepathyQt/future-channel.xml b/TelepathyQt/future-channel.xml
new file mode 100644
index 00000000..6cf1e494
--- /dev/null
+++ b/TelepathyQt/future-channel.xml
@@ -0,0 +1,13 @@
+<tp:spec
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<tp:title>Channel extensions from the future</tp:title>
+
+<xi:include href="../spec/Channel_Interface_Mergeable_Conference.xml"/>
+<xi:include href="../spec/Channel_Interface_Splittable.xml"/>
+
+<xi:include href="../spec/generic-types.xml"/>
+<xi:include href="../spec/errors.xml"/>
+
+</tp:spec>
diff --git a/TelepathyQt/future-interfaces.xml b/TelepathyQt/future-interfaces.xml
new file mode 100644
index 00000000..8cc5d753
--- /dev/null
+++ b/TelepathyQt/future-interfaces.xml
@@ -0,0 +1,14 @@
+<tp:spec
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<tp:title>Extensions from the future</tp:title>
+
+<xi:include href="future-channel.xml"/>
+<xi:include href="future-channel-dispatcher.xml"/>
+<xi:include href="future-misc.xml"/>
+
+<xi:include href="../spec/generic-types.xml"/>
+<xi:include href="../spec/errors.xml"/>
+
+</tp:spec>
diff --git a/TelepathyQt/future-internal.h b/TelepathyQt/future-internal.h
new file mode 100644
index 00000000..e472a9c9
--- /dev/null
+++ b/TelepathyQt/future-internal.h
@@ -0,0 +1,36 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_future_internal_h_HEADER_GUARD_
+#define _TelepathyQt_future_internal_h_HEADER_GUARD_
+
+#include "TelepathyQt/_gen/future-constants.h"
+#include "TelepathyQt/_gen/future-types.h"
+
+#include "TelepathyQt/Channel"
+#include "TelepathyQt/ChannelDispatcher"
+
+#include "TelepathyQt/_gen/future-channel.h"
+#include "TelepathyQt/_gen/future-channel-dispatcher.h"
+#include "TelepathyQt/_gen/future-misc.h"
+
+#endif
diff --git a/TelepathyQt/future-misc.xml b/TelepathyQt/future-misc.xml
new file mode 100644
index 00000000..b756661e
--- /dev/null
+++ b/TelepathyQt/future-misc.xml
@@ -0,0 +1,7 @@
+<tp:spec
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<tp:title>Miscellaneous extensions from the future</tp:title>
+
+</tp:spec>
diff --git a/TelepathyQt/future.cpp b/TelepathyQt/future.cpp
new file mode 100644
index 00000000..cefe8e5f
--- /dev/null
+++ b/TelepathyQt/future.cpp
@@ -0,0 +1,32 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 "TelepathyQt/future-internal.h"
+
+#include "TelepathyQt/_gen/future-channel-body.hpp"
+#include "TelepathyQt/_gen/future-channel.moc.hpp"
+
+#include "TelepathyQt/_gen/future-channel-dispatcher-body.hpp"
+#include "TelepathyQt/_gen/future-channel-dispatcher.moc.hpp"
+
+#include "TelepathyQt/_gen/future-misc-body.hpp"
+#include "TelepathyQt/_gen/future-misc.moc.hpp"
diff --git a/TelepathyQt/generic-capability-filter.dox b/TelepathyQt/generic-capability-filter.dox
new file mode 100644
index 00000000..53d70ae7
--- /dev/null
+++ b/TelepathyQt/generic-capability-filter.dox
@@ -0,0 +1,36 @@
+/*
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * \class Tp::GenericCapabilityFilter
+ * \ingroup utils
+ * \headerfile TelepathyQt/generic-capability-filter.h <TelepathyQt/GenericCapabilityFilter>
+ *
+ * \brief The GenericCapabilityFilter class provides a generic filter object to
+ * be used to filter objects by capabilities.
+ *
+ * The objects used in conjunction with this filter must implement a method
+ * called capabilities() returning a CapabilitiesBase (or a subclass of it)
+ * instance.
+ * Specialized classes such as AccountCapabilityFilter are also provided and
+ * should be used where appropriate.
+ */
diff --git a/TelepathyQt/generic-capability-filter.h b/TelepathyQt/generic-capability-filter.h
new file mode 100644
index 00000000..ece57895
--- /dev/null
+++ b/TelepathyQt/generic-capability-filter.h
@@ -0,0 +1,114 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_generic_capability_filter_h_HEADER_GUARD_
+#define _TelepathyQt_generic_capability_filter_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/ConnectionCapabilities>
+#include <TelepathyQt/Filter>
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+template <class T>
+class GenericCapabilityFilter : public Filter<T>
+{
+public:
+ static SharedPtr<GenericCapabilityFilter<T> > create(
+ const RequestableChannelClassSpecList &rccSpecs = RequestableChannelClassSpecList())
+ {
+ return SharedPtr<GenericCapabilityFilter<T> >(new GenericCapabilityFilter<T>(
+ rccSpecs));
+ }
+
+ inline virtual ~GenericCapabilityFilter() { }
+
+ inline virtual bool isValid() const { return true; }
+
+ inline virtual bool matches(const SharedPtr<T> &t) const
+ {
+ bool supportedRcc;
+ RequestableChannelClassSpecList objectRccSpecs = t->capabilities().allClassSpecs();
+ Q_FOREACH (const RequestableChannelClassSpec &filterRccSpec, mFilter) {
+ supportedRcc = false;
+
+ Q_FOREACH (const RequestableChannelClassSpec &objectRccSpec, objectRccSpecs) {
+ /* check if fixed properties match */
+ if (filterRccSpec.fixedProperties() == objectRccSpec.fixedProperties()) {
+ supportedRcc = true;
+
+ /* check if all allowed properties in the filter RCC
+ * are in the object RCC allowed properties */
+ Q_FOREACH (const QString &value, filterRccSpec.allowedProperties()) {
+ if (!objectRccSpec.allowsProperty(value)) {
+ /* one of the properties in the filter RCC
+ * allowed properties is not in the object RCC
+ * allowed properties */
+ supportedRcc = false;
+ break;
+ }
+ }
+
+ /* this RCC is supported, no need to check anymore */
+ if (supportedRcc) {
+ break;
+ }
+ }
+ }
+
+ /* one of the filter RCC is not supported, this object
+ * won't match filter */
+ if (!supportedRcc) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ inline RequestableChannelClassSpecList filter() const { return mFilter; }
+
+ inline void addRequestableChannelClassSubset(const RequestableChannelClassSpec &rccSpec)
+ {
+ mFilter.append(rccSpec.bareClass());
+ }
+
+ inline void setRequestableChannelClassesSubset(const RequestableChannelClassSpecList &rccSpecs)
+ {
+ mFilter = rccSpecs.bareClasses();
+ }
+
+private:
+ GenericCapabilityFilter(const RequestableChannelClassSpecList &rccSpecs)
+ : Filter<T>(), mFilter(rccSpecs) { }
+
+ RequestableChannelClassSpecList mFilter;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/generic-property-filter.dox b/TelepathyQt/generic-property-filter.dox
new file mode 100644
index 00000000..12a42451
--- /dev/null
+++ b/TelepathyQt/generic-property-filter.dox
@@ -0,0 +1,33 @@
+/*
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * \class Tp::GenericPropertyFilter
+ * \ingroup utils
+ * \headerfile TelepathyQt/generic-property-filter.h <TelepathyQt/GenericPropertyFilter>
+ *
+ * \brief The GenericPropertyFilter class provides a generic filter object
+ * to be used to filter objects by properties.
+ *
+ * Specialized classes such as AccountPropertyFilter are also provided and
+ * should be used where appropriate.
+ */
diff --git a/TelepathyQt/generic-property-filter.h b/TelepathyQt/generic-property-filter.h
new file mode 100644
index 00000000..ac56b595
--- /dev/null
+++ b/TelepathyQt/generic-property-filter.h
@@ -0,0 +1,77 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_generic_property_filter_h_HEADER_GUARD_
+#define _TelepathyQt_generic_property_filter_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Filter>
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+template <class T>
+class GenericPropertyFilter : public Filter<T>
+{
+public:
+ inline virtual ~GenericPropertyFilter() { }
+
+ inline virtual bool isValid() const { return true; }
+
+ inline virtual bool matches(const SharedPtr<T> &t) const
+ {
+ for (QVariantMap::const_iterator i = mFilter.constBegin();
+ i != mFilter.constEnd(); ++i) {
+ QString propertyName = i.key();
+ QVariant propertyValue = i.value();
+
+ if (t->property(propertyName.toLatin1().constData()) != propertyValue) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ inline QVariantMap filter() const { return mFilter; }
+
+ inline void addProperty(const QString &propertyName, const QVariant &propertyValue)
+ {
+ mFilter.insert(propertyName, propertyValue);
+ }
+
+ inline void setProperties(const QVariantMap &filter) { mFilter = filter; }
+
+protected:
+ inline GenericPropertyFilter() : Filter<T>() { }
+
+private:
+ QVariantMap mFilter;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/global.h b/TelepathyQt/global.h
new file mode 100644
index 00000000..eb2111ec
--- /dev/null
+++ b/TelepathyQt/global.h
@@ -0,0 +1,103 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_global_h_HEADER_GUARD_
+#define _TelepathyQt_global_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <QtGlobal>
+
+#ifdef BUILDING_TP_QT
+# define TP_QT_EXPORT Q_DECL_EXPORT
+#else
+# define TP_QT_EXPORT Q_DECL_IMPORT
+#endif
+
+#if !defined(Q_OS_WIN) && defined(QT_VISIBILITY_AVAILABLE)
+# define TP_QT_NO_EXPORT __attribute__((visibility("hidden")))
+#endif
+
+#ifndef TP_QT_NO_EXPORT
+# define TP_QT_NO_EXPORT
+#endif
+
+/**
+ * @def TP_QT_DEPRECATED
+ * @ingroup TELEPATHY_QT4_MACROS
+ *
+ * The TP_QT_DEPRECATED macro can be used to trigger compile-time
+ * warnings with newer compilers when deprecated functions are used.
+ *
+ * For non-inline functions, the macro gets inserted at front of the
+ * function declaration, right before the return type:
+ *
+ * \code
+ * TP_QT_DEPRECATED void deprecatedFunctionA();
+ * TP_QT_DEPRECATED int deprecatedFunctionB() const;
+ * \endcode
+ *
+ * For functions which are implemented inline,
+ * the TP_QT_DEPRECATED macro is inserted at the front, right before the
+ * return type, but after "static", "inline" or "virtual":
+ *
+ * \code
+ * TP_QT_DEPRECATED void deprecatedInlineFunctionA() { .. }
+ * virtual TP_QT_DEPRECATED int deprecatedInlineFunctionB() { .. }
+ * static TP_QT_DEPRECATED bool deprecatedInlineFunctionC() { .. }
+ * inline TP_QT_DEPRECATED bool deprecatedInlineFunctionD() { .. }
+ * \endcode
+ *
+ * You can also mark whole structs or classes as deprecated, by inserting the
+ * TP_QT_DEPRECATED macro after the struct/class keyword, but before the
+ * name of the struct/class:
+ *
+ * \code
+ * class TP_QT_DEPRECATED DeprecatedClass { };
+ * struct TP_QT_DEPRECATED DeprecatedStruct { };
+ * \endcode
+ *
+ * \note
+ * It does not make much sense to use the TP_QT_DEPRECATED keyword for a
+ * Qt signal; this is because usually get called by the class which they belong
+ * to, and one would assume that a class author does not use deprecated methods
+ * of his own class. The only exception to this are signals which are connected
+ * to other signals; they get invoked from moc-generated code. In any case,
+ * printing a warning message in either case is not useful.
+ * For slots, it can make sense (since slots can be invoked directly) but be
+ * aware that if the slots get triggered by a signal, they will get called from
+ * moc code as well and thus the warnings are useless.
+ *
+ * \note
+ * TP_QT_DEPRECATED cannot be used for constructors.
+ */
+#ifndef TP_QT_DEPRECATED
+# ifdef TP_QT_DEPRECATED_WARNINGS
+# define TP_QT_DEPRECATED Q_DECL_DEPRECATED
+# else
+# define TP_QT_DEPRECATED
+# endif
+#endif
+
+#endif
diff --git a/TelepathyQt/groups.dox b/TelepathyQt/groups.dox
new file mode 100644
index 00000000..bf7d34b3
--- /dev/null
+++ b/TelepathyQt/groups.dox
@@ -0,0 +1,115 @@
+/*
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * \defgroup clientsideproxies Client-side proxies
+ *
+ * Proxy objects representing remote service objects accessed via D-Bus.
+ *
+ * In addition to providing direct access to methods, signals and properties
+ * exported by the remote objects, some of these proxies offer features like
+ * automatic inspection of remote object capabilities, property tracking,
+ * backwards compatibility helpers for older services and other utilities.
+ */
+
+/**
+ * \defgroup clientaccount Account proxies
+ * \ingroup clientsideproxies
+ *
+ * Proxy objects representing remote Telepathy account objects and their
+ * optional interfaces.
+ */
+
+/**
+ * \defgroup clientam Account manager proxies
+ * \ingroup clientsideproxies
+ *
+ * Proxy objects representing remote Telepathy account manager objects and their
+ * optional interfaces.
+ */
+
+/**
+ * \defgroup clientchannel Channel proxies
+ * \ingroup clientsideproxies
+ *
+ * Proxy objects representing remote Telepathy Channel objects and their
+ * optional interfaces.
+ */
+
+/**
+ * \defgroup clientchanneldispatcher ChannelDispatcher proxies
+ * \ingroup clientsideproxies
+ *
+ * Proxy objects representing remote Telepathy ChannelDispatcher objects and
+ * their optional interfaces.
+ */
+
+/**
+ * \defgroup clientchanneldispatchoperation ChannelDispatchOperation proxies
+ * \ingroup clientsideproxies
+ *
+ * Proxy objects representing remote Telepathy ChannelDispatchOperation objects
+ * and their optional interfaces.
+ */
+
+/**
+ * \defgroup clientchannelrequest ChannelRequest proxies
+ * \ingroup clientsideproxies
+ *
+ * Proxy objects representing remote Telepathy ChannelRequest objects and their
+ * optional interfaces.
+ */
+
+/**
+ * \defgroup clientclient Client proxies
+ * \ingroup clientsideproxies
+ *
+ * Proxy objects representing remote Telepathy Client objects (approvers,
+ * handlers and observers) and their optional interfaces.
+ */
+
+/**
+ * \defgroup clientcm Connection manager proxies
+ * \ingroup clientsideproxies
+ *
+ * Proxy objects representing remote Telepathy ConnectionManager objects and
+ * their optional interfaces.
+ */
+
+/**
+ * \defgroup clientconn Connection proxies
+ * \ingroup clientsideproxies
+ *
+ * Proxy objects representing remote Telepathy Connection objects and their
+ * optional interfaces.
+ */
+
+/**
+ * \defgroup wrappers Wrapper classes
+ *
+ * Wrapper classes representing a Telepathy type.
+ */
+
+/**
+ * \defgroup utils Utililty classes
+ *
+ * Utility classes.
+ */
diff --git a/TelepathyQt/handled-channel-notifier.cpp b/TelepathyQt/handled-channel-notifier.cpp
new file mode 100644
index 00000000..47b2b55b
--- /dev/null
+++ b/TelepathyQt/handled-channel-notifier.cpp
@@ -0,0 +1,103 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/HandledChannelNotifier>
+
+#include "TelepathyQt/_gen/handled-channel-notifier.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+#include "TelepathyQt/request-temporary-handler-internal.h"
+
+#include <TelepathyQt/ChannelRequestHints>
+#include <TelepathyQt/ClientRegistrar>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT HandledChannelNotifier::Private
+{
+ Private(const ClientRegistrarPtr &cr,
+ const SharedPtr<RequestTemporaryHandler> &handler)
+ : cr(cr),
+ handler(handler),
+ channel(handler->channel())
+ {
+ }
+
+ ClientRegistrarPtr cr;
+ SharedPtr<RequestTemporaryHandler> handler;
+ ChannelPtr channel; // needed to keep channel alive, since RTH maintains only a weak ref
+};
+
+/**
+ * \class HandledChannelNotifier
+ * \ingroup clientchannel
+ * \headerfile TelepathyQt/handled-channel-notifier.h <TelepathyQt/HandledChannelNotifier>
+ *
+ * \brief The HandledChannelNotifier class can be used to keep track of
+ * channel() being re-requested.
+ *
+ * Instances of this class cannot be constructed directly; the only way to get
+ * one is trough PendingChannel.
+ */
+
+HandledChannelNotifier::HandledChannelNotifier(const ClientRegistrarPtr &cr,
+ const SharedPtr<RequestTemporaryHandler> &handler)
+ : mPriv(new Private(cr, handler))
+{
+ connect(mPriv->channel.data(),
+ SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
+ SLOT(onChannelInvalidated()));
+ connect(handler.data(),
+ SIGNAL(channelReceived(Tp::ChannelPtr,QDateTime,Tp::ChannelRequestHints)),
+ SLOT(onChannelReceived(Tp::ChannelPtr,QDateTime,Tp::ChannelRequestHints)));
+}
+
+HandledChannelNotifier::~HandledChannelNotifier()
+{
+ delete mPriv;
+}
+
+ChannelPtr HandledChannelNotifier::channel() const
+{
+ return mPriv->channel;
+}
+
+void HandledChannelNotifier::onChannelReceived(const Tp::ChannelPtr &channel,
+ const QDateTime &userActionTime, const Tp::ChannelRequestHints &requestHints)
+{
+ emit handledAgain(userActionTime, requestHints);
+}
+
+void HandledChannelNotifier::onChannelInvalidated()
+{
+ deleteLater();
+}
+
+void HandledChannelNotifier::connectNotify(const char *signalName)
+{
+ if (qstrcmp(signalName, SIGNAL(handledAgain(QDateTime,Tp::ChannelRequestHints))) == 0) {
+ mPriv->handler->setQueueChannelReceived(false);
+ }
+}
+
+} // Tp
diff --git a/TelepathyQt/handled-channel-notifier.h b/TelepathyQt/handled-channel-notifier.h
new file mode 100644
index 00000000..0dab6af7
--- /dev/null
+++ b/TelepathyQt/handled-channel-notifier.h
@@ -0,0 +1,75 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_handled_channel_notifier_h_HEADER_GUARD_
+#define _TelepathyQt_handled_channel_notifier_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Channel>
+#include <TelepathyQt/Types>
+
+#include <QObject>
+
+namespace Tp
+{
+
+class ChannelRequestHints;
+class RequestTemporaryHandler;
+
+class TP_QT_EXPORT HandledChannelNotifier : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(HandledChannelNotifier)
+
+public:
+ ~HandledChannelNotifier();
+
+ ChannelPtr channel() const;
+
+Q_SIGNALS:
+ void handledAgain(const QDateTime &userActionTime, const Tp::ChannelRequestHints &requestHints);
+
+protected:
+ void connectNotify(const char *);
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onChannelReceived(const Tp::ChannelPtr &channel,
+ const QDateTime &userActionTime, const Tp::ChannelRequestHints &requestHints);
+ TP_QT_NO_EXPORT void onChannelInvalidated();
+
+private:
+ friend class PendingChannel;
+
+ HandledChannelNotifier(const ClientRegistrarPtr &cr,
+ const SharedPtr<RequestTemporaryHandler> &handler);
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/incoming-file-transfer-channel.cpp b/TelepathyQt/incoming-file-transfer-channel.cpp
new file mode 100644
index 00000000..3af20197
--- /dev/null
+++ b/TelepathyQt/incoming-file-transfer-channel.cpp
@@ -0,0 +1,390 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/IncomingFileTransferChannel>
+
+#include "TelepathyQt/_gen/incoming-file-transfer-channel.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/Connection>
+#include <TelepathyQt/PendingFailure>
+#include <TelepathyQt/PendingVariant>
+#include <TelepathyQt/Types>
+#include <TelepathyQt/types-internal.h>
+
+#include <QIODevice>
+#include <QTcpSocket>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT IncomingFileTransferChannel::Private
+{
+ Private(IncomingFileTransferChannel *parent);
+ ~Private();
+
+ // Public object
+ IncomingFileTransferChannel *parent;
+
+ Client::ChannelTypeFileTransferInterface *fileTransferInterface;
+
+ QIODevice *output;
+ QTcpSocket *socket;
+ SocketAddressIPv4 addr;
+
+ qulonglong requestedOffset;
+ qint64 pos;
+};
+
+IncomingFileTransferChannel::Private::Private(IncomingFileTransferChannel *parent)
+ : parent(parent),
+ fileTransferInterface(parent->interface<Client::ChannelTypeFileTransferInterface>()),
+ output(0),
+ socket(0),
+ requestedOffset(0),
+ pos(0)
+{
+ parent->connect(fileTransferInterface,
+ SIGNAL(URIDefined(QString)),
+ SLOT(onUriDefined(QString)));
+ parent->connect(fileTransferInterface,
+ SIGNAL(URIDefined(QString)),
+ SIGNAL(uriDefined(QString)));
+}
+
+IncomingFileTransferChannel::Private::~Private()
+{
+}
+
+/**
+ * \class IncomingFileTransferChannel
+ * \ingroup clientchannel
+ * \headerfile TelepathyQt/incoming-file-transfer-channel.h <TelepathyQt/IncomingFileTransferChannel>
+ *
+ * \brief The IncomingFileTransferChannel class represents a Telepathy channel
+ * of type FileTransfer for incoming file transfers.
+ *
+ * For more details, please refer to \telepathy_spec.
+ *
+ * See \ref async_model, \ref shared_ptr
+ */
+
+/**
+ * Feature representing the core that needs to become ready to make the
+ * IncomingFileTransferChannel object usable.
+ *
+ * This is currently the same as FileTransferChannel::FeatureCore, but may change to include more.
+ *
+ * When calling isReady(), becomeReady(), this feature is implicitly added
+ * to the requested features.
+ */
+const Feature IncomingFileTransferChannel::FeatureCore =
+ Feature(QLatin1String(FileTransferChannel::staticMetaObject.className()), 0); // FT::FeatureCore
+
+/**
+ * Create a new IncomingFileTransferChannel object.
+ *
+ * \param connection Connection owning this channel, and specifying the
+ * service.
+ * \param objectPath The channel object path.
+ * \param immutableProperties The channel immutable properties.
+ * \return A IncomingFileTransferChannelPtr object pointing to the newly created
+ * IncomingFileTransfer object.
+ */
+IncomingFileTransferChannelPtr IncomingFileTransferChannel::create(
+ const ConnectionPtr &connection, const QString &objectPath,
+ const QVariantMap &immutableProperties)
+{
+ return IncomingFileTransferChannelPtr(new IncomingFileTransferChannel(
+ connection, objectPath, immutableProperties,
+ IncomingFileTransferChannel::FeatureCore));
+}
+
+/**
+ * Construct a new IncomingFileTransferChannel object.
+ *
+ * \param connection Connection owning this channel, and specifying the
+ * service.
+ * \param objectPath The channel object path.
+ * \param immutableProperties The channel immutable properties.
+ * \param coreFeature The core feature of the channel type, if any. The corresponding introspectable should
+ * depend on IncomingFileTransferChannel::FeatureCore.
+ */
+IncomingFileTransferChannel::IncomingFileTransferChannel(
+ const ConnectionPtr &connection, const QString &objectPath,
+ const QVariantMap &immutableProperties,
+ const Feature &coreFeature)
+ : FileTransferChannel(connection, objectPath, immutableProperties, coreFeature),
+ mPriv(new Private(this))
+{
+}
+
+/**
+ * Class destructor.
+ */
+IncomingFileTransferChannel::~IncomingFileTransferChannel()
+{
+ delete mPriv;
+}
+
+/**
+ * Set the URI where the file will be saved.
+ *
+ * This property may be set by the channel handler before calling AcceptFile to inform observers
+ * where the incoming file will be saved. When the URI property is set, the signal
+ * uriDefined() is emitted.
+ *
+ * This method requires IncomingFileTransferChannel::FeatureCore to be ready.
+ *
+ * \param uri The URI where the file will be saved.
+ * \return A PendingOperation object which will emit PendingOperation::finished
+ * when the call has finished.
+ * \sa FileTransferChannel::uri(), uriDefined()
+ */
+PendingOperation *IncomingFileTransferChannel::setUri(const QString& uri)
+{
+ if (!isReady(FileTransferChannel::FeatureCore)) {
+ warning() << "FileTransferChannel::FeatureCore must be ready before "
+ "calling setUri";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Channel not ready"),
+ IncomingFileTransferChannelPtr(this));
+ }
+
+ if (state() != FileTransferStatePending) {
+ warning() << "setUri must be called before calling acceptFile";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Cannot set URI after calling acceptFile"),
+ IncomingFileTransferChannelPtr(this));
+ }
+
+ return mPriv->fileTransferInterface->setPropertyURI(uri);
+}
+
+/**
+ * Accept a file transfer that's in the #FileTransferStatePending state().
+ *
+ * The state will change to #FileTransferStateOpen as soon as the transfer
+ * starts.
+ * The given output device should not be closed/destroyed until the state()
+ * changes to #FileTransferStateCompleted or #FileTransferStateCancelled.
+ *
+ * Only the primary handler of a file transfer channel may call this method.
+ *
+ * This method requires IncomingFileTransferChannel::FeatureCore to be ready.
+ *
+ * \param offset The desired offset in bytes where the file transfer should
+ * start. The offset is taken from the beginning of the file.
+ * Specifying an offset of zero will start the transfer from the
+ * beginning of the file. The offset that is actually given in the
+ * initialOffset() method can differ from this argument where the
+ * requested offset is not supported. (For example, some
+ * protocols do not support offsets at all so the initialOffset()
+ * will always be 0.).
+ * \param output A QIODevice object where the data will be written to. The
+ * device should be ready to use when the state() changes to
+ * #FileTransferStateCompleted.
+ * If the transfer is cancelled, state() becomes
+ * #FileTransferStateCancelled, the data in \a output should be
+ * ignored
+ * \return A PendingOperation object which will emit PendingOperation::finished
+ * when the call has finished.
+ * \sa FileTransferChannel::stateChanged(), FileTransferChannel::state(),
+ * FileTransferChannel::stateReason(), FileTransferChannel::initialOffset()
+ */
+PendingOperation *IncomingFileTransferChannel::acceptFile(qulonglong offset,
+ QIODevice *output)
+{
+ if (!isReady(FileTransferChannel::FeatureCore)) {
+ warning() << "FileTransferChannel::FeatureCore must be ready before "
+ "calling acceptFile";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Channel not ready"),
+ IncomingFileTransferChannelPtr(this));
+ }
+
+ // let's fail here direclty as we may only have one device to handle
+ if (mPriv->output) {
+ warning() << "File transfer can only be started once in the same "
+ "channel";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("File transfer can only be started once in the same channel"),
+ IncomingFileTransferChannelPtr(this));
+ }
+
+ if ((!output->isOpen() && !output->open(QIODevice::WriteOnly)) &&
+ (!output->isWritable())) {
+ warning() << "Unable to open IO device for writing";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_PERMISSION_DENIED),
+ QLatin1String("Unable to open IO device for writing"),
+ IncomingFileTransferChannelPtr(this));
+ }
+
+ mPriv->output = output;
+
+ mPriv->requestedOffset = offset;
+
+ PendingVariant *pv = new PendingVariant(
+ mPriv->fileTransferInterface->AcceptFile(SocketAddressTypeIPv4,
+ SocketAccessControlLocalhost, QDBusVariant(QVariant(QString())),
+ offset),
+ IncomingFileTransferChannelPtr(this));
+ connect(pv,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onAcceptFileFinished(Tp::PendingOperation*)));
+ return pv;
+}
+
+void IncomingFileTransferChannel::onAcceptFileFinished(PendingOperation *op)
+{
+ if (op->isError()) {
+ warning() << "Error accepting file transfer " <<
+ op->errorName() << ":" << op->errorMessage();
+ invalidate(op->errorName(), op->errorMessage());
+ return;
+ }
+
+ PendingVariant *pv = qobject_cast<PendingVariant *>(op);
+ mPriv->addr = qdbus_cast<SocketAddressIPv4>(pv->result());
+ debug().nospace() << "Got address " << mPriv->addr.address <<
+ ":" << mPriv->addr.port;
+
+ if (state() == FileTransferStateOpen) {
+ // now we have the address and we are already opened,
+ // connect to host
+ connectToHost();
+ }
+}
+
+void IncomingFileTransferChannel::connectToHost()
+{
+ if (isConnected() || mPriv->addr.address.isNull()) {
+ return;
+ }
+
+ // we already have initialOffsetDefined, called before State became Open, so
+ // let's make sure everything is ok.
+ if (initialOffset() > mPriv->requestedOffset) {
+ // either the CM or the sender is doing something really wrong here,
+ // cancel the transfer.
+ warning() << "InitialOffset bigger than requested offset, "
+ "cancelling the transfer";
+ cancel();
+ invalidate(TP_QT_ERROR_INCONSISTENT,
+ QLatin1String("Initial offset bigger than requested offset"));
+ return;
+ }
+
+ mPriv->pos = initialOffset();
+
+ mPriv->socket = new QTcpSocket(this);
+
+ connect(mPriv->socket, SIGNAL(connected()),
+ SLOT(onSocketConnected()));
+ connect(mPriv->socket, SIGNAL(disconnected()),
+ SLOT(onSocketDisconnected()));
+ connect(mPriv->socket, SIGNAL(error(QAbstractSocket::SocketError)),
+ SLOT(onSocketError(QAbstractSocket::SocketError)));
+ connect(mPriv->socket, SIGNAL(readyRead()),
+ SLOT(doTransfer()));
+
+ debug().nospace() << "Connecting to host " <<
+ mPriv->addr.address << ":" << mPriv->addr.port << "...";
+ mPriv->socket->connectToHost(mPriv->addr.address, mPriv->addr.port);
+}
+
+void IncomingFileTransferChannel::onSocketConnected()
+{
+ debug() << "Connected to host";
+ setConnected();
+
+ doTransfer();
+}
+
+void IncomingFileTransferChannel::onSocketDisconnected()
+{
+ debug() << "Disconnected from host";
+ setFinished();
+}
+
+void IncomingFileTransferChannel::onSocketError(QAbstractSocket::SocketError error)
+{
+ setFinished();
+}
+
+void IncomingFileTransferChannel::doTransfer()
+{
+ QByteArray data;
+ while (mPriv->socket->bytesAvailable()) {
+ data = mPriv->socket->readAll();
+
+ // skip until we reach requetedOffset and start writing from there
+ if ((qulonglong) mPriv->pos < mPriv->requestedOffset) {
+ if ((qulonglong) data.length() <= (mPriv->requestedOffset - mPriv->pos)) {
+ break;
+ }
+ data = data.mid(mPriv->requestedOffset - mPriv->pos);
+ }
+
+ mPriv->output->write(data); // never fails
+ }
+
+ mPriv->pos += data.length();
+}
+
+void IncomingFileTransferChannel::setFinished()
+{
+ if (isFinished()) {
+ // it shouldn't happen but let's make sure
+ return;
+ }
+
+ if (mPriv->socket) {
+ disconnect(mPriv->socket, SIGNAL(connected()),
+ this, SLOT(onSocketConnected()));
+ disconnect(mPriv->socket, SIGNAL(disconnected()),
+ this, SLOT(onSocketDisconnected()));
+ disconnect(mPriv->socket, SIGNAL(error(QAbstractSocket::SocketError)),
+ this, SLOT(onSocketError(QAbstractSocket::SocketError)));
+ disconnect(mPriv->socket, SIGNAL(readyRead()),
+ this, SLOT(doTransfer()));
+ mPriv->socket->close();
+ }
+
+ if (mPriv->output) {
+ mPriv->output->close();
+ }
+
+ FileTransferChannel::setFinished();
+}
+
+/**
+ * \fn void IncomingFileTransferChannel::uriDefined(const QString &uri)
+ *
+ * Emitted when the value of uri() changes.
+ *
+ * \param uri The new URI of this file transfer channel.
+ * \sa FileTransferChannel::uri(), setUri()
+ */
+
+} // Tp
diff --git a/TelepathyQt/incoming-file-transfer-channel.h b/TelepathyQt/incoming-file-transfer-channel.h
new file mode 100644
index 00000000..01937d3f
--- /dev/null
+++ b/TelepathyQt/incoming-file-transfer-channel.h
@@ -0,0 +1,81 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_incoming_file_transfer_channel_h_HEADER_GUARD_
+#define _TelepathyQt_incoming_file_transfer_channel_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/FileTransferChannel>
+
+#include <QAbstractSocket>
+
+namespace Tp
+{
+
+class TP_QT_EXPORT IncomingFileTransferChannel : public FileTransferChannel
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(IncomingFileTransferChannel)
+
+public:
+ static const Feature FeatureCore;
+
+ static IncomingFileTransferChannelPtr create(const ConnectionPtr &connection,
+ const QString &objectPath, const QVariantMap &immutableProperties);
+
+ virtual ~IncomingFileTransferChannel();
+
+ PendingOperation *setUri(const QString& uri);
+ PendingOperation *acceptFile(qulonglong offset, QIODevice *output);
+
+Q_SIGNALS:
+ void uriDefined(const QString &uri);
+
+protected:
+ IncomingFileTransferChannel(const ConnectionPtr &connection,
+ const QString &objectPath,
+ const QVariantMap &immutableProperties,
+ const Feature &coreFeature = IncomingFileTransferChannel::FeatureCore);
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onAcceptFileFinished(Tp::PendingOperation *op);
+
+ TP_QT_NO_EXPORT void onSocketConnected();
+ TP_QT_NO_EXPORT void onSocketDisconnected();
+ TP_QT_NO_EXPORT void onSocketError(QAbstractSocket::SocketError error);
+ TP_QT_NO_EXPORT void doTransfer();
+
+private:
+ TP_QT_NO_EXPORT void connectToHost();
+ TP_QT_NO_EXPORT void setFinished();
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/incoming-stream-tube-channel.cpp b/TelepathyQt/incoming-stream-tube-channel.cpp
new file mode 100644
index 00000000..d73d011d
--- /dev/null
+++ b/TelepathyQt/incoming-stream-tube-channel.cpp
@@ -0,0 +1,435 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010-2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/IncomingStreamTubeChannel>
+
+#include "TelepathyQt/_gen/incoming-stream-tube-channel.moc.hpp"
+
+#include "TelepathyQt/types-internal.h"
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/PendingFailure>
+#include <TelepathyQt/PendingStreamTubeConnection>
+#include <TelepathyQt/PendingVariant>
+#include <TelepathyQt/Types>
+
+#include <QHostAddress>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT IncomingStreamTubeChannel::Private
+{
+ Private(IncomingStreamTubeChannel *parent);
+
+ // Public object
+ IncomingStreamTubeChannel *parent;
+ static bool initRandom;
+};
+
+bool IncomingStreamTubeChannel::Private::initRandom = true;
+
+IncomingStreamTubeChannel::Private::Private(IncomingStreamTubeChannel *parent)
+ : parent(parent)
+{
+}
+
+/**
+ * \class IncomingStreamTubeChannel
+ * \ingroup clientchannel
+ * \headerfile TelepathyQt/incoming-stream-tube-channel.h <TelepathyQt/IncomingStreamTubeChannel>
+ *
+ * \brief The IncomingStreamTubeChannel class represents an incoming Telepathy channel
+ * of type StreamTube.
+ *
+ * In particular, this class is meant to be used as a comfortable way for
+ * accepting incoming stream tubes. Tubes can be accepted as TCP and/or Unix sockets with various
+ * access control methods depending on what the service supports using the various overloads of
+ * acceptTubeAsTcpSocket() and acceptTubeAsUnixSocket().
+ *
+ * Once a tube is successfully accepted and open (the PendingStreamTubeConnection returned from the
+ * accepting methods has finished), the application can connect to the socket the address of which
+ * can be retrieved from PendingStreamTubeConnection::ipAddress() and/or
+ * PendingStreamTubeConnection::localAddress() depending on which accepting method was used.
+ * Connecting to this socket will open a tunneled connection to the service listening at the
+ * offering end of the tube.
+ *
+ * For more details, please refer to \telepathy_spec.
+ *
+ * See \ref async_model, \ref shared_ptr
+ */
+
+/**
+ * Feature representing the core that needs to become ready to make the
+ * IncomingStreamTubeChannel object usable.
+ *
+ * This is currently the same as StreamTubeChannel::FeatureCore, but may change to include more.
+ *
+ * When calling isReady(), becomeReady(), this feature is implicitly added
+ * to the requested features.
+ */
+const Feature IncomingStreamTubeChannel::FeatureCore =
+ Feature(QLatin1String(StreamTubeChannel::staticMetaObject.className()), 0); // ST::FeatureCore
+
+/**
+ * Create a new IncomingStreamTubeChannel object.
+ *
+ * \param connection Connection owning this channel, and specifying the
+ * service.
+ * \param objectPath The channel object path.
+ * \param immutableProperties The channel immutable properties.
+ * \return A IncomingStreamTubeChannelPtr object pointing to the newly created
+ * IncomingStreamTubeChannel object.
+ */
+IncomingStreamTubeChannelPtr IncomingStreamTubeChannel::create(const ConnectionPtr &connection,
+ const QString &objectPath, const QVariantMap &immutableProperties)
+{
+ return IncomingStreamTubeChannelPtr(new IncomingStreamTubeChannel(connection, objectPath,
+ immutableProperties, IncomingStreamTubeChannel::FeatureCore));
+}
+
+/**
+ * Construct a new IncomingStreamTubeChannel object.
+ *
+ * \param connection Connection owning this channel, and specifying the
+ * service.
+ * \param objectPath The channel object path.
+ * \param immutableProperties The channel immutable properties.
+ * \param coreFeature The core feature of the channel type, if any. The corresponding introspectable should
+ * depend on IncomingStreamTubeChannel::FeatureCore.
+ */
+IncomingStreamTubeChannel::IncomingStreamTubeChannel(const ConnectionPtr &connection,
+ const QString &objectPath,
+ const QVariantMap &immutableProperties,
+ const Feature &coreFeature)
+ : StreamTubeChannel(connection, objectPath,
+ immutableProperties, coreFeature),
+ mPriv(new Private(this))
+{
+}
+
+/**
+ * Class destructor.
+ */
+IncomingStreamTubeChannel::~IncomingStreamTubeChannel()
+{
+ delete mPriv;
+}
+
+/**
+ * Accept an incoming stream tube as a TCP socket.
+ *
+ * This method accepts an incoming connection request for a stream tube. It can be called
+ * only if the tube is in the #TubeStateLocalPending state.
+ *
+ * The connection manager will open a TCP socket for the application to connect to. The address of
+ * the socket will be returned in PendingStreamTubeConnection::ipAddress() once the operation has
+ * finished successfully.
+ *
+ * This overload lets you specify an allowed address/port combination for connecting to the CM
+ * socket. Connections with other source addresses won't be accepted. The accessors
+ * supportsIPv4SocketsWithSpecifiedAddress() and supportsIPv6SocketsWithSpecifiedAddress() can be
+ * used to verify that the connection manager supports this kind of access control; otherwise, this
+ * method will always fail unless QHostAddress::Any or QHostAddress::AnyIPv6 is passed, in which
+ * case the behavior is identical to the always supported acceptTubeAsTcpSocket() overload.
+ *
+ * Note that when using QHostAddress::Any or QHostAddress::AnyIPv6, \a allowedPort is ignored.
+ *
+ * This method requires IncomingStreamTubeChannel::FeatureCore to be ready.
+ *
+ * \param allowedAddress An allowed address for connecting to the socket.
+ * \param allowedPort An allowed port for connecting to the socket.
+ * \return A PendingStreamTubeConnection which will emit PendingStreamTubeConnection::finished
+ * when the stream tube is ready to be used
+ * (hence in the #TubeStateOpen state).
+ */
+PendingStreamTubeConnection *IncomingStreamTubeChannel::acceptTubeAsTcpSocket(
+ const QHostAddress &allowedAddress,
+ quint16 allowedPort)
+{
+ if (!isReady(IncomingStreamTubeChannel::FeatureCore)) {
+ warning() << "IncomingStreamTubeChannel::FeatureCore must be ready before "
+ "calling acceptTubeAsTcpSocket";
+ return new PendingStreamTubeConnection(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Channel not ready"),
+ IncomingStreamTubeChannelPtr(this));
+ }
+
+ // The tube must be in local pending state
+ if (state() != TubeChannelStateLocalPending) {
+ warning() << "You can accept tubes only when they are in LocalPending state";
+ return new PendingStreamTubeConnection(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Channel not ready"),
+ IncomingStreamTubeChannelPtr(this));
+ }
+
+ QVariant controlParameter;
+ SocketAccessControl accessControl;
+
+ // Now, let's check what we need to do with accessControl. There is just one special case, Port.
+ if (allowedAddress != QHostAddress::Any && allowedAddress != QHostAddress::AnyIPv6) {
+ // We need to have a valid QHostAddress AND Port.
+ if (allowedAddress.isNull() || allowedPort == 0) {
+ warning() << "You have to set a valid allowed address+port to use Port access control";
+ return new PendingStreamTubeConnection(QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT),
+ QLatin1String("The supplied allowed address and/or port was invalid"),
+ IncomingStreamTubeChannelPtr(this));
+ }
+
+ accessControl = SocketAccessControlPort;
+
+ // IPv4 or IPv6?
+ if (allowedAddress.protocol() == QAbstractSocket::IPv4Protocol) {
+ // IPv4 case
+ SocketAddressIPv4 addr;
+ addr.address = allowedAddress.toString();
+ addr.port = allowedPort;
+
+ controlParameter = QVariant::fromValue(addr);
+ } else if (allowedAddress.protocol() == QAbstractSocket::IPv6Protocol) {
+ // IPv6 case
+ SocketAddressIPv6 addr;
+ addr.address = allowedAddress.toString();
+ addr.port = allowedPort;
+
+ controlParameter = QVariant::fromValue(addr);
+ } else {
+ // We're handling an IPv4/IPv6 socket only
+ warning() << "acceptTubeAsTcpSocket can be called only with a QHostAddress "
+ "representing an IPv4 or IPv6 address";
+ return new PendingStreamTubeConnection(QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT),
+ QLatin1String("Invalid host given"),
+ IncomingStreamTubeChannelPtr(this));
+ }
+ } else {
+ // We have to do no special stuff here
+ accessControl = SocketAccessControlLocalhost;
+ // Since QDBusMarshaller does not like null variants, just add an empty string.
+ controlParameter = QVariant(QString());
+ }
+
+ // Set the correct address type and access control
+ setAddressType(allowedAddress.protocol() == QAbstractSocket::IPv4Protocol ?
+ SocketAddressTypeIPv4 :
+ SocketAddressTypeIPv6);
+ setAccessControl(accessControl);
+
+ // Fail early if the combination is not supported
+ if ((accessControl == SocketAccessControlLocalhost &&
+ addressType() == SocketAddressTypeIPv4 &&
+ !supportsIPv4SocketsOnLocalhost()) ||
+ (accessControl == SocketAccessControlPort &&
+ addressType() == SocketAddressTypeIPv4 &&
+ !supportsIPv4SocketsWithSpecifiedAddress()) ||
+ (accessControl == SocketAccessControlLocalhost &&
+ addressType() == SocketAddressTypeIPv6 &&
+ !supportsIPv6SocketsOnLocalhost()) ||
+ (accessControl == SocketAccessControlPort &&
+ addressType() == SocketAddressTypeIPv6 &&
+ !supportsIPv6SocketsWithSpecifiedAddress())) {
+ warning() << "You requested an address type/access control combination "
+ "not supported by this channel";
+ return new PendingStreamTubeConnection(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("The requested address type/access control "
+ "combination is not supported"),
+ IncomingStreamTubeChannelPtr(this));
+ }
+
+ // Perform the actual call
+ PendingVariant *pv = new PendingVariant(
+ interface<Client::ChannelTypeStreamTubeInterface>()->Accept(
+ addressType(),
+ accessControl,
+ QDBusVariant(controlParameter)),
+ IncomingStreamTubeChannelPtr(this));
+
+ PendingStreamTubeConnection *op = new PendingStreamTubeConnection(pv, addressType(),
+ false, 0, IncomingStreamTubeChannelPtr(this));
+ return op;
+}
+
+/**
+ * Accept an incoming stream tube as a TCP socket.
+ *
+ * This method accepts an incoming connection request for a stream tube. It can be called
+ * only if the tube is in the #TubeStateLocalPending state.
+ *
+ * The connection manager will open a TCP socket for the application to connect to. The address of
+ * the socket will be returned in PendingStreamTubeConnection::ipAddress() once the operation has
+ * finished successfully.
+ *
+ * Using this overload, the connection manager will accept every incoming connection from localhost.
+ *
+ * This accept method must be supported by all connection managers adhering to the \telepathy_spec.
+ *
+ * This method requires IncomingStreamTubeChannel::FeatureCore to be ready.
+ *
+ * \return A PendingStreamTubeConnection which will emit PendingStreamTubeConnection::finished
+ * when the stream tube is ready to be used
+ * (hence in the #TubeStateOpen state).
+ */
+PendingStreamTubeConnection *IncomingStreamTubeChannel::acceptTubeAsTcpSocket()
+{
+ return acceptTubeAsTcpSocket(QHostAddress::Any, 0);
+}
+
+/**
+ * Accept an incoming stream tube as a Unix socket.
+ *
+ * This method accepts an incoming connection request for a stream tube. It can be called
+ * only if the tube is in the #TubeStateLocalPending state.
+ *
+ * An Unix socket (can be used with QLocalSocket or alike) will be opened by the connection manager
+ * as the local tube endpoint. This is only supported if supportsUnixSocketsOnLocalhost() is \c
+ * true.
+ *
+ * You can also specify whether the CM should require an SCM_CREDS or SCM_CREDENTIALS message
+ * upon connection instead of accepting every incoming connection from localhost. This provides
+ * additional security, but requires sending the byte retrieved from
+ * PendingStreamTubeConnection::credentialByte() in-line in the socket byte stream (in a credentials
+ * message if available on the platform), which might not be compatible with all protocols or
+ * libraries. Also, only connection managers for which supportsUnixSocketsWithCredentials() is \c
+ * true support this type of access control.
+ *
+ * This method requires IncomingStreamTubeChannel::FeatureCore to be ready.
+ *
+ * \param requireCredentials Whether the CM should require an SCM_CREDS or SCM_CREDENTIALS message
+ * upon connection.
+ * \return A PendingStreamTubeConnection which will emit PendingStreamTubeConnection::finished
+ * when the stream tube is ready to be used
+ * (hence in the #TubeStateOpen state).
+ * \sa StreamTubeChannel::supportsUnixSocketsOnLocalhost(),
+ * StreamTubeChannel::supportsUnixSocketsWithCredentials(),
+ * StreamTubeChannel::supportsAbstractUnixSocketsOnLocalhost(),
+ * StreamTubeChannel::supportsAbstractUnixSocketsWithCredentials()
+ */
+PendingStreamTubeConnection *IncomingStreamTubeChannel::acceptTubeAsUnixSocket(
+ bool requireCredentials)
+{
+ if (!isReady(IncomingStreamTubeChannel::FeatureCore)) {
+ warning() << "IncomingStreamTubeChannel::FeatureCore must be ready before "
+ "calling acceptTubeAsUnixSocket";
+ return new PendingStreamTubeConnection(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Channel not ready"),
+ IncomingStreamTubeChannelPtr(this));
+ }
+
+ // The tube must be in local pending state
+ if (state() != TubeChannelStateLocalPending) {
+ warning() << "You can accept tubes only when they are in LocalPending state";
+ return new PendingStreamTubeConnection(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Channel not ready"),
+ IncomingStreamTubeChannelPtr(this));
+ }
+
+ SocketAccessControl accessControl = requireCredentials ?
+ SocketAccessControlCredentials :
+ SocketAccessControlLocalhost;
+ setAddressType(SocketAddressTypeUnix);
+ setAccessControl(accessControl);
+
+ // Fail early if the combination is not supported
+ if ((accessControl == SocketAccessControlLocalhost &&
+ addressType() == SocketAddressTypeUnix &&
+ !supportsUnixSocketsOnLocalhost()) ||
+ (accessControl == SocketAccessControlCredentials &&
+ addressType() == SocketAddressTypeUnix &&
+ !supportsUnixSocketsWithCredentials()) ||
+ (accessControl == SocketAccessControlLocalhost &&
+ addressType() == SocketAddressTypeAbstractUnix &&
+ !supportsAbstractUnixSocketsOnLocalhost()) ||
+ (accessControl == SocketAccessControlCredentials &&
+ addressType() == SocketAddressTypeAbstractUnix &&
+ !supportsAbstractUnixSocketsWithCredentials())) {
+ warning() << "You requested an address type/access control combination "
+ "not supported by this channel";
+ return new PendingStreamTubeConnection(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("The requested address type/access control "
+ "combination is not supported"),
+ IncomingStreamTubeChannelPtr(this));
+ }
+
+ QDBusVariant accessControlParam;
+ uchar credentialByte = 0;
+ if (accessControl == SocketAccessControlLocalhost) {
+ accessControlParam.setVariant(qVariantFromValue(static_cast<uint>(0)));
+ } else if (accessControl == SocketAccessControlCredentials) {
+ if (mPriv->initRandom) {
+ qsrand(QTime::currentTime().msec());
+ mPriv->initRandom = false;
+ }
+ credentialByte = static_cast<uchar>(qrand());
+ accessControlParam.setVariant(qVariantFromValue(credentialByte));
+ } else {
+ Q_ASSERT(false);
+ }
+
+ // Perform the actual call
+ PendingVariant *pv = new PendingVariant(
+ interface<Client::ChannelTypeStreamTubeInterface>()->Accept(
+ addressType(),
+ accessControl,
+ accessControlParam),
+ IncomingStreamTubeChannelPtr(this));
+
+ PendingStreamTubeConnection *op = new PendingStreamTubeConnection(pv, addressType(),
+ requireCredentials, credentialByte, IncomingStreamTubeChannelPtr(this));
+ return op;
+}
+
+/**
+ * Return the local address of the opened stream tube.
+ *
+ * Calling this method when the tube has not been opened will cause it
+ * to return an undefined value. The same will happen if the tube has been accepted as a TCP
+ * socket. Use ipAddress() if that is the case.
+ *
+ * \return Unix socket address if using an Unix socket,
+ * or an undefined value otherwise.
+ * \sa acceptTubeAsUnixSocket(), ipAddress()
+ */
+QString IncomingStreamTubeChannel::localAddress() const
+{
+ return StreamTubeChannel::localAddress();
+}
+
+/**
+ * Return the IP address/port combination of the opened stream tube.
+ *
+ * Calling this method when the tube has not been opened will cause it
+ * to return an undefined value. The same will happen if the tube has been accepted as an Unix
+ * socket. Use localAddress() if that is the case.
+ *
+ * \return Pair of IP address as QHostAddress and port if using a TCP socket,
+ * or an undefined value otherwise.
+ * \sa acceptTubeAsTcpSocket(), localAddress()
+ */
+QPair<QHostAddress, quint16> IncomingStreamTubeChannel::ipAddress() const
+{
+ return StreamTubeChannel::ipAddress();
+}
+
+void IncomingStreamTubeChannel::onNewLocalConnection(uint connectionId)
+{
+ addConnection(connectionId);
+}
+
+}
diff --git a/TelepathyQt/incoming-stream-tube-channel.h b/TelepathyQt/incoming-stream-tube-channel.h
new file mode 100644
index 00000000..1dff19cb
--- /dev/null
+++ b/TelepathyQt/incoming-stream-tube-channel.h
@@ -0,0 +1,80 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010-2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_incoming_stream_tube_channel_h_HEADER_GUARD_
+#define _TelepathyQt_incoming_stream_tube_channel_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/StreamTubeChannel>
+
+#include <QtNetwork/QHostAddress>
+
+class QIODevice;
+
+namespace Tp
+{
+
+class PendingStreamTubeConnection;
+
+class TP_QT_EXPORT IncomingStreamTubeChannel : public StreamTubeChannel
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(IncomingStreamTubeChannel)
+
+public:
+ static const Feature FeatureCore;
+
+ static IncomingStreamTubeChannelPtr create(const ConnectionPtr &connection,
+ const QString &objectPath, const QVariantMap &immutableProperties);
+
+ virtual ~IncomingStreamTubeChannel();
+
+ PendingStreamTubeConnection *acceptTubeAsTcpSocket();
+ PendingStreamTubeConnection *acceptTubeAsTcpSocket(const QHostAddress &allowedAddress,
+ quint16 allowedPort);
+ PendingStreamTubeConnection *acceptTubeAsUnixSocket(bool requireCredentials = false);
+
+ // FIXME: (API/ABI break) Remove ipAddress() and localAddress() (already in StreamTubeChannel)
+ QPair<QHostAddress, quint16> ipAddress() const;
+ QString localAddress() const;
+
+protected:
+ IncomingStreamTubeChannel(const ConnectionPtr &connection,
+ const QString &objectPath,
+ const QVariantMap &immutableProperties,
+ const Feature &coreFeature = IncomingStreamTubeChannel::FeatureCore);
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onNewLocalConnection(uint connectionId);
+
+private:
+ struct Private;
+ friend class PendingStreamTubeConnection;
+ friend struct Private;
+ Private *mPriv;
+};
+
+}
+
+#endif
diff --git a/TelepathyQt/key-file.cpp b/TelepathyQt/key-file.cpp
new file mode 100644
index 00000000..a5ca9f46
--- /dev/null
+++ b/TelepathyQt/key-file.cpp
@@ -0,0 +1,582 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/KeyFile>
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/Utils>
+
+#include <QtCore/QByteArray>
+#include <QtCore/QFile>
+#include <QtCore/QHash>
+#include <QtCore/QString>
+#include <QtCore/QStringList>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT KeyFile::Private
+{
+ Private();
+ Private(const QString &fName);
+
+ void setFileName(const QString &fName);
+ void setError(KeyFile::Status status, const QString &reason);
+ bool read();
+
+ bool validateKey(const QByteArray &data, int from, int to, QString &result);
+
+ QStringList allGroups() const;
+ QStringList allKeys() const;
+ QStringList keys() const;
+ bool contains(const QString &key) const;
+ QString rawValue(const QString &key) const;
+ QString value(const QString &key) const;
+ QStringList valueAsStringList(const QString &key) const;
+
+ QString fileName;
+ KeyFile::Status status;
+ QHash<QString, QHash<QString, QByteArray> > groups;
+ QString currentGroup;
+};
+
+KeyFile::Private::Private()
+ : status(KeyFile::None)
+{
+}
+
+KeyFile::Private::Private(const QString &fName)
+ : fileName(fName),
+ status(KeyFile::NoError)
+{
+ read();
+}
+
+void KeyFile::Private::setFileName(const QString &fName)
+{
+ fileName = fName;
+ status = KeyFile::NoError;
+ currentGroup = QString();
+ groups.clear();
+ read();
+}
+
+void KeyFile::Private::setError(KeyFile::Status st, const QString &reason)
+{
+ warning() << QString(QLatin1String("ERROR: filename(%1) reason(%2)"))
+ .arg(fileName).arg(reason);
+ status = st;
+ groups.clear();
+}
+
+bool KeyFile::Private::read()
+{
+ QFile file(fileName);
+ if (!file.exists()) {
+ setError(KeyFile::NotFoundError,
+ QLatin1String("file does not exist"));
+ return false;
+ }
+
+ if (!file.open(QFile::ReadOnly)) {
+ setError(KeyFile::AccessError,
+ QLatin1String("cannot open file for readonly access"));
+ return false;
+ }
+
+ QByteArray data;
+ QByteArray group;
+ QString currentGroup;
+ QHash<QString, QByteArray> groupMap;
+ QByteArray rawValue;
+ int line = 0;
+ int idx;
+ while (!file.atEnd()) {
+ data = file.readLine().trimmed();
+ line++;
+
+ if (data.size() == 0) {
+ // skip empty lines
+ continue;
+ }
+
+ char ch = data.at(0);
+ if (ch == '#') {
+ // skip comments
+ continue;
+ }
+ else if (ch == '[') {
+ if (groupMap.size()) {
+ groups[currentGroup] = groupMap;
+ groupMap.clear();
+ }
+
+ idx = data.indexOf(']');
+ if (idx == -1) {
+ // line starts with [ and it's not a group
+ setError(KeyFile::FormatError,
+ QString(QLatin1String("invalid group at line %2 - missing ']'"))
+ .arg(line));
+ return false;
+ }
+
+ group = data.mid(1, idx - 1).trimmed();
+ if (groups.contains(QLatin1String(group))) {
+ setError(KeyFile::FormatError,
+ QString(QLatin1String("duplicated group '%1' at line %2"))
+ .arg(QLatin1String(group)).arg(line));
+ return false;
+ }
+
+ currentGroup = QLatin1String("");
+ if (!unescapeString(group, 0, group.size(), currentGroup)) {
+ setError(KeyFile::FormatError,
+ QString(QLatin1String("invalid group '%1' at line %2"))
+ .arg(currentGroup).arg(line));
+ return false;
+ }
+ }
+ else {
+ idx = data.indexOf('=');
+ if (idx == -1) {
+ setError(KeyFile::FormatError,
+ QString(QLatin1String("format error at line %1 - missing '='"))
+ .arg(line));
+ return false;
+ }
+
+ // remove trailing spaces
+ char ch;
+ int idxKeyEnd = idx;
+ while ((ch = data.at(idxKeyEnd - 1)) == ' ' || ch == '\t') {
+ --idxKeyEnd;
+ }
+
+ QString key;
+ if (!validateKey(data, 0, idxKeyEnd, key)) {
+ setError(KeyFile::FormatError,
+ QString(QLatin1String("invalid key '%1' at line %2"))
+ .arg(key).arg(line));
+ return false;
+ }
+
+ if (groupMap.contains(key)) {
+ setError(KeyFile::FormatError,
+ QString(QLatin1String("duplicated key '%1' on group '%2' at line %3"))
+ .arg(key).arg(currentGroup).arg(line));
+ return false;
+ }
+
+ data = data.mid(idx + 1).trimmed();
+ rawValue = data.mid(0, data.size());
+ groupMap[key] = rawValue;
+ }
+ }
+
+ if (groupMap.size()) {
+ groups[currentGroup] = groupMap;
+ groupMap.clear();
+ }
+
+ return true;
+}
+
+bool KeyFile::Private::validateKey(const QByteArray &data, int from, int to, QString &result)
+{
+ int i = from;
+ bool ret = true;
+ while (i < to) {
+ uint ch = data.at(i++);
+ // as an extension to the Desktop Entry spec, we allow " ", "_", "." and "@"
+ // as valid key characters - "_" and "." are needed for keys that are
+ // D-Bus property names, and GKeyFile and KConfigIniBackend also accept
+ // all four of those characters.
+ if (!((ch >= 'a' && ch <= 'z') ||
+ (ch >= 'A' && ch <= 'Z') ||
+ (ch >= '0' && ch <= '9') ||
+ (ch == ' ') ||
+ (ch == '-') || (ch == '_') ||
+ (ch == '.') || (ch == '@'))) {
+ ret = false;
+ }
+ result += ch;
+ }
+ return ret;
+}
+
+QStringList KeyFile::Private::allGroups() const
+{
+ return groups.keys();
+}
+
+QStringList KeyFile::Private::allKeys() const
+{
+ QStringList keys;
+ QHash<QString, QHash<QString, QByteArray> >::const_iterator itrGroups = groups.begin();
+ while (itrGroups != groups.end()) {
+ keys << itrGroups.value().keys();
+ ++itrGroups;
+ }
+ return keys;
+}
+
+QStringList KeyFile::Private::keys() const
+{
+ QHash<QString, QByteArray> groupMap = groups[currentGroup];
+ return groupMap.keys();
+}
+
+bool KeyFile::Private::contains(const QString &key) const
+{
+ QHash<QString, QByteArray> groupMap = groups[currentGroup];
+ return groupMap.contains(key);
+}
+
+QString KeyFile::Private::rawValue(const QString &key) const
+{
+ QHash<QString, QByteArray> groupMap = groups[currentGroup];
+ QByteArray rawValue = groupMap.value(key);
+ return QLatin1String(rawValue);
+}
+
+QString KeyFile::Private::value(const QString &key) const
+{
+ QHash<QString, QByteArray> groupMap = groups[currentGroup];
+ QString result;
+ QByteArray rawValue = groupMap.value(key);
+ if (unescapeString(rawValue, 0, rawValue.size(), result)) {
+ return result;
+ }
+ return QString();
+}
+
+QStringList KeyFile::Private::valueAsStringList(const QString &key) const
+{
+ QHash<QString, QByteArray> groupMap = groups[currentGroup];
+ QStringList result;
+ QByteArray rawValue = groupMap.value(key);
+ if (unescapeStringList(rawValue, 0, rawValue.size(), result)) {
+ return result;
+ }
+ return QStringList();
+}
+
+/**
+ * \class KeyFile
+ * \ingroup utils
+ * \headerfile TelepathyQt/key-file.h <TelepathyQt/KeyFile>
+ *
+ * \brief The KeyFile class provides an easy way to read key-pair files such as
+ * INI style files and .desktop files.
+ *
+ * It follows the rules regarding string escaping as defined in
+ * http://standards.freedesktop.org/desktop-entry-spec/latest/index.html
+ *
+ * \todo Consider making this private, because the ConnectionManager and Account classes provide a
+ * nice view to the data parsed by this class anyway (and there's even a higher-level ManagerFile
+ * class in between) (fd.o #41655).
+ */
+
+/**
+ * Create a KeyFile object used to read (key-pair) compliant files.
+ *
+ * The status will be KeyFile::None
+ * \sa setFileName()
+ */
+KeyFile::KeyFile()
+ : mPriv(new Private())
+{
+}
+
+/**
+ * Create a KeyFile object used to read (key-pair) compliant files.
+ *
+ * \param fileName Name of the file to be read.
+ */
+KeyFile::KeyFile(const QString &fileName)
+ : mPriv(new Private(fileName))
+{
+}
+
+/**
+ * Create a KeyFile object used to read (key-pair) compliant files.
+ */
+KeyFile::KeyFile(const KeyFile &other)
+ : mPriv(new Private())
+{
+ mPriv->fileName = other.mPriv->fileName;
+ mPriv->status = other.mPriv->status;
+ mPriv->groups = other.mPriv->groups;
+ mPriv->currentGroup = other.mPriv->currentGroup;
+}
+
+/**
+ * Class destructor.
+ */
+KeyFile::~KeyFile()
+{
+ delete mPriv;
+}
+
+KeyFile &KeyFile::operator=(const KeyFile &other)
+{
+ mPriv->fileName = other.mPriv->fileName;
+ mPriv->status = other.mPriv->status;
+ mPriv->groups = other.mPriv->groups;
+ mPriv->currentGroup = other.mPriv->currentGroup;
+ return *this;
+}
+
+/**
+ * Set the name of the file to be read.
+ *
+ * \param fileName Name of the file to be read.
+ */
+void KeyFile::setFileName(const QString &fileName)
+{
+ mPriv->setFileName(fileName);
+}
+
+/**
+ * Return the name of the file associated with this object.
+ *
+ * \return Name of the file associated with this object.
+ */
+QString KeyFile::fileName() const
+{
+ return mPriv->fileName;
+}
+
+/**
+ * Return a status code indicating the first error that was met by #KeyFile,
+ * or KeyFile::NoError if no error occurred.
+ *
+ * Make sure to use this method if you set the filename to be read using
+ * setFileName().
+ *
+ * \return Status code.
+ * \sa setFileName()
+ */
+KeyFile::Status KeyFile::status() const
+{
+ return mPriv->status;
+}
+
+/**
+ * Set the current group to be used while reading keys.
+ *
+ * Query functions such as keys(), contains() and value() are based on this
+ * group.
+ *
+ * By default a empty group is used as the group for global
+ * keys and is used as the default group if none is set.
+ *
+ * \param group Name of the group to be used.
+ * \sa group()
+ */
+void KeyFile::setGroup(const QString &group)
+{
+ mPriv->currentGroup = group;
+}
+
+/**
+ * Return the current group.
+ *
+ * \return Name of the current group.
+ * \sa setGroup()
+ */
+QString KeyFile::group() const
+{
+ return mPriv->currentGroup;
+}
+
+/**
+ * Return all groups in the desktop file.
+ *
+ * Global keys will be added to a empty group.
+ *
+ * \return List of all groups in the desktop file.
+ */
+QStringList KeyFile::allGroups() const
+{
+ return mPriv->allGroups();
+}
+
+/**
+ * Return all keys described in the desktop file.
+ *
+ * \return List of all keys in the desktop file.
+ */
+QStringList KeyFile::allKeys() const
+{
+ return mPriv->allKeys();
+}
+
+/**
+ * Return a list of keys in the current group.
+ *
+ * \return List of all keys in the current group.
+ * \sa group(), setGroup()
+ */
+QStringList KeyFile::keys() const
+{
+ return mPriv->keys();
+}
+
+/**
+ * Check if the current group contains a key named \a key.
+ *
+ * \return true if \a key exists, false otherwise.
+ * \sa group(), setGroup()
+ */
+bool KeyFile::contains(const QString &key) const
+{
+ return mPriv->contains(key);
+}
+
+/**
+ * Get the raw value for the key in the current group named \a key.
+ *
+ * The raw value is the value as is in the key file.
+ *
+ * \return Value of \a key, empty string if not found.
+ * \sa group(), setGroup()
+ */
+QString KeyFile::rawValue(const QString &key) const
+{
+ return mPriv->rawValue(key);
+}
+
+/**
+ * Get the value for the key in the current group named \a key.
+ *
+ * Escape sequences in the value are interpreted as defined in:
+ * http://standards.freedesktop.org/desktop-entry-spec/latest/
+ *
+ * \return Value of \a key, empty string if not found or an error occurred.
+ * \sa group(), setGroup()
+ */
+QString KeyFile::value(const QString &key) const
+{
+ return mPriv->value(key);
+}
+
+/**
+ * Get the value for the key in the current group named \a key as a list.
+ *
+ * Return a list containing all strings on this key separated by ';'.
+ * Escape sequences in the value are interpreted as defined in:
+ * http://standards.freedesktop.org/desktop-entry-spec/latest/
+ *
+ * \return Value of \a key as a list, empty string list if not found or an error occurred.
+ * \sa group(), setGroup()
+ */
+QStringList KeyFile::valueAsStringList(const QString &key) const
+{
+ return mPriv->valueAsStringList(key);
+}
+
+bool KeyFile::unescapeString(const QByteArray &data, int from, int to, QString &result)
+{
+ int i = from;
+ while (i < to) {
+ uint ch = data.at(i++);
+
+ if (ch == '\\') {
+ if (i == to) {
+ result += QLatin1String("\\");
+ return true;
+ }
+
+ char nextCh = data.at(i++);
+ switch (nextCh) {
+ case 's':
+ result += QLatin1String(" ");
+ break;
+ case 'n':
+ result += QLatin1String("\n");
+ break;
+ case 't':
+ result += QLatin1String("\t");
+ break;
+ case 'r':
+ result += QLatin1String("\r");
+ break;
+ case ';':
+ result += QLatin1String(";");
+ break;
+ case '\\':
+ result += QLatin1String("\\");
+ break;
+ default:
+ return false;
+ }
+ } else {
+ result += ch;
+ }
+ }
+
+ return true;
+}
+
+bool KeyFile::unescapeStringList(const QByteArray &data, int from, int to, QStringList &result)
+{
+ QByteArray value;
+ QList<QByteArray> valueList;
+ int i = from;
+ char ch;
+ while (i < to) {
+ ch = data.at(i++);
+
+ if (ch == '\\') {
+ value += ch;
+ if (i < to) {
+ value += data.at(i++);
+ continue;
+ } else {
+ valueList << value;
+ break;
+ }
+ } else if (ch == ';') {
+ valueList << value;
+ value = "";
+ } else {
+ value += ch;
+ if (i == to) {
+ valueList << value;
+ }
+ }
+ }
+
+ foreach (value, valueList) {
+ QString str;
+ if (!unescapeString(value, 0, value.size(), str)) {
+ return false;
+ }
+ result << str;
+ }
+
+ return true;
+}
+
+} // Tp
diff --git a/TelepathyQt/key-file.h b/TelepathyQt/key-file.h
new file mode 100644
index 00000000..74d50d15
--- /dev/null
+++ b/TelepathyQt/key-file.h
@@ -0,0 +1,91 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_key_file_h_HEADER_GUARD_
+#define _TelepathyQt_key_file_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Global>
+
+#include <QMetaType>
+#include <QtGlobal>
+
+class QString;
+class QStringList;
+
+namespace Tp
+{
+
+class TP_QT_EXPORT KeyFile
+{
+public:
+ enum Status {
+ None = 0,
+ NoError,
+ NotFoundError,
+ AccessError,
+ FormatError,
+ };
+
+ KeyFile();
+ KeyFile(const KeyFile &other);
+ KeyFile(const QString &fileName);
+ ~KeyFile();
+
+ KeyFile &operator=(const KeyFile &other);
+
+ void setFileName(const QString &fileName);
+ QString fileName() const;
+
+ Status status() const;
+
+ void setGroup(const QString &group);
+ QString group() const;
+
+ QStringList allGroups() const;
+ QStringList allKeys() const;
+ QStringList keys() const;
+ bool contains(const QString &key) const;
+
+ QString rawValue(const QString &key) const;
+ QString value(const QString &key) const;
+ QStringList valueAsStringList(const QString &key) const;
+
+ static bool unescapeString(const QByteArray &data, int from, int to,
+ QString &result);
+ static bool unescapeStringList(const QByteArray &data, int from, int to,
+ QStringList &result);
+
+private:
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+}
+
+Q_DECLARE_METATYPE(Tp::KeyFile);
+
+#endif
diff --git a/TelepathyQt/location-info.cpp b/TelepathyQt/location-info.cpp
new file mode 100644
index 00000000..db7ac75b
--- /dev/null
+++ b/TelepathyQt/location-info.cpp
@@ -0,0 +1,221 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/LocationInfo>
+
+#include <QDBusArgument>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT LocationInfo::Private : public QSharedData
+{
+ QVariantMap location;
+};
+
+/**
+ * \class LocationInfo
+ * \ingroup clientconn
+ * \headerfile TelepathyQt/location-info.h <TelepathyQt/LocationInfo>
+ *
+ * \brief The LocationInfo class represents the location of a
+ * Telepathy Contact.
+ */
+
+/**
+ * Construct a new LocationInfo object.
+ */
+LocationInfo::LocationInfo()
+ : mPriv(new Private)
+{
+}
+
+LocationInfo::LocationInfo(const QVariantMap &location)
+ : mPriv(new Private)
+{
+ mPriv->location = location;
+}
+
+LocationInfo::LocationInfo(const LocationInfo &other)
+ : mPriv(other.mPriv)
+{
+}
+
+/**
+ * Class destructor.
+ */
+LocationInfo::~LocationInfo()
+{
+}
+
+LocationInfo &LocationInfo::operator=(const LocationInfo &other)
+{
+ this->mPriv = other.mPriv;
+ return *this;
+}
+
+QString LocationInfo::countryCode() const
+{
+ return qdbus_cast<QString>(mPriv->location.value(
+ QLatin1String("countrycode")));
+}
+
+QString LocationInfo::country() const
+{
+ return qdbus_cast<QString>(mPriv->location.value(
+ QLatin1String("country")));
+}
+
+QString LocationInfo::region() const
+{
+ return qdbus_cast<QString>(mPriv->location.value(
+ QLatin1String("region")));
+}
+
+QString LocationInfo::locality() const
+{
+ return qdbus_cast<QString>(mPriv->location.value(
+ QLatin1String("locality")));
+}
+
+QString LocationInfo::area() const
+{
+ return qdbus_cast<QString>(mPriv->location.value(
+ QLatin1String("area")));
+}
+
+QString LocationInfo::postalCode() const
+{
+ return qdbus_cast<QString>(mPriv->location.value(
+ QLatin1String("postalcode")));
+}
+
+QString LocationInfo::street() const
+{
+ return qdbus_cast<QString>(mPriv->location.value(
+ QLatin1String("street")));
+}
+
+QString LocationInfo::building() const
+{
+ return qdbus_cast<QString>(mPriv->location.value(
+ QLatin1String("building")));
+}
+
+QString LocationInfo::floor() const
+{
+ return qdbus_cast<QString>(mPriv->location.value(
+ QLatin1String("floor")));
+}
+
+QString LocationInfo::room() const
+{
+ return qdbus_cast<QString>(mPriv->location.value(
+ QLatin1String("room")));
+}
+
+QString LocationInfo::text() const
+{
+ return qdbus_cast<QString>(mPriv->location.value(
+ QLatin1String("text")));
+}
+
+QString LocationInfo::description() const
+{
+ return qdbus_cast<QString>(mPriv->location.value(
+ QLatin1String("description")));
+}
+
+QString LocationInfo::uri() const
+{
+ return qdbus_cast<QString>(mPriv->location.value(
+ QLatin1String("uri")));
+}
+
+QString LocationInfo::language() const
+{
+ return qdbus_cast<QString>(mPriv->location.value(
+ QLatin1String("language")));
+}
+
+double LocationInfo::latitude() const
+{
+ return qdbus_cast<double>(mPriv->location.value(
+ QLatin1String("lat")));
+}
+
+double LocationInfo::longitude() const
+{
+ return qdbus_cast<double>(mPriv->location.value(
+ QLatin1String("lon")));
+}
+
+double LocationInfo::altitude() const
+{
+ return qdbus_cast<double>(mPriv->location.value(
+ QLatin1String("alt")));
+}
+
+double LocationInfo::accuracy() const
+{
+ return qdbus_cast<double>(mPriv->location.value(
+ QLatin1String("accuracy")));
+}
+
+double LocationInfo::speed() const
+{
+ return qdbus_cast<double>(mPriv->location.value(
+ QLatin1String("speed")));
+}
+
+double LocationInfo::bearing() const
+{
+ return qdbus_cast<double>(mPriv->location.value(
+ QLatin1String("bearing")));
+}
+
+QDateTime LocationInfo::timestamp() const
+{
+ // FIXME See http://bugs.freedesktop.org/show_bug.cgi?id=21690
+ qlonglong t = qdbus_cast<qlonglong>(mPriv->location.value(
+ QLatin1String("timestamp")));
+ if (t != 0) {
+ return QDateTime::fromTime_t((uint) t);
+ }
+ return QDateTime();
+}
+
+QVariantMap LocationInfo::allDetails() const
+{
+ return mPriv->location;
+}
+
+void LocationInfo::updateData(const QVariantMap &location)
+{
+ if (!isValid()) {
+ mPriv = new Private;
+ }
+
+ mPriv->location = location;
+}
+
+} // Tp
diff --git a/TelepathyQt/location-info.h b/TelepathyQt/location-info.h
new file mode 100644
index 00000000..221fbed2
--- /dev/null
+++ b/TelepathyQt/location-info.h
@@ -0,0 +1,95 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_location_info_h_HEADER_GUARD_
+#define _TelepathyQt_location_info_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Global>
+
+#include <QDateTime>
+#include <QSharedDataPointer>
+#include <QString>
+#include <QVariantMap>
+
+namespace Tp
+{
+
+class TP_QT_EXPORT LocationInfo
+{
+public:
+ LocationInfo();
+ LocationInfo(const QVariantMap &location);
+ LocationInfo(const LocationInfo &other);
+ virtual ~LocationInfo();
+
+ bool isValid() const { return mPriv.constData() != 0; }
+
+ LocationInfo &operator=(const LocationInfo &other);
+
+ QString countryCode() const;
+ QString country() const;
+ QString region() const;
+ QString locality() const;
+ QString area() const;
+ QString postalCode() const;
+ QString street() const;
+
+ QString building() const;
+ QString floor() const;
+ QString room() const;
+ QString text() const;
+ QString description() const;
+ QString uri() const;
+
+ QString language() const;
+
+ double latitude() const;
+ double longitude() const;
+ double altitude() const;
+ double accuracy() const;
+
+ double speed() const;
+ double bearing() const;
+
+ QDateTime timestamp() const;
+
+ QVariantMap allDetails() const;
+
+private:
+ friend class Contact;
+
+ TP_QT_NO_EXPORT void updateData(const QVariantMap &location);
+
+ struct Private;
+ friend struct Private;
+ QSharedDataPointer<Private> mPriv;
+};
+
+} // Tp
+
+Q_DECLARE_METATYPE(Tp::LocationInfo);
+
+#endif
diff --git a/TelepathyQt/main.dox b/TelepathyQt/main.dox
new file mode 100644
index 00000000..d87e346e
--- /dev/null
+++ b/TelepathyQt/main.dox
@@ -0,0 +1,133 @@
+/*
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * \mainpage Telepathy-Qt4
+ *
+ * \section Introduction
+ *
+ * Telepathy-Qt4 is a Qt4 high-level binding for \telepathy.
+ *
+ * \telepathy is a \dbus framework for unifying real time communication, including instant
+ * messaging, voice calls and video calls. It abstracts differences between protocols to
+ * provide a unified interface for applications.
+ *
+ * Releases can be found <a
+ * href="http://telepathy.freedesktop.org/releases/telepathy-qt4">here</a>.
+ *
+ * Development is done in the git repository found <a
+ * href="http://cgit.freedesktop.org/telepathy/telepathy-qt4/">here</a>.
+ *
+ * \li <a href="classes.html">All Classes</a>
+ *
+ * \section getting_started Getting Started
+ * \li \subpage installation
+ *
+ * \section examples Examples
+ *
+ * This is the list of examples in Telepathy-Qt4's examples directory.
+ * The examples demonstrate Telepathy-Qt4 features in small, self-contained
+ * programs. They are not all designed to be impressive when you run them,
+ * but their source code is carefully written to show good Telepathy-Qt4
+ * programming practices.
+ *
+ * \li \subpage accounts_example
+ * \li \subpage contact_messenger_example
+ * \li \subpage protocols_example
+ * \li \subpage roster_example
+ *
+ * \section developer_resources Further Information
+ * \li \subpage bugreport
+ * \li \subpage mailing_lists
+ * \li \subpage async_model
+ * \li \subpage shared_ptr
+ */
+
+/**
+ * \page installation Installation
+ *
+ * \section installation_from_source Installing from source on Linux
+ *
+ * \subsection installation_from_source_requirements Requirements
+ *
+ * Building Telepathy-Qt4 requires:
+ * \li Qt, including QtDBus <http://www.qtsoftware.com/>
+ * \li GNU make <http://www.gnu.org/software/make/>
+ * \li pkg-config <http://ftp.gnome.org/pub/GNOME/sources/pkg-config/>
+ * \li libxslt, xsltproc <http://xmlsoft.org/XSLT/>
+ * \li Python <http://www.python.org/>
+ *
+ * For the full set of regression tests to run, you'll also need:
+ * \li telepathy-glib <http://telepathy.freedesktop.org/releases/telepathy-glib/>
+ *
+ * and to build the example VoIP call UI (examples/call), you'll need:
+ * \li telepathy-glib <http://telepathy.freedesktop.org/releases/telepathy-glib/>
+ * \li telepathy-farsight
+ * <http://telepathy.freedesktop.org/releases/telepathy-farsight/>
+ * \li GStreamer <http://gstreamer.freedesktop.org/>\n
+ *
+ * Building also requires the cmake build system.
+ *
+ * \subsection installation_from_source_building Building
+ *
+ * After installing all dependencies, run:
+ *
+ * \verbatim
+ $ mkdir build; cd build
+ $ cmake ..
+ $ make
+ $ make install \endverbatim
+ */
+
+/**
+ * \page bugreport How to report a bug
+ *
+ * Before reporting a bug, please check the <a
+ * href="https://bugs.freedesktop.org/query.cgi?product=Telepathy&component=telepathy-qt4">
+ * Bug Tracker</a> to see if the issue is already known.
+ *
+ * Always include the following information in your bug report:
+ * \li The version of Telepathy-Qt4 you are using
+ *
+ * Please submit the bug report, feature request or "to-do" item
+ * <a
+ * href="https://bugs.freedesktop.org/enter_bug.cgi?product=Telepathy&component=telepathy-qt4">
+ * here</a>.
+ */
+
+/**
+ * \page mailing_lists Mailing Lists
+ *
+ * <a href="http://lists.freedesktop.org/mailman/listinfo/telepathy">General
+ * discussion list</a>\n
+ * This list should be used for general discussion about \telepathy usage,
+ * development.
+ *
+ * <a
+ * href="http://lists.freedesktop.org/mailman/listinfo/telepathy-commits">
+ * Commits list</a>\n
+ * Subscribe to this list to follow the commits.
+ *
+ * <a
+ * href="http://lists.freedesktop.org/mailman/listinfo/telepathy-bugs">
+ * Bugs list</a>\n
+ * Subscribe to this list to follow the bug reports.
+ */
diff --git a/TelepathyQt/manager-file.cpp b/TelepathyQt/manager-file.cpp
new file mode 100644
index 00000000..e04c1e49
--- /dev/null
+++ b/TelepathyQt/manager-file.cpp
@@ -0,0 +1,618 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/ManagerFile>
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/KeyFile>
+
+#include <QtCore/QDir>
+#include <QtCore/QHash>
+#include <QtCore/QString>
+#include <QtCore/QStringList>
+#include <QtDBus/QDBusVariant>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT ManagerFile::Private
+{
+ Private();
+ Private(const QString &cnName);
+
+ void init();
+ bool parse(const QString &fileName);
+ bool isValid() const;
+
+ bool hasParameter(const QString &protocol, const QString &paramName) const;
+ ParamSpec *getParameter(const QString &protocol, const QString &paramName);
+ QStringList protocols() const;
+ ParamSpecList parameters(const QString &protocol) const;
+
+ QVariant valueForKey(const QString &param, const QString &dbusSignature);
+
+ struct ProtocolInfo
+ {
+ ProtocolInfo() {}
+ ProtocolInfo(const ParamSpecList &params, const PresenceSpecList &statuses)
+ : params(params),
+ statuses(statuses)
+ {
+ }
+
+ ParamSpecList params;
+ QString vcardField;
+ QString englishName;
+ QString iconName;
+ RequestableChannelClassList rccs;
+ PresenceSpecList statuses;
+ AvatarSpec avatarRequirements;
+ };
+
+ QString cmName;
+ KeyFile keyFile;
+ QHash<QString, ProtocolInfo> protocolsMap;
+ bool valid;
+};
+
+ManagerFile::Private::Private()
+ : valid(false)
+{
+}
+
+ManagerFile::Private::Private(const QString &cmName)
+ : cmName(cmName),
+ valid(false)
+{
+ init();
+}
+
+void ManagerFile::Private::init()
+{
+ // TODO: should we cache the configDirs anywhere?
+ QStringList configDirs;
+
+ QString xdgDataHome = QString::fromLocal8Bit(qgetenv("XDG_DATA_HOME"));
+ if (xdgDataHome.isEmpty()) {
+ configDirs << QDir::homePath() + QLatin1String("/.local/share/data/telepathy/managers/");
+ }
+ else {
+ configDirs << xdgDataHome + QLatin1String("/telepathy/managers/");
+ }
+
+ QString xdgDataDirsEnv = QString::fromLocal8Bit(qgetenv("XDG_DATA_DIRS"));
+ if (xdgDataDirsEnv.isEmpty()) {
+ configDirs << QLatin1String("/usr/local/share/telepathy/managers/");
+ configDirs << QLatin1String("/usr/share/telepathy/managers/");
+ }
+ else {
+ QStringList xdgDataDirs = xdgDataDirsEnv.split(QLatin1Char(':'));
+ foreach (const QString xdgDataDir, xdgDataDirs) {
+ configDirs << xdgDataDir + QLatin1String("/telepathy/managers/");
+ }
+ }
+
+ foreach (const QString configDir, configDirs) {
+ QString fileName = configDir + cmName + QLatin1String(".manager");
+ if (QFile::exists(fileName)) {
+ debug() << "parsing manager file" << fileName;
+ protocolsMap.clear();
+ if (!parse(fileName)) {
+ warning() << "error parsing manager file" << fileName;
+ continue;
+ }
+ valid = true;
+ return;
+ }
+ }
+}
+
+bool ManagerFile::Private::parse(const QString &fileName)
+{
+ keyFile.setFileName(fileName);
+ if (keyFile.status() != KeyFile::NoError) {
+ return false;
+ }
+
+ /* read supported protocols and parameters */
+ QString protocol;
+ QStringList groups = keyFile.allGroups();
+ foreach (const QString group, groups) {
+ if (group.startsWith(QLatin1String("Protocol "))) {
+ protocol = group.right(group.length() - 9);
+ keyFile.setGroup(group);
+
+ ParamSpecList paramSpecList;
+ SimpleStatusSpecMap statuses;
+ QString param;
+ QStringList params = keyFile.keys();
+ foreach (param, params) {
+ ParamSpec spec;
+ SimpleStatusSpec status;
+ spec.flags = 0;
+
+ QStringList values = keyFile.value(param).split(QLatin1String(" "));
+
+ if (param.startsWith(QLatin1String("param-"))) {
+ spec.name = param.right(param.length() - 6);
+
+ if (values.length() == 0) {
+ warning() << "param" << spec.name << "set but no signature defined";
+ return false;
+ }
+
+ if (spec.name.endsWith(QLatin1String("password"))) {
+ spec.flags |= ConnMgrParamFlagSecret;
+ }
+
+ spec.signature = values[0];
+
+ if (values.contains(QLatin1String("secret"))) {
+ spec.flags |= ConnMgrParamFlagSecret;
+ }
+
+ if (values.contains(QLatin1String("dbus-property"))) {
+ spec.flags |= ConnMgrParamFlagDBusProperty;
+ }
+
+ if (values.contains(QLatin1String("required"))) {
+ spec.flags |= ConnMgrParamFlagRequired;
+ }
+
+ if (values.contains(QLatin1String("register"))) {
+ spec.flags |= ConnMgrParamFlagRegister;
+ }
+
+ paramSpecList.append(spec);
+ } else if (param.startsWith(QLatin1String("status-"))) {
+ QString statusName = param.right(param.length() - 7);
+
+ if (values.length() == 0) {
+ warning() << "status" << statusName << "set but no type defined";
+ return false;
+ }
+
+ bool ok;
+ status.type = values[0].toUInt(&ok);
+ if (!ok) {
+ warning() << "status" << statusName << "set but type is not an uint";
+ return false;
+ }
+
+ if (values.contains(QLatin1String("settable"))) {
+ status.maySetOnSelf = true;
+ } else {
+ status.maySetOnSelf = false;
+ }
+
+ if (values.contains(QLatin1String("message"))) {
+ status.canHaveMessage = true;
+ } else {
+ status.canHaveMessage = false;
+ }
+
+ if (statuses.contains(statusName)) {
+ warning() << "status" << statusName << "defined more than once, "
+ "replacing it";
+ }
+
+ statuses.insert(statusName, status);
+ }
+ }
+
+ protocolsMap.insert(protocol, ProtocolInfo(paramSpecList, PresenceSpecList(statuses)));
+
+ /* now that we have all param-* created, let's find their default values */
+ foreach (param, params) {
+ if (param.startsWith(QLatin1String("default-"))) {
+ QString paramName = param.right(param.length() - 8);
+
+ if (!hasParameter(protocol, paramName)) {
+ warning() << "param" << paramName
+ << "has default value set, but not a definition";
+ continue;
+ }
+
+ ParamSpec *spec = getParameter(protocol, paramName);
+
+ spec->flags |= ConnMgrParamFlagHasDefault;
+
+ /* map based on the param dbus signature, otherwise use
+ * QString */
+ QVariant value = valueForKey(param, spec->signature);
+ if (value.type() == QVariant::Invalid) {
+ warning() << "param" << paramName
+ << "has invalid signature";
+ protocolsMap.clear();
+ return false;
+ }
+ spec->defaultValue = QDBusVariant(value);
+ }
+ }
+
+ ProtocolInfo &info = protocolsMap[protocol];
+ info.vcardField = keyFile.value(QLatin1String("VCardField"));
+ info.englishName = keyFile.value(QLatin1String("EnglishName"));
+ if (info.englishName.isEmpty()) {
+ QStringList words = protocol.split(QLatin1Char('-'));
+ for (int i = 0; i < words.size(); ++i) {
+ words[i][0] = words[i].at(0).toUpper();
+ }
+ info.englishName = words.join(QLatin1String(" "));
+ }
+
+ info.iconName = keyFile.value(QLatin1String("Icon"));
+ if (info.iconName.isEmpty()) {
+ info.iconName = QString(QLatin1String("im-%1")).arg(protocol);
+ }
+
+ QStringList supportedMimeTypes = keyFile.valueAsStringList(
+ QLatin1String("SupportedAvatarMIMETypes"));
+ uint minHeight = keyFile.value(QLatin1String("MinimumAvatarHeight")).toUInt();
+ uint maxHeight = keyFile.value(QLatin1String("MaximumAvatarHeight")).toUInt();
+ uint recommendedHeight = keyFile.value(
+ QLatin1String("RecommendedAvatarHeight")).toUInt();
+ uint minWidth = keyFile.value(QLatin1String("MinimumAvatarWidth")).toUInt();
+ uint maxWidth = keyFile.value(QLatin1String("MaximumAvatarWidth")).toUInt();
+ uint recommendedWidth = keyFile.value(
+ QLatin1String("RecommendedAvatarWidth")).toUInt();
+ uint maxBytes = keyFile.value(QLatin1String("MaximumAvatarBytes")).toUInt();
+ info.avatarRequirements = AvatarSpec(supportedMimeTypes,
+ minHeight, maxHeight, recommendedHeight,
+ minWidth, maxWidth, recommendedWidth,
+ maxBytes);
+
+ QStringList rccGroups = keyFile.valueAsStringList(
+ QLatin1String("RequestableChannelClasses"));
+ RequestableChannelClass rcc;
+ foreach (const QString &rccGroup, rccGroups) {
+ keyFile.setGroup(rccGroup);
+
+ foreach (const QString &key, keyFile.keys()) {
+ int spaceIdx = key.indexOf(QLatin1String(" "));
+ if (spaceIdx == -1) {
+ continue;
+ }
+
+ QString propertyName = key.mid(0, spaceIdx);
+ QString signature = key.mid(spaceIdx + 1);
+ QString param = keyFile.value(key);
+ QVariant value = valueForKey(key, signature);
+ rcc.fixedProperties.insert(propertyName, value);
+ }
+
+ rcc.allowedProperties = keyFile.valueAsStringList(
+ QLatin1String("allowed"));
+
+ info.rccs.append(rcc);
+
+ rcc.fixedProperties.clear();
+ rcc.allowedProperties.clear();
+ }
+ }
+ }
+
+ return true;
+}
+
+bool ManagerFile::Private::isValid() const
+{
+ return ((keyFile.status() == KeyFile::NoError) && (valid));
+}
+
+bool ManagerFile::Private::hasParameter(const QString &protocol,
+ const QString &paramName) const
+{
+ ParamSpecList paramSpecList = protocolsMap[protocol].params;
+ foreach (const ParamSpec &paramSpec, paramSpecList) {
+ if (paramSpec.name == paramName) {
+ return true;
+ }
+ }
+ return false;
+}
+
+ParamSpec *ManagerFile::Private::getParameter(const QString &protocol,
+ const QString &paramName)
+{
+ ParamSpecList &paramSpecList = protocolsMap[protocol].params;
+ for (int i = 0; i < paramSpecList.size(); ++i) {
+ ParamSpec &paramSpec = paramSpecList[i];
+ if (paramSpec.name == paramName) {
+ return &paramSpec;
+ }
+ }
+ return NULL;
+}
+
+QStringList ManagerFile::Private::protocols() const
+{
+ return protocolsMap.keys();
+}
+
+ParamSpecList ManagerFile::Private::parameters(const QString &protocol) const
+{
+ return protocolsMap.value(protocol).params;
+}
+
+QVariant ManagerFile::Private::valueForKey(const QString &param,
+ const QString &dbusSignature)
+{
+ QString value = keyFile.rawValue(param);
+ return parseValueWithDBusSignature(value, dbusSignature);
+}
+
+
+/**
+ * \class ManagerFile
+ * \ingroup utils
+ * \headerfile TelepathyQt/manager-file.h <TelepathyQt/ManagerFile>
+ *
+ * \brief The ManagerFile class provides an easy way to read Telepathy manager
+ * files according to the \telepathy_spec.
+ *
+ * \todo Consider making this private, because the ConnectionManager and Account classes provide a
+ * nice view to the data parsed by this class anyway (fd.o #41655).
+ */
+
+/**
+ * Create a ManagerFile object used to read .manager compliant files.
+ */
+ManagerFile::ManagerFile()
+ : mPriv(new Private())
+{
+}
+
+/**
+ * Create a ManagerFile object used to read .manager compliant files.
+ *
+ * \param cmName Name of the connection manager to read the file for.
+ */
+ManagerFile::ManagerFile(const QString &cmName)
+ : mPriv(new Private(cmName))
+{
+}
+
+/**
+ * Create a ManagerFile object used to read .manager compliant files.
+ */
+ManagerFile::ManagerFile(const ManagerFile &other)
+ : mPriv(new Private())
+{
+ mPriv->cmName = other.mPriv->cmName;
+ mPriv->keyFile = other.mPriv->keyFile;
+ mPriv->protocolsMap = other.mPriv->protocolsMap;
+ mPriv->valid = other.mPriv->valid;
+}
+
+/**
+ * Class destructor.
+ */
+ManagerFile::~ManagerFile()
+{
+ delete mPriv;
+}
+
+ManagerFile &ManagerFile::operator=(const ManagerFile &other)
+{
+ mPriv->cmName = other.mPriv->cmName;
+ mPriv->keyFile = other.mPriv->keyFile;
+ mPriv->protocolsMap = other.mPriv->protocolsMap;
+ mPriv->valid = other.mPriv->valid;
+ return *this;
+}
+
+/**
+ * Check whether or not a ManagerFile object is valid. If the file for the
+ * specified connection manager cannot be found it will be considered invalid.
+ *
+ * \return true if valid, false otherwise.
+ */
+bool ManagerFile::isValid() const
+{
+ return mPriv->isValid();
+}
+
+/**
+ * Return a list of all protocols defined in the manager file.
+ *
+ * \return List of all protocols defined in the file.
+ */
+QStringList ManagerFile::protocols() const
+{
+ return mPriv->protocols();
+}
+
+/**
+ * Return a list of parameters for the given \a protocol.
+ *
+ * \param protocol Name of the protocol to look for.
+ * \return List of ParamSpec of a specific protocol defined in the file, or an
+ * empty list if the protocol is not defined.
+ */
+ParamSpecList ManagerFile::parameters(const QString &protocol) const
+{
+ return mPriv->parameters(protocol);
+}
+
+/**
+ * Return the name of the most common vCard field used for the given \a protocol's
+ * contact identifiers, normalized to lower case.
+ *
+ * \param protocol Name of the protocol to look for.
+ * \return The most common vCard field used for the given protocol's contact
+ * identifiers, or an empty string if there is no such field or the
+ * protocol is not defined.
+ */
+QString ManagerFile::vcardField(const QString &protocol) const
+{
+ return mPriv->protocolsMap.value(protocol).vcardField;
+}
+
+/**
+ * Return the English-language name of the given \a protocol, such as "AIM" or "Yahoo!".
+ *
+ * The name can be used as a fallback if an application doesn't have a localized name for the
+ * protocol.
+ *
+ * If the manager file doesn't specify the english name, it is inferred from the protocol name, such
+ * that for example "google-talk" becomes "Google Talk", but "local-xmpp" becomes "Local Xmpp".
+ *
+ * \param protocol Name of the protocol to look for.
+ * \return An English-language name for the given \a protocol.
+ */
+QString ManagerFile::englishName(const QString &protocol) const
+{
+ return mPriv->protocolsMap.value(protocol).englishName;
+}
+
+/**
+ * Return the name of an icon for the given \a protocol in the system's icon
+ * theme, such as "im-msn".
+ *
+ * If the manager file doesn't specify the icon name, "im-<protocolname>" is assumed.
+ *
+ * \param protocol Name of the protocol to look for.
+ * \return The likely name of an icon for the given \a protocol.
+ */
+QString ManagerFile::iconName(const QString &protocol) const
+{
+ return mPriv->protocolsMap.value(protocol).iconName;
+}
+
+/**
+ * Return a list of channel classes which might be requestable from a connection
+ * to the given \a protocol.
+ *
+ * \param protocol Name of the protocol to look for.
+ * \return A list of channel classes which might be requestable from a
+ * connection to the given \a protocol or a default constructed
+ * RequestableChannelClassList instance if the protocol is not defined.
+ */
+RequestableChannelClassList ManagerFile::requestableChannelClasses(
+ const QString &protocol) const
+{
+ return mPriv->protocolsMap.value(protocol).rccs;
+}
+
+/**
+ * Return a list of PresenceSpec representing the possible presence statuses
+ * from a connection to the given \a protocol.
+ *
+ * \param protocol Name of the protocol to look for.
+ * \return A list of PresenceSpec representing the possible presence statuses
+ * from a connection to the given \a protocol or an empty list
+ * if the protocol is not defined.
+ */
+PresenceSpecList ManagerFile::allowedPresenceStatuses(const QString &protocol) const
+{
+ return mPriv->protocolsMap.value(protocol).statuses;
+}
+
+/**
+ * Return the requirements (size limits, supported MIME types, etc)
+ * for avatars used on the given \a protocol.
+ *
+ * \param protocol Name of the protocol to look for.
+ * \return The requirements for avatars used on the given \a protocol or an invalid
+ * AvatarSpec if the protocol is not defined.
+ */
+AvatarSpec ManagerFile::avatarRequirements(const QString &protocol) const
+{
+ return mPriv->protocolsMap.value(protocol).avatarRequirements;
+}
+
+QVariant::Type ManagerFile::variantTypeFromDBusSignature(const QString &signature)
+{
+ QVariant::Type type;
+ if (signature == QLatin1String("b")) {
+ type = QVariant::Bool;
+ } else if (signature == QLatin1String("n") || signature == QLatin1String("i")) {
+ type = QVariant::Int;
+ } else if (signature == QLatin1String("q") || signature == QLatin1String("u")) {
+ type = QVariant::UInt;
+ } else if (signature == QLatin1String("x")) {
+ type = QVariant::LongLong;
+ } else if (signature == QLatin1String("t")) {
+ type = QVariant::ULongLong;
+ } else if (signature == QLatin1String("d")) {
+ type = QVariant::Double;
+ } else if (signature == QLatin1String("as")) {
+ type = QVariant::StringList;
+ } else if (signature == QLatin1String("s") || signature == QLatin1String("o")) {
+ type = QVariant::String;
+ } else {
+ type = QVariant::Invalid;
+ }
+
+ return type;
+}
+
+QVariant ManagerFile::parseValueWithDBusSignature(const QString &value,
+ const QString &dbusSignature)
+{
+ QVariant::Type type = variantTypeFromDBusSignature(dbusSignature);
+
+ if (type == QVariant::Invalid) {
+ return QVariant(type);
+ }
+
+ switch (type) {
+ case QVariant::Bool:
+ {
+ if (value.toLower() == QLatin1String("true") ||
+ value == QLatin1String("1")) {
+ return QVariant(true);
+ } else {
+ return QVariant(false);
+ }
+ break;
+ }
+ case QVariant::Int:
+ return QVariant(value.toInt());
+ case QVariant::UInt:
+ return QVariant(value.toUInt());
+ case QVariant::LongLong:
+ return QVariant(value.toLongLong());
+ case QVariant::ULongLong:
+ return QVariant(value.toULongLong());
+ case QVariant::Double:
+ return QVariant(value.toDouble());
+ case QVariant::StringList:
+ {
+ QStringList list;
+ QByteArray rawValue = value.toAscii();
+ if (KeyFile::unescapeStringList(rawValue, 0, rawValue.size(), list)) {
+ return QVariant(list);
+ } else {
+ return QVariant(QVariant::Invalid);
+ }
+ }
+ default:
+ break;
+ }
+ return QVariant(value);
+}
+
+} // Tp
diff --git a/TelepathyQt/manager-file.h b/TelepathyQt/manager-file.h
new file mode 100644
index 00000000..200c89d0
--- /dev/null
+++ b/TelepathyQt/manager-file.h
@@ -0,0 +1,78 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_manager_file_h_HEADER_GUARD_
+#define _TelepathyQt_manager_file_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/AvatarSpec>
+#include <TelepathyQt/PresenceSpec>
+#include <TelepathyQt/Types>
+
+#include <QMetaType>
+#include <QVariant>
+
+namespace Tp
+{
+
+class TP_QT_EXPORT ManagerFile
+{
+public:
+ ManagerFile();
+ ManagerFile(const ManagerFile &other);
+ ManagerFile(const QString &cmName);
+ ~ManagerFile();
+
+ ManagerFile &operator=(const ManagerFile &other);
+
+ QString cmName() const;
+
+ bool isValid() const;
+ QStringList protocols() const;
+ ParamSpecList parameters(const QString &protocol) const;
+ QString vcardField(const QString &protocol) const;
+ QString englishName(const QString &protocol) const;
+ QString iconName(const QString &protocol) const;
+ RequestableChannelClassList requestableChannelClasses(
+ const QString &protocol) const;
+ PresenceSpecList allowedPresenceStatuses(const QString &protocol) const;
+ AvatarSpec avatarRequirements(const QString &protocol) const;
+
+ static QVariant::Type variantTypeFromDBusSignature(
+ const QString &dbusSignature);
+ static QVariant parseValueWithDBusSignature(const QString &value,
+ const QString &dbusSignature);
+
+private:
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+}
+
+Q_DECLARE_METATYPE(Tp::ManagerFile);
+
+#endif
diff --git a/TelepathyQt/media-session-handler.cpp b/TelepathyQt/media-session-handler.cpp
new file mode 100644
index 00000000..b3eb4789
--- /dev/null
+++ b/TelepathyQt/media-session-handler.cpp
@@ -0,0 +1,26 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/MediaSessionHandler>
+
+#include "TelepathyQt/_gen/cli-media-session-handler-body.hpp"
+#include "TelepathyQt/_gen/cli-media-session-handler.moc.hpp"
diff --git a/TelepathyQt/media-session-handler.h b/TelepathyQt/media-session-handler.h
new file mode 100644
index 00000000..9603f888
--- /dev/null
+++ b/TelepathyQt/media-session-handler.h
@@ -0,0 +1,50 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_media_session_handler_h_HEADER_GUARD_
+#define _TelepathyQt_media_session_handler_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+/**
+ * \addtogroup clientsideproxies Client-side proxies
+ *
+ * Proxy objects representing remote service objects accessed via D-Bus.
+ *
+ * In addition to providing direct access to methods, signals and properties
+ * exported by the remote objects, some of these proxies offer features like
+ * automatic inspection of remote object capabilities, property tracking,
+ * backwards compatibility helpers for older services and other utilities.
+ */
+
+/**
+ * \defgroup clientmsesh Media session handler proxies
+ * \ingroup clientsideproxies
+ *
+ * Proxy objects representing remote Telepathy MediaSessionHandler objects.
+ */
+
+#include <TelepathyQt/_gen/cli-media-session-handler.h>
+
+#endif
diff --git a/TelepathyQt/media-session-handler.xml b/TelepathyQt/media-session-handler.xml
new file mode 100644
index 00000000..8b051cd2
--- /dev/null
+++ b/TelepathyQt/media-session-handler.xml
@@ -0,0 +1,9 @@
+<tp:spec
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<tp:title>Media session handler</tp:title>
+
+<xi:include href="../spec/Media_Session_Handler.xml"/>
+
+</tp:spec>
diff --git a/TelepathyQt/media-stream-handler.cpp b/TelepathyQt/media-stream-handler.cpp
new file mode 100644
index 00000000..1361716f
--- /dev/null
+++ b/TelepathyQt/media-stream-handler.cpp
@@ -0,0 +1,26 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/MediaStreamHandler>
+
+#include "TelepathyQt/_gen/cli-media-stream-handler-body.hpp"
+#include "TelepathyQt/_gen/cli-media-stream-handler.moc.hpp"
diff --git a/TelepathyQt/media-stream-handler.h b/TelepathyQt/media-stream-handler.h
new file mode 100644
index 00000000..635d8490
--- /dev/null
+++ b/TelepathyQt/media-stream-handler.h
@@ -0,0 +1,50 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_media_stream_handler_h_HEADER_GUARD_
+#define _TelepathyQt_media_stream_handler_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+/**
+ * \addtogroup clientsideproxies Client-side proxies
+ *
+ * Proxy objects representing remote service objects accessed via D-Bus.
+ *
+ * In addition to providing direct access to methods, signals and properties
+ * exported by the remote objects, some of these proxies offer features like
+ * automatic inspection of remote object capabilities, property tracking,
+ * backwards compatibility helpers for older services and other utilities.
+ */
+
+/**
+ * \defgroup clientmstrh Media stream handler proxies
+ * \ingroup clientsideproxies
+ *
+ * Proxy objects representing remote Telepathy MediaStreamHandler objects.
+ */
+
+#include <TelepathyQt/_gen/cli-media-stream-handler.h>
+
+#endif
diff --git a/TelepathyQt/media-stream-handler.xml b/TelepathyQt/media-stream-handler.xml
new file mode 100644
index 00000000..348d5a68
--- /dev/null
+++ b/TelepathyQt/media-stream-handler.xml
@@ -0,0 +1,9 @@
+<tp:spec
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<tp:title>Media stream handler</tp:title>
+
+<xi:include href="../spec/Media_Stream_Handler.xml"/>
+
+</tp:spec>
diff --git a/TelepathyQt/message-content-part.cpp b/TelepathyQt/message-content-part.cpp
new file mode 100644
index 00000000..13363583
--- /dev/null
+++ b/TelepathyQt/message-content-part.cpp
@@ -0,0 +1,96 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/MessageContentPart>
+
+#include <QSharedData>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT MessageContentPart::Private : public QSharedData
+{
+ Private(const MessagePart &mp)
+ : mp(mp) {}
+
+ MessagePart mp;
+};
+
+/**
+ * \class MessageContentPart
+ * \ingroup wrappers
+ * \headerfile TelepathyQt/message-content-part.h <TelepathyQt/MessageContentPart>
+ *
+ * \brief The MessageContentPart class represents a Telepathy message part.
+ */
+
+MessageContentPart::MessageContentPart()
+{
+}
+
+MessageContentPart::MessageContentPart(const MessagePart &mp)
+ : mPriv(new Private(mp))
+{
+}
+
+MessageContentPart::MessageContentPart(const MessageContentPart &other)
+ : mPriv(other.mPriv)
+{
+}
+
+MessageContentPart::~MessageContentPart()
+{
+}
+
+MessageContentPart &MessageContentPart::operator=(const MessageContentPart &other)
+{
+ this->mPriv = other.mPriv;
+ return *this;
+}
+
+bool MessageContentPart::operator==(const MessageContentPart &other) const
+{
+ if (!isValid() || !other.isValid()) {
+ if (!isValid() && !other.isValid()) {
+ return true;
+ }
+ return false;
+ }
+
+ return mPriv->mp == other.mPriv->mp;
+}
+
+MessagePart MessageContentPart::barePart() const
+{
+ return isValid() ? mPriv->mp : MessagePart();
+}
+
+/**
+ * \class MessageContentPartList
+ * \ingroup wrappers
+ * \headerfile TelepathyQt/message-content-part.h <TelepathyQt/MessageContentPartList>
+ *
+ * \brief The MessageContentPartList class represents a list of
+ * MessageContentPart.
+ */
+
+} // Tp
diff --git a/TelepathyQt/message-content-part.h b/TelepathyQt/message-content-part.h
new file mode 100644
index 00000000..7ad289e6
--- /dev/null
+++ b/TelepathyQt/message-content-part.h
@@ -0,0 +1,96 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_message_content_part_h_HEADER_GUARD_
+#define _TelepathyQt_message_content_part_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+class TP_QT_EXPORT MessageContentPart
+{
+public:
+ MessageContentPart();
+ MessageContentPart(const MessagePart &mp);
+ MessageContentPart(const MessageContentPart &other);
+ ~MessageContentPart();
+
+ bool isValid() const { return mPriv.constData() != 0; }
+
+ MessageContentPart &operator=(const MessageContentPart &other);
+ bool operator==(const MessageContentPart &other) const;
+
+ MessagePart barePart() const;
+
+private:
+ struct Private;
+ friend struct Private;
+ QSharedDataPointer<Private> mPriv;
+};
+
+class TP_QT_EXPORT MessageContentPartList :
+ public QList<MessageContentPart>
+{
+public:
+ MessageContentPartList() { }
+ MessageContentPartList(const MessagePart &mp)
+ {
+ append(MessageContentPart(mp));
+ }
+ MessageContentPartList(const MessagePartList &mps)
+ {
+ Q_FOREACH (const MessagePart &mp, mps) {
+ append(MessageContentPart(mp));
+ }
+ }
+ MessageContentPartList(const MessageContentPart &mcp)
+ {
+ append(mcp);
+ }
+ MessageContentPartList(const QList<MessageContentPart> &other)
+ : QList<MessageContentPart>(other)
+ {
+ }
+
+ MessagePartList bareParts() const
+ {
+ MessagePartList list;
+ Q_FOREACH (const MessageContentPart &mcp, *this) {
+ list.append(mcp.barePart());
+ }
+ return list;
+ }
+};
+
+} // Tp
+
+Q_DECLARE_METATYPE(Tp::MessageContentPart);
+Q_DECLARE_METATYPE(Tp::MessageContentPartList);
+
+#endif
diff --git a/TelepathyQt/message.cpp b/TelepathyQt/message.cpp
new file mode 100644
index 00000000..1f6ca82b
--- /dev/null
+++ b/TelepathyQt/message.cpp
@@ -0,0 +1,911 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/Message>
+#include <TelepathyQt/ReceivedMessage>
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/TextChannel>
+
+#include <QDateTime>
+#include <QPointer>
+#include <QSet>
+
+namespace Tp
+{
+
+namespace
+{
+
+QVariant valueFromPart(const MessagePartList &parts, uint index, const char *key)
+{
+ return parts.at(index).value(QLatin1String(key)).variant();
+}
+
+uint uintOrZeroFromPart(const MessagePartList &parts, uint index, const char *key)
+{
+ return valueFromPart(parts, index, key).toUInt();
+}
+
+QString stringOrEmptyFromPart(const MessagePartList &parts, uint index, const char *key)
+{
+ QString s = valueFromPart(parts, index, key).toString();
+ if (s.isNull()) {
+ s = QLatin1String("");
+ }
+ return s;
+}
+
+bool booleanFromPart(const MessagePartList &parts, uint index, const char *key,
+ bool assumeIfAbsent)
+{
+ QVariant v = valueFromPart(parts, index, key);
+ if (v.isValid() && v.type() == QVariant::Bool) {
+ return v.toBool();
+ }
+ return assumeIfAbsent;
+}
+
+MessagePartList partsFromPart(const MessagePartList &parts, uint index, const char *key)
+{
+ return qdbus_cast<MessagePartList>(valueFromPart(parts, index, key));
+}
+
+bool partContains(const MessagePartList &parts, uint index, const char *key)
+{
+ return parts.at(index).contains(QLatin1String(key));
+}
+
+}
+
+struct TP_QT_NO_EXPORT Message::Private : public QSharedData
+{
+ Private(const MessagePartList &parts);
+ ~Private();
+
+ uint senderHandle() const;
+ QString senderId() const;
+ uint pendingId() const;
+ void clearSenderHandle();
+
+ MessagePartList parts;
+
+ // if the Text interface says "non-text" we still only have the text,
+ // because the interface can't tell us anything else...
+ bool forceNonText;
+
+ // for received messages only
+ QWeakPointer<TextChannel> textChannel;
+ ContactPtr sender;
+};
+
+Message::Private::Private(const MessagePartList &parts)
+ : parts(parts),
+ forceNonText(false),
+ sender(0)
+{
+}
+
+Message::Private::~Private()
+{
+}
+
+inline uint Message::Private::senderHandle() const
+{
+ return uintOrZeroFromPart(parts, 0, "message-sender");
+}
+
+inline QString Message::Private::senderId() const
+{
+ return stringOrEmptyFromPart(parts, 0, "message-sender-id");
+}
+
+inline uint Message::Private::pendingId() const
+{
+ return uintOrZeroFromPart(parts, 0, "pending-message-id");
+}
+
+void Message::Private::clearSenderHandle()
+{
+ parts[0].remove(QLatin1String("message-sender"));
+}
+
+/**
+ * \class Message
+ * \ingroup clientchannel
+ * \headerfile TelepathyQt/message.h <TelepathyQt/Message>
+ *
+ * \brief The Message class represents a Telepathy message in a TextChannel.
+ *
+ * This class is implicitly shared, like QString.
+ */
+
+/**
+ * \internal Default constructor.
+ */
+Message::Message()
+ : mPriv(new Private(MessagePartList()))
+{
+}
+
+/**
+ * Construct a new Message object.
+ *
+ * \param parts The parts of a message as defined by the \telepathy_spec.
+ * This list must have length at least 1.
+ */
+Message::Message(const MessagePartList &parts)
+ : mPriv(new Private(parts))
+{
+ Q_ASSERT(parts.size() > 0);
+}
+
+/**
+ * Construct a new Message object.
+ *
+ * \param timestamp The time the message was sent.
+ * \param type The message type.
+ * \param text The message body.
+ */
+Message::Message(uint timestamp, uint type, const QString &text)
+ : mPriv(new Private(MessagePartList() << MessagePart() << MessagePart()))
+{
+ mPriv->parts[0].insert(QLatin1String("message-sent"),
+ QDBusVariant(static_cast<qlonglong>(timestamp)));
+ mPriv->parts[0].insert(QLatin1String("message-type"),
+ QDBusVariant(type));
+
+ mPriv->parts[1].insert(QLatin1String("content-type"),
+ QDBusVariant(QLatin1String("text/plain")));
+ mPriv->parts[1].insert(QLatin1String("content"), QDBusVariant(text));
+}
+
+/**
+ * Construct a new Message object.
+ *
+ * \param type The message type.
+ * \param text The message body.
+ */
+Message::Message(ChannelTextMessageType type, const QString &text)
+ : mPriv(new Private(MessagePartList() << MessagePart() << MessagePart()))
+{
+ mPriv->parts[0].insert(QLatin1String("message-type"),
+ QDBusVariant(static_cast<uint>(type)));
+
+ mPriv->parts[1].insert(QLatin1String("content-type"),
+ QDBusVariant(QLatin1String("text/plain")));
+ mPriv->parts[1].insert(QLatin1String("content"), QDBusVariant(text));
+}
+
+/**
+ * Copy constructor.
+ */
+Message::Message(const Message &other)
+ : mPriv(other.mPriv)
+{
+}
+
+/**
+ * Assignment operator.
+ */
+Message &Message::operator=(const Message &other)
+{
+ if (this != &other) {
+ mPriv = other.mPriv;
+ }
+
+ return *this;
+}
+
+/**
+ * Equality operator.
+ */
+bool Message::operator==(const Message &other) const
+{
+ return this->mPriv == other.mPriv;
+}
+
+/**
+ * Class destructor.
+ */
+Message::~Message()
+{
+}
+
+/**
+ * Return the time the message was sent, or QDateTime() if that time is
+ * unknown.
+ *
+ * \return The timestamp as QDateTime.
+ */
+QDateTime Message::sent() const
+{
+ // FIXME See http://bugs.freedesktop.org/show_bug.cgi?id=21690
+ uint stamp = valueFromPart(mPriv->parts, 0, "message-sent").toUInt();
+ if (stamp != 0) {
+ return QDateTime::fromTime_t(stamp);
+ } else {
+ return QDateTime();
+ }
+}
+
+/**
+ * Return the type of this message, or #ChannelTextMessageTypeNormal
+ * if the type is not recognised.
+ *
+ * \return The type as #ChannelTextMessageType.
+ */
+ChannelTextMessageType Message::messageType() const
+{
+ uint raw = valueFromPart(mPriv->parts, 0, "message-type").toUInt();
+
+ if (raw < static_cast<uint>(NUM_CHANNEL_TEXT_MESSAGE_TYPES)) {
+ return ChannelTextMessageType(raw);
+ } else {
+ return ChannelTextMessageTypeNormal;
+ }
+}
+
+/**
+ * Return whether this message was truncated during delivery.
+ *
+ * \return \c true if truncated, \c false otherwise.
+ */
+bool Message::isTruncated() const
+{
+ for (int i = 1; i < size(); i++) {
+ if (booleanFromPart(mPriv->parts, i, "truncated", false)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Return whether this message contains parts not representable as plain
+ * text.
+ *
+ * \return \c true if it cannot completely be represented as plain text, \c false
+ * otherwise.
+ */
+bool Message::hasNonTextContent() const
+{
+ if (mPriv->forceNonText || size() <= 1 || isSpecificToDBusInterface()) {
+ return true;
+ }
+
+ QSet<QString> texts;
+ QSet<QString> textNeeded;
+
+ for (int i = 1; i < size(); i++) {
+ QString altGroup = stringOrEmptyFromPart(mPriv->parts, i, "alternative");
+ QString contentType = stringOrEmptyFromPart(mPriv->parts, i, "content-type");
+
+ if (contentType == QLatin1String("text/plain")) {
+ if (!altGroup.isEmpty()) {
+ // we can use this as an alternative for a non-text part
+ // with the same altGroup
+ texts << altGroup;
+ }
+ } else {
+ QString alt = stringOrEmptyFromPart(mPriv->parts, i, "alternative");
+ if (altGroup.isEmpty()) {
+ // we can't possibly rescue this part by using a text/plain
+ // alternative, because it's not in any alternative group
+ return true;
+ } else {
+ // maybe we'll find a text/plain alternative for this
+ textNeeded << altGroup;
+ }
+ }
+ }
+
+ textNeeded -= texts;
+ return !textNeeded.isEmpty();
+}
+
+/**
+ * Return the unique token identifying this message (e.g. the id attribute
+ * for XMPP messages), or an empty string if there is no suitable token.
+ *
+ * \return The non-empty message identifier, or an empty string if none.
+ */
+QString Message::messageToken() const
+{
+ return stringOrEmptyFromPart(mPriv->parts, 0, "message-token");
+}
+
+/**
+ * Return whether this message is specific to a D-Bus interface. This is
+ * \c false in almost all cases.
+ *
+ * If this function returns \c true, the message is specific to the interface
+ * indicated by dbusInterface(). Clients that don't understand that interface
+ * should not display the message. However, if the client would acknowledge
+ * an ordinary message, it must also acknowledge this interface-specific
+ * message.
+ *
+ * \return \c true if dbusInterface() would return a non-empty string, \c false otherwise.
+ * \sa dbusInterface()
+ */
+bool Message::isSpecificToDBusInterface() const
+{
+ return !dbusInterface().isEmpty();
+}
+
+/**
+ * Return the D-Bus interface to which this message is specific, or an
+ * empty string for normal messages.
+ *
+ * \return The D-Bus interface name, or an empty string.
+ * \sa isSpecificToDBusInterface()
+ */
+QString Message::dbusInterface() const
+{
+ return stringOrEmptyFromPart(mPriv->parts, 0, "interface");
+}
+
+/**
+ * Return the message body containing all "text/plain" parts.
+ *
+ * \return The body text.
+ */
+QString Message::text() const
+{
+ // Alternative-groups for which we've already emitted an alternative
+ QSet<QString> altGroupsUsed;
+ QString text;
+
+ for (int i = 1; i < size(); i++) {
+ QString altGroup = stringOrEmptyFromPart(mPriv->parts, i, "alternative");
+ QString contentType = stringOrEmptyFromPart(mPriv->parts, i, "content-type");
+
+ if (contentType == QLatin1String("text/plain")) {
+ if (!altGroup.isEmpty()) {
+ if (altGroupsUsed.contains(altGroup)) {
+ continue;
+ } else {
+ altGroupsUsed << altGroup;
+ }
+ }
+
+ QVariant content = valueFromPart(mPriv->parts, i, "content");
+ if (content.type() == QVariant::String) {
+ text += content.toString();
+ } else {
+ // O RLY?
+ debug() << "allegedly text/plain part wasn't";
+ }
+ }
+ }
+
+ return text;
+}
+
+/**
+ * Return the message's header part, as defined by the \telepathy_spec.
+ *
+ * This is provided for advanced clients that need to access
+ * additional information not available through the normal Message API.
+ *
+ * \return The header as a MessagePart object. The same thing as part(0).
+ */
+MessagePart Message::header() const
+{
+ return part(0);
+}
+
+/**
+ * Return the number of parts in this message.
+ *
+ * \return 1 greater than the largest valid argument to part().
+ * \sa part(), parts()
+ */
+int Message::size() const
+{
+ return mPriv->parts.size();
+}
+
+/**
+ * Return the message's part for \a index, as defined by the \telepathy_spec.
+ *
+ * This is provided for advanced clients that need to access
+ * additional information not available through the normal Message API.
+ *
+ * \param index The part to access, which must be strictly less than size();
+ * part number 0 is the header, parts numbered 1 or greater
+ * are the body of the message.
+ * \return A MessagePart object.
+ */
+MessagePart Message::part(uint index) const
+{
+ return mPriv->parts.at(index);
+}
+
+/**
+ * Return the list of message parts forming this message.
+ *
+ * \return The list of MessagePart objects.
+ */
+MessagePartList Message::parts() const
+{
+ return mPriv->parts;
+}
+
+/**
+ * \class ReceivedMessage
+ * \ingroup clientchannel
+ * \headerfile TelepathyQt/message.h <TelepathyQt/ReceivedMessage>
+ *
+ * \brief The ReceivedMessage class is a subclass of Message, representing a
+ * received message only.
+ *
+ * It contains additional information that's generally only
+ * available on received messages.
+ */
+
+/**
+ * \class ReceivedMessage::DeliveryDetails
+ * \ingroup clientchannel
+ * \headerfile TelepathyQt/message.h <TelepathyQt/ReceivedMessage>
+ *
+ * \brief The ReceivedMessage::DeliveryDetails class represents the details of a delivery report.
+ */
+
+struct TP_QT_NO_EXPORT ReceivedMessage::DeliveryDetails::Private : public QSharedData
+{
+ Private(const MessagePartList &parts)
+ : parts(parts)
+ {
+ }
+
+ MessagePartList parts;
+};
+
+/**
+ * Default constructor.
+ */
+ReceivedMessage::DeliveryDetails::DeliveryDetails()
+{
+}
+
+/**
+ * Copy constructor.
+ */
+ReceivedMessage::DeliveryDetails::DeliveryDetails(const DeliveryDetails &other)
+ : mPriv(other.mPriv)
+{
+}
+
+/**
+ * Construct a new ReceivedMessage::DeliveryDetails object.
+ *
+ * \param The message parts.
+ */
+ReceivedMessage::DeliveryDetails::DeliveryDetails(const MessagePartList &parts)
+ : mPriv(new Private(parts))
+{
+}
+
+/**
+ * Class destructor.
+ */
+ReceivedMessage::DeliveryDetails::~DeliveryDetails()
+{
+}
+
+/**
+ * Assignment operator.
+ */
+ReceivedMessage::DeliveryDetails &ReceivedMessage::DeliveryDetails::operator=(
+ const DeliveryDetails &other)
+{
+ this->mPriv = other.mPriv;
+ return *this;
+}
+
+/**
+ * Return the delivery status of a message.
+ *
+ * \return The delivery status as #DeliveryStatus.
+ */
+DeliveryStatus ReceivedMessage::DeliveryDetails::status() const
+{
+ if (!isValid()) {
+ return DeliveryStatusUnknown;
+ }
+ return static_cast<DeliveryStatus>(uintOrZeroFromPart(mPriv->parts, 0, "delivery-status"));
+}
+
+/**
+ * Return whether this delivery report contains an identifier for the message to which it
+ * refers.
+ *
+ * \return \c true if an original message token is known, \c false otherwise.
+ * \sa originalToken()
+ */
+bool ReceivedMessage::DeliveryDetails::hasOriginalToken() const
+{
+ if (!isValid()) {
+ return false;
+ }
+ return partContains(mPriv->parts, 0, "delivery-token");
+}
+
+/**
+ * Return an identifier for the message to which this delivery report refers, or an empty string if
+ * hasOriginalToken() returns \c false.
+ *
+ * Clients may match this against the token produced by the TextChannel::send() method and
+ * TextChannel::messageSent() signal. A status report with no token could match any sent message,
+ * and a sent message with an empty token could match any status report.
+ * If multiple sent messages match, clients should use some reasonable heuristic.
+ *
+ * \return The message token if hasOriginalToken() returns \c true, an empty string otherwise.
+ * \sa hasOriginalToken().
+ */
+QString ReceivedMessage::DeliveryDetails::originalToken() const
+{
+ if (!isValid()) {
+ return QString();
+ }
+ return stringOrEmptyFromPart(mPriv->parts, 0, "delivery-token");
+}
+
+/**
+ * Return whether the delivery of the message this delivery report refers to, failed.
+ *
+ * \return \c true if the message delivery failed, \c false otherwise.
+ * \sa error()
+ */
+bool ReceivedMessage::DeliveryDetails::isError() const
+{
+ if (!isValid()) {
+ return false;
+ }
+ DeliveryStatus st(status());
+ return st == DeliveryStatusTemporarilyFailed || st == DeliveryStatusPermanentlyFailed;
+}
+
+/**
+ * Return the reason for the delivery failure if known.
+ *
+ * \return The reason as #ChannelTextSendError.
+ * \sa isError()
+ */
+ChannelTextSendError ReceivedMessage::DeliveryDetails::error() const
+{
+ if (!isValid()) {
+ return ChannelTextSendErrorUnknown;
+ }
+ return static_cast<ChannelTextSendError>(uintOrZeroFromPart(mPriv->parts, 0, "delivery-error"));
+}
+
+/**
+ * Return whether this delivery report contains a debugging information on why the message it refers
+ * to could not be delivered.
+ *
+ * \return \c true if a debugging information is provided, \c false otherwise.
+ * \sa debugMessage()
+ */
+bool ReceivedMessage::DeliveryDetails::hasDebugMessage() const
+{
+ if (!isValid()) {
+ return false;
+ }
+ return partContains(mPriv->parts, 0, "delivery-error-message");
+}
+
+/**
+ * Return the debugging information on why the message this delivery report refers to could not be
+ * delivered.
+ *
+ * \return The debug string.
+ * \sa hasDebugMessage()
+ */
+QString ReceivedMessage::DeliveryDetails::debugMessage() const
+{
+ if (!isValid()) {
+ return QString();
+ }
+ return stringOrEmptyFromPart(mPriv->parts, 0, "delivery-error-message");
+}
+
+/**
+ * Return the reason for the delivery failure if known, specified as a
+ * (possibly implementation-specific) D-Bus error.
+ *
+ * \return The D-Bus error string representing the error.
+ */
+QString ReceivedMessage::DeliveryDetails::dbusError() const
+{
+ if (!isValid()) {
+ return QString();
+ }
+ QString ret = stringOrEmptyFromPart(mPriv->parts, 0, "delivery-dbus-error");
+ if (ret.isEmpty()) {
+ switch (error()) {
+ case ChannelTextSendErrorOffline:
+ ret = TP_QT_ERROR_OFFLINE;
+ break;
+ case ChannelTextSendErrorInvalidContact:
+ ret = TP_QT_ERROR_DOES_NOT_EXIST;
+ break;
+ case ChannelTextSendErrorPermissionDenied:
+ ret = TP_QT_ERROR_PERMISSION_DENIED;
+ break;
+ case ChannelTextSendErrorTooLong:
+ ret = TP_QT_ERROR_INVALID_ARGUMENT;
+ break;
+ case ChannelTextSendErrorNotImplemented:
+ ret = TP_QT_ERROR_NOT_IMPLEMENTED;
+ break;
+ default:
+ ret = TP_QT_ERROR_NOT_AVAILABLE;
+ }
+ }
+ return ret;
+}
+
+/**
+ * Return whether the message content for the message this delivery report refers to is known.
+ *
+ * \return \c true if the original message content is known, \c false otherwise.
+ * \sa echoedMessage()
+ */
+bool ReceivedMessage::DeliveryDetails::hasEchoedMessage() const
+{
+ if (!isValid()) {
+ return false;
+ }
+ return partContains(mPriv->parts, 0, "delivery-echo");
+}
+
+/**
+ * Return the Message object for the message this delivery report refers to, omitted if the message
+ * is unknown.
+ *
+ * <div class='rationale'>
+ * <h5>Rationale:</h5>
+ * <div>
+ * Some protocols, like XMPP, echo the failing message back to the sender. This is sometimes the
+ * only way to match it against the sent message, so we include it here.
+ * </div>
+ * </div>
+ *
+ * \return The Message object, or an empty Message object if hasEchoedMessage()
+ * returns \c false.
+ * \sa hasEchoedMessage()
+ */
+Message ReceivedMessage::DeliveryDetails::echoedMessage() const
+{
+ if (!isValid()) {
+ return Message();
+ }
+ return Message(partsFromPart(mPriv->parts, 0, "delivery-echo"));
+}
+
+/**
+ * \internal Default constructor.
+ */
+ReceivedMessage::ReceivedMessage()
+{
+}
+
+/**
+ * Construct a new ReceivedMessage object.
+ *
+ * \param parts The parts of a message as defined by the \telepathy_spec.
+ * This list must have length at least 1.
+ * \param channel The channel owning this message.
+ */
+ReceivedMessage::ReceivedMessage(const MessagePartList &parts,
+ const TextChannelPtr &channel)
+ : Message(parts)
+{
+ if (!mPriv->parts[0].contains(QLatin1String("message-received"))) {
+ mPriv->parts[0].insert(QLatin1String("message-received"),
+ QDBusVariant(static_cast<qlonglong>(
+ QDateTime::currentDateTime().toTime_t())));
+ }
+ mPriv->textChannel = QWeakPointer<TextChannel>(channel.data());
+}
+
+/**
+ * Copy constructor.
+ */
+ReceivedMessage::ReceivedMessage(const ReceivedMessage &other)
+ : Message(other)
+{
+}
+
+/**
+ * Assignment operator.
+ */
+ReceivedMessage &ReceivedMessage::operator=(const ReceivedMessage &other)
+{
+ if (this != &other) {
+ mPriv = other.mPriv;
+ }
+
+ return *this;
+}
+
+/**
+ * Class destructor.
+ */
+ReceivedMessage::~ReceivedMessage()
+{
+}
+
+/**
+ * Return the time the message was received.
+ *
+ * \return The timestamp as QDateTime, or QDateTime() if unknown.
+ */
+QDateTime ReceivedMessage::received() const
+{
+ // FIXME See http://bugs.freedesktop.org/show_bug.cgi?id=21690
+ uint stamp = valueFromPart(mPriv->parts, 0, "message-received").toUInt();
+ if (stamp != 0) {
+ return QDateTime::fromTime_t(stamp);
+ } else {
+ return QDateTime();
+ }
+}
+
+/**
+ * Return the contact who sent the message.
+ *
+ * \return A pointer to the Contact object.
+ * \sa senderNickname()
+ */
+ContactPtr ReceivedMessage::sender() const
+{
+ return mPriv->sender;
+}
+
+/**
+ * Return the nickname chosen by the sender of the message, which can be different for each
+ * message in a conversation.
+ *
+ * \return The nickname.
+ * \sa sender()
+ */
+QString ReceivedMessage::senderNickname() const
+{
+ QString ret = stringOrEmptyFromPart(mPriv->parts, 0, "sender-nickname");
+ if (ret.isEmpty() && mPriv->sender) {
+ ret = mPriv->sender->alias();
+ }
+ return ret;
+}
+
+/**
+ * If this message replaces a previous message, return the value of
+ * messageToken() for that previous message. Otherwise, return an empty string.
+ *
+ * For instance, a user interface could replace the superseded
+ * message with this message, or grey out the superseded message.
+ *
+ * \return The message token of the superseded message or an empty string if none.
+ */
+QString ReceivedMessage::supersededToken() const
+{
+ return stringOrEmptyFromPart(mPriv->parts, 0, "supersedes");
+}
+
+/**
+ * Return whether the incoming message was part of a replay of message
+ * history.
+ *
+ * If \c true, loggers can use this to improve their heuristics for elimination
+ * of duplicate messages (a simple, correct implementation would be to avoid
+ * logging any message that has this flag).
+ *
+ * \return \c true if the scrollback flag is set, \c false otherwise.
+ */
+bool ReceivedMessage::isScrollback() const
+{
+ return booleanFromPart(mPriv->parts, 0, "scrollback", false);
+}
+
+/**
+ * Return whether the incoming message was seen in a previous channel during
+ * the lifetime of the connection, but was not acknowledged before that
+ * channel closed, causing the channel in which it now appears to open.
+ *
+ * If \c true, loggers should not log this message again.
+ *
+ * \return \c true if the rescued flag is set, \c false otherwise.
+ */
+bool ReceivedMessage::isRescued() const
+{
+ return booleanFromPart(mPriv->parts, 0, "rescued", false);
+}
+
+/**
+ * Return whether the incoming message is a delivery report.
+ *
+ * \return \c true if a delivery report, \c false otherwise.
+ * \sa deliveryDetails()
+ */
+bool ReceivedMessage::isDeliveryReport() const
+{
+ return messageType() == ChannelTextMessageTypeDeliveryReport;
+}
+
+/**
+ * Return the details of a delivery report.
+ *
+ * This method should only be used if isDeliveryReport() returns \c true.
+ *
+ * \return The delivery report as a ReceivedMessage::DeliveryDetails object.
+ * \sa isDeliveryReport()
+ */
+ReceivedMessage::DeliveryDetails ReceivedMessage::deliveryDetails() const
+{
+ return DeliveryDetails(parts());
+}
+
+/**
+ * Return whether this message is from \a channel.
+ *
+ * \return \c true if the message is from \a channel, \c false otherwise.
+ */
+bool ReceivedMessage::isFromChannel(const TextChannelPtr &channel) const
+{
+ return TextChannelPtr(mPriv->textChannel) == channel;
+}
+
+uint ReceivedMessage::pendingId() const
+{
+ return mPriv->pendingId();
+}
+
+uint ReceivedMessage::senderHandle() const
+{
+ return mPriv->senderHandle();
+}
+
+QString ReceivedMessage::senderId() const
+{
+ return mPriv->senderId();
+}
+
+void ReceivedMessage::setForceNonText()
+{
+ mPriv->forceNonText = true;
+}
+
+void ReceivedMessage::clearSenderHandle()
+{
+ mPriv->clearSenderHandle();
+}
+
+void ReceivedMessage::setSender(const ContactPtr &sender)
+{
+ mPriv->sender = sender;
+}
+
+} // Tp
diff --git a/TelepathyQt/message.h b/TelepathyQt/message.h
new file mode 100644
index 00000000..c6a0211c
--- /dev/null
+++ b/TelepathyQt/message.h
@@ -0,0 +1,173 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_message_h_HEADER_GUARD_
+#define _TelepathyQt_message_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <QSharedDataPointer>
+
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/Contact>
+#include <TelepathyQt/Types>
+
+class QDateTime;
+
+namespace Tp
+{
+
+class Contact;
+class TextChannel;
+
+class TP_QT_EXPORT Message
+{
+public:
+ Message(ChannelTextMessageType, const QString &);
+ Message(const Message &other);
+ ~Message();
+
+ Message &operator=(const Message &other);
+ bool operator==(const Message &other) const;
+ inline bool operator!=(const Message &other) const
+ {
+ return !(*this == other);
+ }
+
+ // Convenient access to headers
+
+ QDateTime sent() const;
+
+ ChannelTextMessageType messageType() const;
+
+ bool isTruncated() const;
+
+ bool hasNonTextContent() const;
+
+ QString messageToken() const;
+
+ bool isSpecificToDBusInterface() const;
+ QString dbusInterface() const;
+
+ QString text() const;
+
+ // Direct access to the whole message (header and body)
+
+ MessagePart header() const;
+
+ int size() const;
+ MessagePart part(uint index) const;
+ MessagePartList parts() const;
+
+private:
+ friend class ContactMessenger;
+ friend class ReceivedMessage;
+ friend class TextChannel;
+
+ TP_QT_NO_EXPORT Message();
+ TP_QT_NO_EXPORT Message(const MessagePartList &parts);
+ TP_QT_NO_EXPORT Message(uint, uint, const QString &);
+
+ struct Private;
+ friend struct Private;
+ QSharedDataPointer<Private> mPriv;
+};
+
+class TP_QT_EXPORT ReceivedMessage : public Message
+{
+public:
+ class DeliveryDetails
+ {
+ public:
+ DeliveryDetails();
+ DeliveryDetails(const DeliveryDetails &other);
+ ~DeliveryDetails();
+
+ DeliveryDetails &operator=(const DeliveryDetails &other);
+
+ bool isValid() const { return mPriv.constData() != 0; }
+
+ DeliveryStatus status() const;
+
+ bool hasOriginalToken() const;
+ QString originalToken() const;
+
+ bool isError() const;
+ ChannelTextSendError error() const;
+
+ bool hasDebugMessage() const;
+ QString debugMessage() const;
+
+ QString dbusError() const;
+
+ bool hasEchoedMessage() const;
+ Message echoedMessage() const;
+
+ private:
+ friend class ReceivedMessage;
+
+ TP_QT_NO_EXPORT DeliveryDetails(const MessagePartList &parts);
+
+ struct Private;
+ friend struct Private;
+ QSharedDataPointer<Private> mPriv;
+ };
+
+ ReceivedMessage(const ReceivedMessage &other);
+ ReceivedMessage &operator=(const ReceivedMessage &other);
+ ~ReceivedMessage();
+
+ QDateTime received() const;
+ ContactPtr sender() const;
+ QString senderNickname() const;
+
+ QString supersededToken() const;
+
+ bool isScrollback() const;
+ bool isRescued() const;
+
+ bool isDeliveryReport() const;
+ DeliveryDetails deliveryDetails() const;
+
+ bool isFromChannel(const TextChannelPtr &channel) const;
+
+private:
+ friend class TextChannel;
+
+ TP_QT_NO_EXPORT ReceivedMessage(const MessagePartList &parts,
+ const TextChannelPtr &channel);
+ TP_QT_NO_EXPORT ReceivedMessage();
+
+ TP_QT_NO_EXPORT uint senderHandle() const;
+ TP_QT_NO_EXPORT QString senderId() const;
+ TP_QT_NO_EXPORT uint pendingId() const;
+
+ TP_QT_NO_EXPORT void setForceNonText();
+ TP_QT_NO_EXPORT void clearSenderHandle();
+ TP_QT_NO_EXPORT void setSender(const ContactPtr &sender);
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/method-invocation-context.dox b/TelepathyQt/method-invocation-context.dox
new file mode 100644
index 00000000..dd7f4ab5
--- /dev/null
+++ b/TelepathyQt/method-invocation-context.dox
@@ -0,0 +1,41 @@
+/*
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * \class Tp::MethodInvocationContext
+ * \ingroup utils
+ * \headerfile TelepathyQt/method-invocation-context.h <TelepathyQt/MethodInvocationContext>
+ *
+ * \brief The MethodInvocationContext class provides a way for the service implementation to
+ * respond to method calls.
+ *
+ * The methods setFinished() and setFinishedWithError() can be used to indicate
+ * whether the method call succeeded or failed.
+ *
+ * If neither setFinished() nor setFinishedWithError() is called explicitly,
+ * the method call will be considered to have failed.
+ *
+ " In case an asynchronous operation needs to be performed when implementing a method call
+ * receiving a MethodInvocationContextPtr object, a reference to this object may be kept around
+ * until all asynchronous operations finish, and the appropriate finish method
+ * should be called to indicate whether the method call succeeded or failed later.
+ */
diff --git a/TelepathyQt/method-invocation-context.h b/TelepathyQt/method-invocation-context.h
new file mode 100644
index 00000000..29c5f9fb
--- /dev/null
+++ b/TelepathyQt/method-invocation-context.h
@@ -0,0 +1,192 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_method_invocation_context_h_HEADER_GUARD_
+#define _TelepathyQt_method_invocation_context_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <QtDBus>
+#include <QtCore>
+
+namespace Tp
+{
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+namespace MethodInvocationContextTypes
+{
+
+struct Nil
+{
+};
+
+template<int Index,
+ typename T1, typename T2, typename T3, typename T4,
+ typename T5, typename T6, typename T7, typename T8>
+struct Select
+{
+ typedef Select<Index - 1, T2, T3, T4, T5, T6, T7, T8, Nil> Next;
+ typedef typename Next::Type Type;
+};
+template<typename T1, typename T2, typename T3, typename T4,
+ typename T5, typename T6, typename T7, typename T8>
+struct Select<0, T1, T2, T3, T4, T5, T6, T7, T8>
+{
+ typedef T1 Type;
+};
+
+template<typename T1, typename T2, typename T3, typename T4,
+ typename T5, typename T6, typename T7, typename T8>
+struct ForEach
+{
+ typedef ForEach<T2, T3, T4, T5, T6, T7, T8, Nil> Next;
+ enum { Total = Next::Total + 1 };
+};
+template<>
+struct ForEach<Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil>
+{
+ enum { Total = 0 };
+};
+
+}
+
+#endif /* DOXYGEN_SHOULD_SKIP_THIS */
+
+template<typename T1 = MethodInvocationContextTypes::Nil, typename T2 = MethodInvocationContextTypes::Nil,
+ typename T3 = MethodInvocationContextTypes::Nil, typename T4 = MethodInvocationContextTypes::Nil,
+ typename T5 = MethodInvocationContextTypes::Nil, typename T6 = MethodInvocationContextTypes::Nil,
+ typename T7 = MethodInvocationContextTypes::Nil, typename T8 = MethodInvocationContextTypes::Nil>
+class MethodInvocationContext : public RefCounted
+{
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+ template<int Index>
+ struct Select : MethodInvocationContextTypes::Select<Index, T1, T2, T3, T4, T5, T6, T7, T8>
+ {
+ };
+
+ typedef MethodInvocationContextTypes::ForEach<T1, T2, T3, T4, T5, T6, T7, T8> ForEach;
+ enum { Count = ForEach::Total };
+#endif /* DOXYGEN_SHOULD_SKIP_THIS */
+
+public:
+ MethodInvocationContext(const QDBusConnection &bus, const QDBusMessage &message)
+ : mBus(bus), mMessage(message), mFinished(false)
+ {
+ mMessage.setDelayedReply(true);
+ }
+
+ virtual ~MethodInvocationContext()
+ {
+ if (!mFinished) {
+ setFinishedWithError(QString(), QString());
+ }
+ }
+
+ bool isFinished() const { return mFinished; }
+ bool isError() const { return !mErrorName.isEmpty(); }
+ QString errorName() const { return mErrorName; }
+ QString errorMessage() const { return mErrorMessage; }
+
+ void setFinished(const T1 &t1 = T1(), const T2 &t2 = T2(), const T3 &t3 = T3(),
+ const T4 &t4 = T4(), const T5 &t5 = T5(), const T6 &t6 = T6(),
+ const T7 &t7 = T7(), const T8 &t8 = T8())
+ {
+ if (mFinished) {
+ return;
+ }
+
+ mFinished = true;
+
+ setReplyValue(0, qVariantFromValue(t1));
+ setReplyValue(1, qVariantFromValue(t2));
+ setReplyValue(2, qVariantFromValue(t3));
+ setReplyValue(3, qVariantFromValue(t4));
+ setReplyValue(4, qVariantFromValue(t5));
+ setReplyValue(5, qVariantFromValue(t6));
+ setReplyValue(6, qVariantFromValue(t7));
+ setReplyValue(7, qVariantFromValue(t8));
+
+ if (mReply.isEmpty()) {
+ mBus.send(mMessage.createReply());
+ } else {
+ mBus.send(mMessage.createReply(mReply));
+ }
+ onFinished();
+ }
+
+ void setFinishedWithError(const QString &errorName,
+ const QString &errorMessage)
+ {
+ if (mFinished) {
+ return;
+ }
+
+ mFinished = true;
+
+ if (errorName.isEmpty()) {
+ mErrorName = QLatin1String("org.freedesktop.Telepathy.Qt4.ErrorHandlingError");
+ } else {
+ mErrorName = errorName;
+ }
+ mErrorMessage = errorMessage;
+
+ mBus.send(mMessage.createErrorReply(mErrorName, mErrorMessage));
+ onFinished();
+ }
+
+ template<int Index> inline
+ typename Select<Index>::Type argumentAt() const
+ {
+ Q_ASSERT(Index >= 0 && Index < Count);
+ return qdbus_cast<typename Select<Index>::Type>(mReply.value(Index));
+ }
+
+protected:
+ virtual void onFinished() {}
+
+private:
+ Q_DISABLE_COPY(MethodInvocationContext)
+
+ void setReplyValue(int index, const QVariant &value)
+ {
+ if (index >= Count) {
+ return;
+ }
+ mReply.insert(index, value);
+ }
+
+ QDBusConnection mBus;
+ QDBusMessage mMessage;
+ bool mFinished;
+ QList<QVariant> mReply;
+ QString mErrorName;
+ QString mErrorMessage;
+};
+
+} // Tp
+
+Q_DECLARE_METATYPE(Tp::MethodInvocationContextTypes::Nil)
+
+#endif
diff --git a/TelepathyQt/not-filter.dox b/TelepathyQt/not-filter.dox
new file mode 100644
index 00000000..3c45a812
--- /dev/null
+++ b/TelepathyQt/not-filter.dox
@@ -0,0 +1,32 @@
+/*
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * \class Tp::NotFilter
+ * \ingroup utils
+ * \headerfile TelepathyQt/not-filter.h <TelepathyQt/NotFilter>
+ *
+ * \brief The NotFilter class provides a generic filter object to be used
+ * in conjunction of other filters.
+ *
+ * The NotFilter will match if its given filter does not match its criteria.
+ */
diff --git a/TelepathyQt/not-filter.h b/TelepathyQt/not-filter.h
new file mode 100644
index 00000000..b31b38ee
--- /dev/null
+++ b/TelepathyQt/not-filter.h
@@ -0,0 +1,73 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_not_filter_h_HEADER_GUARD_
+#define _TelepathyQt_not_filter_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Filter>
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+template <class T>
+class NotFilter : public Filter<T>
+{
+public:
+ static SharedPtr<NotFilter<T> > create(
+ const SharedPtr<const Filter<T> > &filter = SharedPtr<const Filter<T> >())
+ {
+ return SharedPtr<NotFilter<T> >(new NotFilter<T>(filter));
+ }
+
+ inline virtual ~NotFilter() { }
+
+ inline virtual bool isValid() const
+ {
+ return mFilter && mFilter->isValid();
+ }
+
+ inline virtual bool matches(const SharedPtr<T> &t) const
+ {
+ if (!isValid()) {
+ return false;
+ }
+
+ return !mFilter->matches(t);
+ }
+
+ inline SharedPtr<const Filter<T> > filter() const { return mFilter; }
+
+private:
+ NotFilter(const SharedPtr<const Filter<T> > &filter)
+ : Filter<T>(), mFilter(filter) { }
+
+ SharedPtr<const Filter<T> > mFilter;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/object.cpp b/TelepathyQt/object.cpp
new file mode 100644
index 00000000..8d5555f4
--- /dev/null
+++ b/TelepathyQt/object.cpp
@@ -0,0 +1,65 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/Object>
+
+#include "TelepathyQt/_gen/object.moc.hpp"
+
+namespace Tp
+{
+
+/**
+ * \class Object
+ * \ingroup clientobject
+ * \headerfile TelepathyQt/object.h <TelepathyQt/Object>
+ *
+ * \brief The Object class provides an object with property notification.
+ */
+
+/**
+ * Construct a new Object object.
+ */
+Object::Object()
+ : QObject()
+{
+}
+
+/**
+ * Class destructor.
+ */
+Object::~Object()
+{
+}
+
+/**
+ * Notify that a property named \a propertyName changed.
+ *
+ * This method will emit propertyChanged() for \a propertyName.
+ *
+ * \todo Use for more classes beyond Account. Most importantly Contact.
+ */
+void Object::notify(const char *propertyName)
+{
+ emit propertyChanged(QLatin1String(propertyName));
+}
+
+} // Tp
diff --git a/TelepathyQt/object.h b/TelepathyQt/object.h
new file mode 100644
index 00000000..7e24fb88
--- /dev/null
+++ b/TelepathyQt/object.h
@@ -0,0 +1,63 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_object_h_HEADER_GUARD_
+#define _TelepathyQt_object_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Global>
+#include <TelepathyQt/RefCounted>
+
+#include <QObject>
+#include <QString>
+
+namespace Tp
+{
+
+class TP_QT_EXPORT Object : public QObject, public RefCounted
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(Object)
+
+public:
+ virtual ~Object();
+
+Q_SIGNALS:
+ void propertyChanged(const QString &propertyName);
+
+protected:
+ Object();
+
+ void notify(const char *propertyName);
+
+private:
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/optional-interface-factory.cpp b/TelepathyQt/optional-interface-factory.cpp
new file mode 100644
index 00000000..4b0f8b8a
--- /dev/null
+++ b/TelepathyQt/optional-interface-factory.cpp
@@ -0,0 +1,178 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/OptionalInterfaceFactory>
+
+#include <TelepathyQt/AbstractInterface>
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <QMap>
+#include <QString>
+
+namespace Tp
+{
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+struct TP_QT_NO_EXPORT OptionalInterfaceCache::Private
+{
+ QObject *proxy;
+ QMap<QString, AbstractInterface*> interfaces;
+
+ Private(QObject *proxy);
+};
+
+OptionalInterfaceCache::Private::Private(QObject *proxy)
+ : proxy(proxy)
+{
+}
+
+/**
+ * Class constructor.
+ */
+OptionalInterfaceCache::OptionalInterfaceCache(QObject *proxy)
+ : mPriv(new Private(proxy))
+{
+}
+
+/**
+ * Class destructor.
+ *
+ * Frees all interface instances constructed by this factory.
+ */
+OptionalInterfaceCache::~OptionalInterfaceCache()
+{
+ delete mPriv;
+}
+
+QObject *OptionalInterfaceCache::proxy() const
+{
+ return mPriv->proxy;
+}
+
+AbstractInterface *OptionalInterfaceCache::getCached(const QString &name) const
+{
+ if (mPriv->interfaces.contains(name)) {
+ return mPriv->interfaces.value(name);
+ } else {
+ return 0;
+ }
+}
+
+void OptionalInterfaceCache::cache(AbstractInterface *interface) const
+{
+ QString name = interface->interface();
+ Q_ASSERT(!mPriv->interfaces.contains(name));
+
+ mPriv->interfaces[name] = interface;
+}
+
+#endif /* ifndef DOXYGEN_SHOULD_SKIP_THIS */
+
+/**
+ * \class OptionalInterfaceFactory
+ * \ingroup clientsideproxies
+ * \headerfile TelepathyQt/optional-interface-factory.h <TelepathyQt/OptionalInterfaceFactory>
+ *
+ * \brief The OptionalInterfaceFactory class is a helper class for
+ * high-level D-Bus proxy classes willing to offer access to shared
+ * instances of interface proxies for optional interfaces.
+ *
+ * To use this helper in a subclass of DBusProxy (say, ExampleObject),
+ * ExampleObject should inherit from
+ * OptionalInterfaceFactory<ExampleObject>, and call
+ * OptionalInterfaceFactory(this) in its constructor's initialization list.
+ *
+ * \tparam DBusProxySubclass A subclass of DBusProxy
+ */
+
+/**
+ * \enum OptionalInterfaceFactory::InterfaceSupportedChecking
+ *
+ * Specifies if the interface being supported by the remote object should be
+ * checked by optionalInterface() and the convenience functions for it.
+ *
+ * \sa optionalInterface()
+ */
+
+/**
+ * \var OptionalInterfaceFactory::InterfaceSupportedChecking OptionalInterfaceFactory::CheckInterfaceSupported
+ *
+ * Don't return an interface instance unless it can be guaranteed that the
+ * remote object actually implements the interface.
+ */
+
+/**
+ * \var OptionalInterfaceFactory::InterfaceSupportedChecking OptionalInterfaceFactory::BypassInterfaceCheck
+ *
+ * Return an interface instance even if it can't be verified that the remote
+ * object supports the interface.
+ */
+
+/**
+ * \fn OptionalInterfaceFactory::OptionalInterfaceFactory(DBusProxySubclass *this_)
+ *
+ * Class constructor.
+ *
+ * \param this_ The class to which this OptionalInterfaceFactory is
+ * attached
+ */
+
+/**
+ * \fn OptionalInterfaceFactory::~OptionalInterfaceFactory()
+ *
+ * Class destructor.
+ *
+ * Frees all interface instances constructed by this factory.
+ */
+
+ /**
+ * \fn OptionalInterfaceFactory::interfaces() const;
+ *
+ * Return a list of interfaces supported by this object.
+ *
+ * \return List of supported interfaces.
+ */
+
+/**
+ * \fn template <typename Interface> inline Interface *OptionalInterfaceFactory::interface() const
+ *
+ * Return a pointer to a valid instance of a interface class, associated
+ * with the same remote object as the given main interface instance. The
+ * given main interface must be of the class the optional interface is
+ * generated for (for eg. ChannelInterfaceGroupInterface this means
+ * ChannelInterface) or a subclass.
+ *
+ * First invocation of this method for a particular optional interface
+ * class will construct the instance; subsequent calls will return a
+ * pointer to the same instance.
+ *
+ * The returned instance is freed when the factory is destroyed; using
+ * it after destroying the factory will likely produce a crash. As the
+ * instance is shared, it should not be freed directly.
+ *
+ * \tparam Interface Class of the interface instance to get.
+ * \return A pointer to an optional interface instance.
+ */
+
+} // Tp
diff --git a/TelepathyQt/optional-interface-factory.h b/TelepathyQt/optional-interface-factory.h
new file mode 100644
index 00000000..babbe47e
--- /dev/null
+++ b/TelepathyQt/optional-interface-factory.h
@@ -0,0 +1,140 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_optional_interface_factory_h_HEADER_GUARD_
+#define _TelepathyQt_optional_interface_factory_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Global>
+
+#include <QObject>
+#include <QStringList>
+#include <QtGlobal>
+
+namespace Tp
+{
+
+class AbstractInterface;
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+class TP_QT_EXPORT OptionalInterfaceCache
+{
+ Q_DISABLE_COPY(OptionalInterfaceCache)
+
+public:
+ explicit OptionalInterfaceCache(QObject *proxy);
+
+ ~OptionalInterfaceCache();
+
+protected:
+ AbstractInterface *getCached(const QString &name) const;
+ void cache(AbstractInterface *interface) const;
+ QObject *proxy() const;
+
+private:
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+#endif /* DOXYGEN_SHOULD_SKIP_THIS */
+
+template <typename DBusProxySubclass> class OptionalInterfaceFactory
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+ : private OptionalInterfaceCache
+#endif
+{
+ Q_DISABLE_COPY(OptionalInterfaceFactory)
+
+public:
+ enum InterfaceSupportedChecking
+ {
+ CheckInterfaceSupported,
+ BypassInterfaceCheck
+ };
+
+ inline OptionalInterfaceFactory(DBusProxySubclass *this_)
+ : OptionalInterfaceCache(this_)
+ {
+ }
+
+ inline ~OptionalInterfaceFactory()
+ {
+ }
+
+ inline QStringList interfaces() const { return mInterfaces; }
+
+ inline bool hasInterface(const QString &name) const
+ {
+ return mInterfaces.contains(name);
+ }
+
+ template <class Interface>
+ inline Interface *optionalInterface(
+ InterfaceSupportedChecking check = CheckInterfaceSupported) const
+ {
+ // Check for the remote object supporting the interface
+ QString name(QLatin1String(Interface::staticInterfaceName()));
+ if (check == CheckInterfaceSupported && !mInterfaces.contains(name)) {
+ return 0;
+ }
+
+ // If present or forced, delegate to OptionalInterfaceFactory
+ return interface<Interface>();
+ }
+
+ template <typename Interface>
+ inline Interface *interface() const
+ {
+ AbstractInterface* interfaceMustBeASubclassOfAbstractInterface = static_cast<Interface *>(NULL);
+ Q_UNUSED(interfaceMustBeASubclassOfAbstractInterface);
+
+ // If there is a interface cached already, return it
+ QString name(QLatin1String(Interface::staticInterfaceName()));
+ AbstractInterface *cached = getCached(name);
+ if (cached)
+ return static_cast<Interface *>(cached);
+
+ // Otherwise, cache and return a newly constructed proxy
+ Interface *interface = new Interface(
+ static_cast<DBusProxySubclass *>(proxy()));
+ cache(interface);
+ return interface;
+ }
+
+protected:
+ inline void setInterfaces(const QStringList &interfaces)
+ {
+ mInterfaces = interfaces;
+ }
+
+private:
+ QStringList mInterfaces;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/or-filter.dox b/TelepathyQt/or-filter.dox
new file mode 100644
index 00000000..7829397c
--- /dev/null
+++ b/TelepathyQt/or-filter.dox
@@ -0,0 +1,33 @@
+/*
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * \class Tp::OrFilter
+ * \ingroup utils
+ * \headerfile TelepathyQt/or-filter.h <TelepathyQt/OrFilter>
+ *
+ * \brief The OrFilter class provides a generic filter object to be used
+ * in conjunction of other filters.
+ *
+ * The OrFilter will match if any of its given list of filters matches
+ * their criteria.
+ */
diff --git a/TelepathyQt/or-filter.h b/TelepathyQt/or-filter.h
new file mode 100644
index 00000000..c9b1c428
--- /dev/null
+++ b/TelepathyQt/or-filter.h
@@ -0,0 +1,83 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_or_filter_h_HEADER_GUARD_
+#define _TelepathyQt_or_filter_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Filter>
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+template <class T>
+class OrFilter : public Filter<T>
+{
+public:
+ static SharedPtr<OrFilter<T> > create(
+ const QList<SharedPtr<const Filter<T> > > &filters = QList<SharedPtr<const Filter<T> > >())
+ {
+ return SharedPtr<OrFilter<T> >(new OrFilter<T>(filters));
+ }
+
+ inline virtual ~OrFilter() { }
+
+ inline virtual bool isValid() const
+ {
+ Q_FOREACH (const SharedPtr<const Filter<T> > &filter, mFilters) {
+ if (!filter || !filter->isValid()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ inline virtual bool matches(const SharedPtr<T> &t) const
+ {
+ if (!isValid()) {
+ return false;
+ }
+
+ Q_FOREACH (const SharedPtr<const Filter<T> > &filter, mFilters) {
+ if (filter->matches(t)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ inline QList<SharedPtr<const Filter<T> > > filters() const { return mFilters; }
+
+private:
+ OrFilter(const QList<SharedPtr<const Filter<T> > > &filters)
+ : Filter<T>(), mFilters(filters) { }
+
+ QList<SharedPtr<const Filter<T> > > mFilters;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/outgoing-file-transfer-channel.cpp b/TelepathyQt/outgoing-file-transfer-channel.cpp
new file mode 100644
index 00000000..2fbd4504
--- /dev/null
+++ b/TelepathyQt/outgoing-file-transfer-channel.cpp
@@ -0,0 +1,371 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/OutgoingFileTransferChannel>
+
+#include "TelepathyQt/_gen/outgoing-file-transfer-channel.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/Connection>
+#include <TelepathyQt/PendingFailure>
+#include <TelepathyQt/PendingVariant>
+#include <TelepathyQt/Types>
+#include <TelepathyQt/types-internal.h>
+
+#include <QIODevice>
+#include <QTcpSocket>
+
+namespace Tp
+{
+
+static const int FT_BLOCK_SIZE = 16 * 1024;
+
+struct TP_QT_NO_EXPORT OutgoingFileTransferChannel::Private
+{
+ Private(OutgoingFileTransferChannel *parent);
+ ~Private();
+
+ // Public object
+ OutgoingFileTransferChannel *parent;
+
+ Client::ChannelTypeFileTransferInterface *fileTransferInterface;
+
+ // Introspection
+ QIODevice *input;
+ QTcpSocket *socket;
+ SocketAddressIPv4 addr;
+
+ qint64 pos;
+};
+
+OutgoingFileTransferChannel::Private::Private(OutgoingFileTransferChannel *parent)
+ : parent(parent),
+ fileTransferInterface(parent->interface<Client::ChannelTypeFileTransferInterface>()),
+ input(0),
+ socket(0),
+ pos(0)
+{
+}
+
+OutgoingFileTransferChannel::Private::~Private()
+{
+}
+
+/**
+ * \class OutgoingFileTransferChannel
+ * \ingroup clientchannel
+ * \headerfile TelepathyQt/outgoing-file-transfer-channel.h <TelepathyQt/OutgoingFileTransferChannel>
+ *
+ * \brief The OutgoingFileTransferChannel class represents a Telepathy channel
+ * of type FileTransfer for outgoing file transfers.
+ *
+ * For more details, please refer to \telepathy_spec.
+ *
+ * See \ref async_model, \ref shared_ptr
+ */
+
+/**
+ * Feature representing the core that needs to become ready to make the
+ * OutgoingFileTransferChannel object usable.
+ *
+ * This is currently the same as FileTransferChannel::FeatureCore, but may change to include more.
+ *
+ * When calling isReady(), becomeReady(), this feature is implicitly added
+ * to the requested features.
+ */
+const Feature OutgoingFileTransferChannel::FeatureCore =
+ Feature(QLatin1String(FileTransferChannel::staticMetaObject.className()), 0); // FT::FeatureCore
+
+/**
+ * Create a new OutgoingFileTransferChannel object.
+ *
+ * \param connection Connection owning this channel, and specifying the
+ * service.
+ * \param objectPath The channel object path.
+ * \param immutableProperties The channel immutable properties.
+ * \return An OutgoingFileTransferChannelPtr object pointing to the newly created
+ * OutgoingFileTransferChannel object.
+ */
+OutgoingFileTransferChannelPtr OutgoingFileTransferChannel::create(const ConnectionPtr &connection,
+ const QString &objectPath, const QVariantMap &immutableProperties)
+{
+ return OutgoingFileTransferChannelPtr(new OutgoingFileTransferChannel(
+ connection, objectPath, immutableProperties,
+ OutgoingFileTransferChannel::FeatureCore));
+}
+
+/**
+ * Construct a new OutgoingFileTransferChannel object.
+ *
+ * \param connection Connection owning this channel, and specifying the
+ * service.
+ * \param objectPath The channel object path.
+ * \param immutableProperties The channel immutable properties.
+ * \param coreFeature The core feature of the channel type, if any. The corresponding introspectable should
+ * depend on OutgoingFileTransferChannel::FeatureCore.
+ */
+OutgoingFileTransferChannel::OutgoingFileTransferChannel(
+ const ConnectionPtr &connection,
+ const QString &objectPath,
+ const QVariantMap &immutableProperties,
+ const Feature &coreFeature)
+ : FileTransferChannel(connection, objectPath, immutableProperties, coreFeature),
+ mPriv(new Private(this))
+{
+}
+
+/**
+ * Class destructor.
+ */
+OutgoingFileTransferChannel::~OutgoingFileTransferChannel()
+{
+ delete mPriv;
+}
+
+/**
+ * Provide the file for an outgoing file transfer which has been offered.
+ *
+ * The state will change to #FileTransferStateOpen as soon as the transfer
+ * starts.
+ * The given input device should not be destroyed until the state()
+ * changes to #FileTransferStateCompleted or #FileTransferStateCancelled.
+ * If input is a sequential device QIODevice::isSequential(), it should be
+ * closed when no more data is available, so that it's known when to stop reading.
+ *
+ * Only the primary handler of a file transfer channel may call this method.
+ *
+ * This method requires FileTransferChannel::FeatureCore to be ready.
+ *
+ * \param input A QIODevice object where the data will be read from.
+ * \return A PendingOperation object which will emit PendingOperation::finished
+ * when the call has finished.
+ * \sa stateChanged(), state(), stateReason()
+ */
+PendingOperation *OutgoingFileTransferChannel::provideFile(QIODevice *input)
+{
+ if (!isReady(FileTransferChannel::FeatureCore)) {
+ warning() << "FileTransferChannel::FeatureCore must be ready before "
+ "calling provideFile";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Channel not ready"),
+ OutgoingFileTransferChannelPtr(this));
+ }
+
+ // let's fail here direclty as we may only have one device to handle
+ if (mPriv->input) {
+ warning() << "File transfer can only be started once in the same "
+ "channel";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("File transfer can only be started once in the same channel"),
+ OutgoingFileTransferChannelPtr(this));
+ }
+
+ if ((!input->isOpen() && !input->open(QIODevice::ReadOnly)) &&
+ !input->isReadable()) {
+ warning() << "Unable to open IO device for reading";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_PERMISSION_DENIED),
+ QLatin1String("Unable to open IO device for reading"),
+ OutgoingFileTransferChannelPtr(this));
+ }
+
+ mPriv->input = input;
+ connect(input,
+ SIGNAL(aboutToClose()),
+ SLOT(onInputAboutToClose()));
+
+ PendingVariant *pv = new PendingVariant(
+ mPriv->fileTransferInterface->ProvideFile(
+ SocketAddressTypeIPv4,
+ SocketAccessControlLocalhost,
+ QDBusVariant(QVariant(QString()))),
+ OutgoingFileTransferChannelPtr(this));
+ connect(pv,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onProvideFileFinished(Tp::PendingOperation*)));
+ return pv;
+}
+
+void OutgoingFileTransferChannel::onProvideFileFinished(PendingOperation *op)
+{
+ if (op->isError()) {
+ warning() << "Error providing file transfer " <<
+ op->errorName() << ":" << op->errorMessage();
+ invalidate(op->errorName(), op->errorMessage());
+ return;
+ }
+
+ PendingVariant *pv = qobject_cast<PendingVariant *>(op);
+ mPriv->addr = qdbus_cast<SocketAddressIPv4>(pv->result());
+ debug().nospace() << "Got address " << mPriv->addr.address <<
+ ":" << mPriv->addr.port;
+
+ if (state() == FileTransferStateOpen) {
+ connectToHost();
+ }
+}
+
+void OutgoingFileTransferChannel::connectToHost()
+{
+ if (isConnected() || mPriv->addr.address.isNull()) {
+ return;
+ }
+
+ mPriv->socket = new QTcpSocket(this);
+
+ connect(mPriv->socket, SIGNAL(connected()),
+ SLOT(onSocketConnected()));
+ connect(mPriv->socket, SIGNAL(disconnected()),
+ SLOT(onSocketDisconnected()));
+ connect(mPriv->socket, SIGNAL(error(QAbstractSocket::SocketError)),
+ SLOT(onSocketError(QAbstractSocket::SocketError)));
+ connect(mPriv->socket, SIGNAL(bytesWritten(qint64)),
+ SLOT(doTransfer()));
+
+ debug().nospace() << "Connecting to host " <<
+ mPriv->addr.address << ":" << mPriv->addr.port << "...";
+ mPriv->socket->connectToHost(mPriv->addr.address, mPriv->addr.port);
+}
+
+void OutgoingFileTransferChannel::onSocketConnected()
+{
+ debug() << "Connected to host";
+ setConnected();
+
+ connect(mPriv->input, SIGNAL(readyRead()),
+ SLOT(doTransfer()));
+
+ // for non sequential devices, let's seek to the initialOffset
+ if (!mPriv->input->isSequential()) {
+ if (mPriv->input->seek(initialOffset())) {
+ mPriv->pos = initialOffset();
+ }
+ }
+
+ debug() << "Starting transfer...";
+ doTransfer();
+}
+
+void OutgoingFileTransferChannel::onSocketDisconnected()
+{
+ debug() << "Disconnected from host";
+ setFinished();
+}
+
+void OutgoingFileTransferChannel::onSocketError(QAbstractSocket::SocketError error)
+{
+ debug() << "Socket error" << error;
+ setFinished();
+}
+
+void OutgoingFileTransferChannel::onInputAboutToClose()
+{
+ debug() << "Input closed";
+
+ // read all remaining data from input device and write to output device
+ if (isConnected()) {
+ QByteArray data;
+ data = mPriv->input->readAll();
+ mPriv->socket->write(data); // never fails
+ }
+
+ setFinished();
+}
+
+void OutgoingFileTransferChannel::doTransfer()
+{
+ // read FT_BLOCK_SIZE each time, as input can be a QFile, we don't want to
+ // block reading the whole file
+ char buffer[FT_BLOCK_SIZE];
+ char *p = buffer;
+
+ memset(buffer, 0, sizeof(buffer));
+ qint64 len = mPriv->input->read(buffer, sizeof(buffer));
+
+ bool scheduleTransfer = false;
+ if (((qulonglong) mPriv->pos < initialOffset()) && (len > 0)) {
+ qint64 skip = (qint64) qMin(initialOffset() - mPriv->pos,
+ (qulonglong) len);
+
+ debug() << "skipping" << skip << "bytes";
+ if (skip == len) {
+ // nothing to write, all data read was skipped
+ // schedule a transfer, as readyRead may never be called and
+ // bytesWriiten will not
+ scheduleTransfer = true;
+ goto end;
+ }
+
+ p += skip;
+ len -= skip;
+ }
+
+ if (len > 0) {
+ mPriv->socket->write(p, len); // never fails
+ }
+
+end:
+ if (len == -1 || (!mPriv->input->isSequential() && mPriv->input->atEnd())) {
+ // error or EOF
+ setFinished();
+ return;
+ }
+
+ mPriv->pos += len;
+
+ if (scheduleTransfer) {
+ QMetaObject::invokeMethod(this, SLOT(doTransfer()),
+ Qt::QueuedConnection);
+ }
+}
+
+void OutgoingFileTransferChannel::setFinished()
+{
+ if (isFinished()) {
+ // it shouldn't happen but let's make sure
+ return;
+ }
+
+ if (mPriv->socket) {
+ disconnect(mPriv->socket, SIGNAL(connected()),
+ this, SLOT(onSocketConnected()));
+ disconnect(mPriv->socket, SIGNAL(disconnected()),
+ this, SLOT(onSocketDisconnected()));
+ disconnect(mPriv->socket, SIGNAL(error(QAbstractSocket::SocketError)),
+ this, SLOT(onSocketError(QAbstractSocket::SocketError)));
+ disconnect(mPriv->socket, SIGNAL(bytesWritten(qint64)),
+ this, SLOT(doTransfer()));
+ mPriv->socket->close();
+ }
+
+ if (mPriv->input) {
+ disconnect(mPriv->input, SIGNAL(aboutToClose()),
+ this, SLOT(onInputAboutToClose()));
+ disconnect(mPriv->input, SIGNAL(readyRead()),
+ this, SLOT(doTransfer()));
+ mPriv->input->close();
+ }
+
+ FileTransferChannel::setFinished();
+}
+
+} // Tp
diff --git a/TelepathyQt/outgoing-file-transfer-channel.h b/TelepathyQt/outgoing-file-transfer-channel.h
new file mode 100644
index 00000000..37daa842
--- /dev/null
+++ b/TelepathyQt/outgoing-file-transfer-channel.h
@@ -0,0 +1,78 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_outgoing_file_transfer_channel_h_HEADER_GUARD_
+#define _TelepathyQt_outgoing_file_transfer_channel_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/FileTransferChannel>
+
+#include <QAbstractSocket>
+
+namespace Tp
+{
+
+class TP_QT_EXPORT OutgoingFileTransferChannel : public FileTransferChannel
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(OutgoingFileTransferChannel)
+
+public:
+ static const Feature FeatureCore;
+
+ static OutgoingFileTransferChannelPtr create(const ConnectionPtr &connection,
+ const QString &objectPath, const QVariantMap &immutableProperties);
+
+ virtual ~OutgoingFileTransferChannel();
+
+ PendingOperation *provideFile(QIODevice *input);
+
+protected:
+ OutgoingFileTransferChannel(const ConnectionPtr &connection,
+ const QString &objectPath,
+ const QVariantMap &immutableProperties,
+ const Feature &coreFeature = OutgoingFileTransferChannel::FeatureCore);
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onProvideFileFinished(Tp::PendingOperation *op);
+
+ TP_QT_NO_EXPORT void onSocketConnected();
+ TP_QT_NO_EXPORT void onSocketDisconnected();
+ TP_QT_NO_EXPORT void onSocketError(QAbstractSocket::SocketError error);
+ TP_QT_NO_EXPORT void onInputAboutToClose();
+ TP_QT_NO_EXPORT void doTransfer();
+
+private:
+ TP_QT_NO_EXPORT void connectToHost();
+ TP_QT_NO_EXPORT void setFinished();
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/outgoing-stream-tube-channel-internal.h b/TelepathyQt/outgoing-stream-tube-channel-internal.h
new file mode 100644
index 00000000..b7fe41c8
--- /dev/null
+++ b/TelepathyQt/outgoing-stream-tube-channel-internal.h
@@ -0,0 +1,122 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_outgoing_stream_tube_channel_internal_h_HEADER_GUARD_
+#define _TelepathyQt_outgoing_stream_tube_channel_internal_h_HEADER_GUARD_
+
+#include <TelepathyQt/OutgoingStreamTubeChannel>
+#include <TelepathyQt/PendingOperation>
+
+namespace Tp
+{
+
+class PendingVoid;
+
+class TP_QT_NO_EXPORT PendingOpenTube : public PendingOperation
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(PendingOpenTube)
+
+public:
+ PendingOpenTube(PendingVoid *offerOperation,
+ const QVariantMap &parameters,
+ const OutgoingStreamTubeChannelPtr &object);
+ ~PendingOpenTube();
+
+private Q_SLOTS:
+ void onTubeStateChanged(Tp::TubeChannelState state);
+ void onOfferFinished(Tp::PendingOperation *operation);
+
+private:
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+class TP_QT_NO_EXPORT QueuedContactFactory : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(QueuedContactFactory)
+
+public:
+ QueuedContactFactory(ContactManagerPtr contactManager, QObject* parent = 0);
+ ~QueuedContactFactory();
+
+ QUuid appendNewRequest(const UIntList &handles);
+
+Q_SIGNALS:
+ void contactsRetrieved(QUuid uuid, QList<Tp::ContactPtr> contacts);
+
+private Q_SLOTS:
+ void onPendingContactsFinished(Tp::PendingOperation *operation);
+
+private:
+ struct Entry {
+ QUuid uuid;
+ UIntList handles;
+ };
+
+ void processNextRequest();
+
+ bool m_isProcessing;
+ ContactManagerPtr m_manager;
+ QQueue<Entry> m_queue;
+};
+
+struct TP_QT_NO_EXPORT PendingOpenTube::Private
+{
+ Private(const QVariantMap &parameters, PendingOpenTube *parent);
+
+ // Public object
+ PendingOpenTube *parent;
+
+ OutgoingStreamTubeChannelPtr tube;
+ QVariantMap parameters;
+};
+
+struct TP_QT_NO_EXPORT OutgoingStreamTubeChannel::Private
+{
+ Private(OutgoingStreamTubeChannel *parent);
+
+ OutgoingStreamTubeChannel *parent;
+
+ QHash<uint, Tp::ContactPtr> contactsForConnections;
+ QHash<QPair<QHostAddress, quint16>, uint> connectionsForSourceAddresses;
+ QHash<uchar, uint> connectionsForCredentials;
+
+ QHash<QUuid, QPair<uint, QDBusVariant> > pendingNewConnections;
+
+ struct ClosedConnection {
+ uint id;
+ QString error, message;
+
+ ClosedConnection() : id(~0U) {}
+ ClosedConnection(uint id, const QString &error, const QString &message)
+ : id(id), error(error), message(message) {}
+ };
+ QHash<QUuid, ClosedConnection> pendingClosedConnections;
+
+ QueuedContactFactory *queuedContactFactory;
+};
+
+}
+
+#endif
diff --git a/TelepathyQt/outgoing-stream-tube-channel.cpp b/TelepathyQt/outgoing-stream-tube-channel.cpp
new file mode 100644
index 00000000..22af7b8f
--- /dev/null
+++ b/TelepathyQt/outgoing-stream-tube-channel.cpp
@@ -0,0 +1,821 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010-2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/OutgoingStreamTubeChannel>
+#include "TelepathyQt/outgoing-stream-tube-channel-internal.h"
+
+#include "TelepathyQt/_gen/outgoing-stream-tube-channel.moc.hpp"
+#include "TelepathyQt/_gen/outgoing-stream-tube-channel-internal.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+#include "TelepathyQt/types-internal.h"
+
+#include <TelepathyQt/Connection>
+#include <TelepathyQt/ContactManager>
+#include <TelepathyQt/PendingContacts>
+#include <TelepathyQt/PendingFailure>
+#include <TelepathyQt/Types>
+
+#include <QHostAddress>
+#include <QTcpServer>
+#include <QLocalServer>
+
+namespace Tp
+{
+
+PendingOpenTube::Private::Private(const QVariantMap &parameters, PendingOpenTube *parent)
+ : parent(parent),
+ parameters(parameters)
+{
+}
+
+PendingOpenTube::PendingOpenTube(
+ PendingVoid *offerOperation,
+ const QVariantMap &parameters,
+ const OutgoingStreamTubeChannelPtr &object)
+ : PendingOperation(object),
+ mPriv(new Private(parameters, this))
+{
+ mPriv->tube = object;
+
+ // FIXME: connect to channel invalidation here also
+
+ debug() << "Calling StreamTube.Offer";
+ if (offerOperation->isFinished()) {
+ onOfferFinished(offerOperation);
+ } else {
+ // Connect the pending void
+ connect(offerOperation, SIGNAL(finished(Tp::PendingOperation*)),
+ this, SLOT(onOfferFinished(Tp::PendingOperation*)));
+ }
+}
+
+PendingOpenTube::~PendingOpenTube()
+{
+ delete mPriv;
+}
+
+void PendingOpenTube::onOfferFinished(PendingOperation *op)
+{
+ if (op->isError()) {
+ warning().nospace() << "StreamTube.Offer failed with " <<
+ op->errorName() << ": " << op->errorMessage();
+ setFinishedWithError(op->errorName(), op->errorMessage());
+ return;
+ }
+
+ debug() << "StreamTube.Offer returned successfully";
+
+ // It might have been already opened - check
+ if (mPriv->tube->state() != TubeChannelStateOpen) {
+ debug() << "Awaiting tube to be opened";
+ // Wait until the tube gets opened on the other side
+ connect(mPriv->tube.data(),
+ SIGNAL(stateChanged(Tp::TubeChannelState)),
+ SLOT(onTubeStateChanged(Tp::TubeChannelState)));
+ }
+
+ onTubeStateChanged(mPriv->tube->state());
+}
+
+void PendingOpenTube::onTubeStateChanged(TubeChannelState state)
+{
+ if (state == TubeChannelStateOpen) {
+ debug() << "Tube is now opened";
+ // Inject the parameters into the tube
+ mPriv->tube->setParameters(mPriv->parameters);
+ // The tube is ready: let's notify
+ setFinished();
+ } else {
+ if (state != TubeChannelStateRemotePending) {
+ warning() << "Offering tube failed with" << TP_QT_ERROR_CONNECTION_REFUSED;
+ // Something happened
+ setFinishedWithError(TP_QT_ERROR_CONNECTION_REFUSED,
+ QLatin1String("The connection to this tube was refused"));
+ } else {
+ debug() << "Awaiting remote to accept the tube";
+ }
+ }
+}
+
+QueuedContactFactory::QueuedContactFactory(Tp::ContactManagerPtr contactManager, QObject* parent)
+ : QObject(parent),
+ m_isProcessing(false),
+ m_manager(contactManager)
+{
+}
+
+QueuedContactFactory::~QueuedContactFactory()
+{
+}
+
+void QueuedContactFactory::processNextRequest()
+{
+ if (m_isProcessing || m_queue.isEmpty()) {
+ // Return, nothing to do
+ return;
+ }
+
+ m_isProcessing = true;
+
+ Entry entry = m_queue.dequeue();
+
+ // TODO: pass id hints to ContactManager if we ever gain support to retrieve contact ids
+ // from NewRemoteConnection.
+ PendingContacts *pc = m_manager->contactsForHandles(entry.handles);
+ pc->setProperty("__TpQt4__QueuedContactFactoryUuid", entry.uuid.toString());
+ connect(pc, SIGNAL(finished(Tp::PendingOperation*)),
+ this, SLOT(onPendingContactsFinished(Tp::PendingOperation*)));
+}
+
+QUuid QueuedContactFactory::appendNewRequest(const Tp::UIntList &handles)
+{
+ // Create a new entry
+ Entry entry;
+ entry.uuid = QUuid::createUuid();
+ entry.handles = handles;
+ m_queue.enqueue(entry);
+
+ // Check if we can process a request
+ processNextRequest();
+
+ // Return the UUID
+ return entry.uuid;
+}
+
+void QueuedContactFactory::onPendingContactsFinished(PendingOperation *op)
+{
+ PendingContacts *pc = qobject_cast<PendingContacts*>(op);
+
+ QUuid uuid = QUuid(pc->property("__TpQt4__QueuedContactFactoryUuid").toString());
+
+ emit contactsRetrieved(uuid, pc->contacts());
+
+ // No longer processing
+ m_isProcessing = false;
+
+ // Go for next one
+ processNextRequest();
+}
+
+OutgoingStreamTubeChannel::Private::Private(OutgoingStreamTubeChannel *parent)
+ : parent(parent),
+ queuedContactFactory(new QueuedContactFactory(parent->connection()->contactManager(), parent))
+{
+}
+
+/**
+ * \class OutgoingStreamTubeChannel
+ * \ingroup clientchannel
+ * \headerfile TelepathyQt/outgoing-stream-tube-channel.h <TelepathyQt/OutgoingStreamTubeChannel>
+ *
+ * \brief The OutgoingStreamTubeChannel class represents an outgoing Telepathy channel
+ * of type StreamTube.
+ *
+ * Outgoing (locally initiated/requested) tubes are initially in the #TubeChannelStateNotOffered state. The
+ * various offer methods in this class can be used to offer a local listening TCP or Unix socket for
+ * the tube's target to connect to, at which point the tube becomes #TubeChannelStateRemotePending.
+ * If the target accepts the connection request, the state goes #TubeChannelStateOpen and the
+ * connection manager will start tunneling any incoming connections from the recipient side to the
+ * local service.
+ */
+
+/**
+ * Feature representing the core that needs to become ready to make the
+ * OutgoingStreamTubeChannel object usable.
+ *
+ * This is currently the same as StreamTubeChannel::FeatureCore, but may change to include more.
+ *
+ * When calling isReady(), becomeReady(), this feature is implicitly added
+ * to the requested features.
+ */
+const Feature OutgoingStreamTubeChannel::FeatureCore =
+ Feature(QLatin1String(StreamTubeChannel::staticMetaObject.className()), 0); // ST::FeatureCore
+
+/**
+ * Create a new OutgoingStreamTubeChannel object.
+ *
+ * \param connection Connection owning this channel, and specifying the
+ * service.
+ * \param objectPath The channel object path.
+ * \param immutableProperties The channel immutable properties.
+ * \return A OutgoingStreamTubeChannelPtr object pointing to the newly created
+ * OutgoingStreamTubeChannel object.
+ */
+OutgoingStreamTubeChannelPtr OutgoingStreamTubeChannel::create(const ConnectionPtr &connection,
+ const QString &objectPath, const QVariantMap &immutableProperties)
+{
+ return OutgoingStreamTubeChannelPtr(new OutgoingStreamTubeChannel(connection, objectPath,
+ immutableProperties, OutgoingStreamTubeChannel::FeatureCore));
+}
+
+/**
+ * Construct a new OutgoingStreamTubeChannel object.
+ *
+ * \param connection Connection owning this channel, and specifying the
+ * service.
+ * \param objectPath The channel object path.
+ * \param immutableProperties The channel immutable properties.
+ * \param coreFeature The core feature of the channel type, if any. The corresponding introspectable should
+ * depend on OutgoingStreamTubeChannel::FeatureCore.
+ */
+OutgoingStreamTubeChannel::OutgoingStreamTubeChannel(const ConnectionPtr &connection,
+ const QString &objectPath,
+ const QVariantMap &immutableProperties,
+ const Feature &coreFeature)
+ : StreamTubeChannel(connection, objectPath,
+ immutableProperties, coreFeature),
+ mPriv(new Private(this))
+{
+ connect(mPriv->queuedContactFactory,
+ SIGNAL(contactsRetrieved(QUuid,QList<Tp::ContactPtr>)),
+ this,
+ SLOT(onContactsRetrieved(QUuid,QList<Tp::ContactPtr>)));
+}
+
+/**
+ * Class destructor.
+ */
+OutgoingStreamTubeChannel::~OutgoingStreamTubeChannel()
+{
+ delete mPriv;
+}
+
+/**
+ * Offer a TCP socket over this stream tube.
+ *
+ * This method offers a TCP socket over this tube. The socket's address is given as
+ * a QHostAddress and a numerical port in native byte order.
+ *
+ * If your application uses QTcpServer as the local TCP server implementation, you can use the
+ * offerTcpSocket(const QTcpServer *, const QVariantMap &) overload instead to more easily pass the
+ * server's listen address.
+ *
+ * It is guaranteed that when the PendingOperation returned by this method will be completed,
+ * the tube will be opened and ready to be used.
+ *
+ * Connection managers adhering to the \telepathy_spec should always support offering IPv4 TCP
+ * sockets. IPv6 sockets are only supported if supportsIPv6SocketsOnLocalhost() is \c true.
+ *
+ * Note that the library will try to use #SocketAccessControlPort access control whenever possible,
+ * as it allows to map connections to users based on their source addresses. If
+ * supportsIPv4SocketsWithSpecifiedAddress() or supportsIPv6SocketsWithSpecifiedAddress() for IPv4
+ * and IPv6 sockets respectively is \c false, this feature is not available, and the
+ * connectionsForSourceAddresses() map won't contain useful distinct keys.
+ *
+ * Arbitrary parameters can be associated with the offer to bootstrap legacy protocols; these will
+ * in particular be available as IncomingStreamTubeChannel::parameters() for a tube receiver
+ * implemented using TelepathyQt in the other end.
+ *
+ * This method requires OutgoingStreamTubeChannel::FeatureCore to be ready.
+ *
+ * \param address A valid IPv4 or IPv6 address pointing to an existing socket.
+ * \param port The port the socket is listening for connections to.
+ * \param parameters A dictionary of arbitrary parameters to send with the tube offer.
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the stream tube is ready to be used
+ * (hence in the #TubeStateOpen state).
+ */
+PendingOperation *OutgoingStreamTubeChannel::offerTcpSocket(
+ const QHostAddress &address,
+ quint16 port,
+ const QVariantMap &parameters)
+{
+ if (!isReady(OutgoingStreamTubeChannel::FeatureCore)) {
+ warning() << "OutgoingStreamTubeChannel::FeatureCore must be ready before "
+ "calling offerTube";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Channel not ready"),
+ OutgoingStreamTubeChannelPtr(this));
+ }
+
+ // The tube must be not offered
+ if (state() != TubeChannelStateNotOffered) {
+ warning() << "You can not expose more than a socket for each Stream Tube";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Channel busy"),
+ OutgoingStreamTubeChannelPtr(this));
+ }
+
+ SocketAccessControl accessControl = SocketAccessControlLocalhost;
+ // Check if port is supported
+
+ // In this specific overload, we're handling an IPv4/IPv6 socket
+ if (address.protocol() == QAbstractSocket::IPv4Protocol) {
+ // IPv4 case
+ SocketAccessControl accessControl;
+ // Do some heuristics to find out the best access control.We always prefer port for tracking
+ // connections and source addresses.
+ if (supportsIPv4SocketsWithSpecifiedAddress()) {
+ accessControl = SocketAccessControlPort;
+ } else if (supportsIPv4SocketsOnLocalhost()) {
+ accessControl = SocketAccessControlLocalhost;
+ } else {
+ // There are no combinations supported for this socket
+ warning() << "You requested an address type/access control combination "
+ "not supported by this channel";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("The requested address type/access control "
+ "combination is not supported"),
+ OutgoingStreamTubeChannelPtr(this));
+ }
+
+ setAddressType(SocketAddressTypeIPv4);
+ setAccessControl(accessControl);
+ setIpAddress(qMakePair<QHostAddress, quint16>(address, port));
+
+ SocketAddressIPv4 addr;
+ addr.address = address.toString();
+ addr.port = port;
+
+ PendingVoid *pv = new PendingVoid(
+ interface<Client::ChannelTypeStreamTubeInterface>()->Offer(
+ SocketAddressTypeIPv4,
+ QDBusVariant(QVariant::fromValue(addr)),
+ accessControl,
+ parameters),
+ OutgoingStreamTubeChannelPtr(this));
+ PendingOpenTube *op = new PendingOpenTube(pv, parameters,
+ OutgoingStreamTubeChannelPtr(this));
+ return op;
+ } else if (address.protocol() == QAbstractSocket::IPv6Protocol) {
+ // IPv6 case
+ // Do some heuristics to find out the best access control.We always prefer port for tracking
+ // connections and source addresses.
+ if (supportsIPv6SocketsWithSpecifiedAddress()) {
+ accessControl = SocketAccessControlPort;
+ } else if (supportsIPv6SocketsOnLocalhost()) {
+ accessControl = SocketAccessControlLocalhost;
+ } else {
+ // There are no combinations supported for this socket
+ warning() << "You requested an address type/access control combination "
+ "not supported by this channel";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("The requested address type/access control "
+ "combination is not supported"),
+ OutgoingStreamTubeChannelPtr(this));
+ }
+
+ setAddressType(SocketAddressTypeIPv6);
+ setAccessControl(accessControl);
+ setIpAddress(qMakePair<QHostAddress, quint16>(address, port));
+
+ SocketAddressIPv6 addr;
+ addr.address = address.toString();
+ addr.port = port;
+
+ PendingVoid *pv = new PendingVoid(
+ interface<Client::ChannelTypeStreamTubeInterface>()->Offer(
+ SocketAddressTypeIPv6,
+ QDBusVariant(QVariant::fromValue(addr)),
+ accessControl,
+ parameters),
+ OutgoingStreamTubeChannelPtr(this));
+ PendingOpenTube *op = new PendingOpenTube(pv, parameters,
+ OutgoingStreamTubeChannelPtr(this));
+ return op;
+ } else {
+ // We're handling an IPv4/IPv6 socket only
+ warning() << "offerTube can be called only with a QHostAddress representing "
+ "an IPv4 or IPv6 address";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT),
+ QLatin1String("Invalid host given"),
+ OutgoingStreamTubeChannelPtr(this));
+ }
+
+}
+
+/**
+ * Offer a TCP socket over this stream tube.
+ *
+ * Otherwise identical to offerTcpSocket(const QHostAddress &, quint16, const QVariantMap &), but
+ * allows passing the local service's address in an already listening QTcpServer.
+ *
+ * \param server A valid QTcpServer, which should be already listening for incoming connections.
+ * \param parameters A dictionary of arbitrary parameters to send with the tube offer.
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the stream tube is ready to be used
+ * (hence in the #TubeStateOpen state).
+ */
+PendingOperation *OutgoingStreamTubeChannel::offerTcpSocket(
+ const QTcpServer *server,
+ const QVariantMap &parameters)
+{
+ // In this overload, we're handling a superset of QHostAddress.
+ // Let's redirect the call to QHostAddress's overload
+ return offerTcpSocket(server->serverAddress(), server->serverPort(),
+ parameters);
+}
+
+/**
+ * Offer an Unix socket over this stream tube.
+ *
+ * This method offers an Unix socket over this stream tube. The socket address is given as a
+ * a QString, which should contain the path to the socket. Abstract Unix sockets are also supported,
+ * and are given as addresses prefixed with a \c NUL byte.
+ *
+ * If your application uses QLocalServer as the local Unix server implementation, you can use the
+ * offerUnixSocket(const QLocalServer *, const QVariantMap &, bool) overload instead to more easily
+ * pass the server's listen address.
+ *
+ * Note that only connection managers for which supportsUnixSocketsOnLocalhost() or
+ * supportsAbstractUnixSocketsOnLocalhost() is \c true support exporting Unix sockets.
+ *
+ * If supportsUnixSocketsWithCredentials() or supportsAbstractUnixSocketsWithCredentials(), as
+ * appropriate, returns \c true, the \c requireCredentials parameter can be set to \c true to make
+ * the connection manager pass an SCM_CREDS or SCM_CREDENTIALS message as supported by the platform
+ * when making a new connection. This enables preventing other local users from connecting to the
+ * service, but might not be possible to use with all protocols as the message is in-band in the
+ * data stream.
+ *
+ * Arbitrary parameters can be associated with the offer to bootstrap legacy protocols; these will
+ * in particular be available as IncomingStreamTubeChannel::parameters() for a tube receiver
+ * implemented using TelepathyQt in the other end.
+ *
+ * This method requires OutgoingStreamTubeChannel::FeatureCore to be ready.
+ *
+ * \param address A valid path to an existing Unix socket or abstract Unix socket.
+ * \param parameters A dictionary of arbitrary parameters to send with the tube offer.
+ * \param requireCredentials Whether the server requires a SCM_CREDS or SCM_CREDENTIALS message
+ * upon connection.
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the stream tube is ready to be used
+ * (hence in the #TubeStateOpen state).
+ */
+PendingOperation *OutgoingStreamTubeChannel::offerUnixSocket(
+ const QString &socketAddress,
+ const QVariantMap &parameters,
+ bool requireCredentials)
+{
+ SocketAccessControl accessControl = requireCredentials ?
+ SocketAccessControlCredentials :
+ SocketAccessControlLocalhost;
+
+ if (!isReady(OutgoingStreamTubeChannel::FeatureCore)) {
+ warning() << "OutgoingStreamTubeChannel::FeatureCore must be ready before "
+ "calling offerTube";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Channel not ready"),
+ OutgoingStreamTubeChannelPtr(this));
+ }
+
+ // The tube must be not offered
+ if (state() != TubeChannelStateNotOffered) {
+ warning() << "You can not expose more than a socket for each Stream Tube";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Channel busy"), OutgoingStreamTubeChannelPtr(this));
+ }
+
+ // In this specific overload, we're handling an Unix/AbstractUnix socket
+ if (socketAddress.startsWith(QLatin1Char('\0'))) {
+ // Abstract Unix socket case
+ // Check if the combination type/access control is supported
+ if ((accessControl == SocketAccessControlLocalhost &&
+ !supportsAbstractUnixSocketsOnLocalhost()) ||
+ (accessControl == SocketAccessControlCredentials &&
+ !supportsAbstractUnixSocketsWithCredentials()) ) {
+ warning() << "You requested an address type/access control combination "
+ "not supported by this channel";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("The requested address type/access control "
+ "combination is not supported"),
+ OutgoingStreamTubeChannelPtr(this));
+ }
+
+ setAddressType(SocketAddressTypeAbstractUnix);
+ setAccessControl(accessControl);
+ setLocalAddress(socketAddress);
+
+ PendingVoid *pv = new PendingVoid(
+ interface<Client::ChannelTypeStreamTubeInterface>()->Offer(
+ SocketAddressTypeAbstractUnix,
+ QDBusVariant(QVariant(socketAddress.toLatin1())),
+ accessControl,
+ parameters),
+ OutgoingStreamTubeChannelPtr(this));
+ PendingOpenTube *op = new PendingOpenTube(pv, parameters,
+ OutgoingStreamTubeChannelPtr(this));
+ return op;
+ } else {
+ // Unix socket case
+ // Check if the combination type/access control is supported
+ if ((accessControl == SocketAccessControlLocalhost &&
+ !supportsUnixSocketsOnLocalhost()) ||
+ (accessControl == SocketAccessControlCredentials &&
+ !supportsUnixSocketsWithCredentials()) ||
+ (accessControl != SocketAccessControlLocalhost &&
+ accessControl != SocketAccessControlCredentials) ) {
+ warning() << "You requested an address type/access control combination "
+ "not supported by this channel";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("The requested address type/access control "
+ "combination is not supported"),
+ OutgoingStreamTubeChannelPtr(this));
+ }
+
+ setAddressType(SocketAddressTypeUnix);
+ setAccessControl(accessControl);
+ setLocalAddress(socketAddress);
+
+ PendingVoid *pv = new PendingVoid(
+ interface<Client::ChannelTypeStreamTubeInterface>()->Offer(
+ SocketAddressTypeUnix,
+ QDBusVariant(QVariant(socketAddress.toLatin1())),
+ accessControl,
+ parameters),
+ OutgoingStreamTubeChannelPtr(this));
+ PendingOpenTube *op = new PendingOpenTube(pv, parameters,
+ OutgoingStreamTubeChannelPtr(this));
+ return op;
+ }
+}
+
+/**
+ * Offer an Unix socket over the tube.
+ *
+ * Otherwise identical to offerUnixSocket(const QString &, const QVariantMap &, bool), but allows
+ * passing the local service's address as an already listening QLocalServer.
+ *
+ * This method requires OutgoingStreamTubeChannel::FeatureCore to be ready.
+ *
+ * \param server A valid QLocalServer, which should be already listening for incoming connections.
+ * \param parameters A dictionary of arbitrary parameters to send with the tube offer.
+ * \param requireCredentials Whether the server should require a SCM_CRED or SCM_CREDENTIALS message
+ * upon connection.
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the stream tube is ready to be used
+ * (hence in the #TubeStateOpen state).
+ * \sa StreamTubeChannel::supportsUnixSocketsOnLocalhost(),
+ * StreamTubeChannel::supportsUnixSocketsWithCredentials(),
+ * StreamTubeChannel::supportsAbstractUnixSocketsOnLocalhost(),
+ * StreamTubeChannel::supportsAbstractUnixSocketsWithCredentials()
+ */
+PendingOperation *OutgoingStreamTubeChannel::offerUnixSocket(
+ const QLocalServer *server,
+ const QVariantMap &parameters,
+ bool requireCredentials)
+{
+ // In this overload, we're handling a superset of a local socket
+ // Let's redirect the call to QString's overload
+ return offerUnixSocket(server->fullServerName(), parameters, requireCredentials);
+}
+
+/**
+ * Return a map from a source address to the corresponding connections ids.
+ *
+ * The connection ids retrieved here can be used to map a source address
+ * which connected to your socket to a connection ID (for error reporting) and further, to a contact
+ * (by using contactsForConnections()).
+ *
+ * This method is only useful if a TCP socket was offered on this tube and the connection manager
+ * supports #SocketAccessControlPort, which can be discovered using
+ * supportsIPv4SocketsWithSpecifiedAddress() and supportsIPv6SocketsWithSpecifiedAddress() for IPv4
+ * and IPv6 sockets respectively.
+ *
+ * Note that this function will only return valid data after the tube has been opened.
+ *
+ * This method requires StreamTubeChannel::FeatureConnectionMonitoring to be ready.
+ *
+ * \return The map from source addresses as (QHostAddress, port in native byte order) pairs to the
+ * corresponding connection ids.
+ * \sa connectionsForCredentials()
+ */
+QHash<QPair<QHostAddress, quint16>, uint> OutgoingStreamTubeChannel::connectionsForSourceAddresses() const
+{
+ if (addressType() != SocketAddressTypeIPv4 && addressType() != SocketAddressTypeIPv6) {
+ warning() << "OutgoingStreamTubeChannel::connectionsForSourceAddresses() makes sense "
+ "just when offering a TCP socket";
+ return QHash<QPair<QHostAddress, quint16>, uint>();
+ }
+
+ if (isValid() || !isDroppingConnections() ||
+ !requestedFeatures().contains(StreamTubeChannel::FeatureConnectionMonitoring)) {
+ if (!isReady(StreamTubeChannel::FeatureConnectionMonitoring)) {
+ warning() << "StreamTubeChannel::FeatureConnectionMonitoring must be ready before "
+ " calling connectionsForSourceAddresses";
+ return QHash<QPair<QHostAddress, quint16>, uint>();
+ }
+
+ if (state() != TubeChannelStateOpen) {
+ warning() << "OutgoingStreamTubeChannel::connectionsForSourceAddresses() makes sense "
+ "just when the tube is open";
+ return QHash<QPair<QHostAddress, quint16>, uint>();
+ }
+ }
+
+ return mPriv->connectionsForSourceAddresses;
+}
+
+/**
+ * Return a map from a credential byte to the corresponding connections ids.
+ *
+ * The connection ids retrieved here can be used to map a source address
+ * which connected to your socket to a connection ID (for error reporting) and further, to a contact
+ * (by using contactsForConnections()).
+ *
+ * This method is only useful if this tube was offered using an Unix socket and passing credential
+ * bytes was enabled (\c requireCredentials == true).
+ *
+ * Note that this function will only return valid data after the tube has been opened.
+ *
+ * This method requires StreamTubeChannel::FeatureConnectionMonitoring to be ready.
+ *
+ * \return The map from credential bytes to the corresponding connection ids.
+ * \sa connectionsForSourceAddresses()
+ */
+QHash<uchar, uint> OutgoingStreamTubeChannel::connectionsForCredentials() const
+{
+ if (addressType() != SocketAddressTypeUnix && addressType() != SocketAddressTypeAbstractUnix) {
+ warning() << "OutgoingStreamTubeChannel::connectionsForCredentials() makes sense "
+ "just when offering an Unix socket";
+ return QHash<uchar, uint>();
+ }
+
+ if (accessControl() != SocketAccessControlCredentials) {
+ warning() << "OutgoingStreamTubeChannel::connectionsForCredentials() makes sense "
+ "just when offering an Unix socket requiring credentials";
+ return QHash<uchar, uint>();
+ }
+
+ if (isValid() || !isDroppingConnections() ||
+ !requestedFeatures().contains(StreamTubeChannel::FeatureConnectionMonitoring)) {
+ if (!isReady(StreamTubeChannel::FeatureConnectionMonitoring)) {
+ warning() << "StreamTubeChannel::FeatureConnectionMonitoring must be ready before "
+ "calling OutgoingStreamTubeChannel::connectionsForCredentials()";
+ return QHash<uchar, uint>();
+ }
+
+ if (state() != TubeChannelStateOpen) {
+ warning() << "OutgoingStreamTubeChannel::connectionsForCredentials() makes sense "
+ "just when the tube is opened";
+ return QHash<uchar, uint>();
+ }
+ }
+
+ return mPriv->connectionsForCredentials;
+}
+
+/**
+ * Return a map from connection ids to the associated contact.
+ *
+ * Note that this function will only return valid data after the tube has been opened.
+ *
+ * This method requires StreamTubeChannel::FeatureConnectionMonitoring to be ready.
+
+ * \return The map from connection ids to pointer to Contact objects.
+ * \sa connectionsForSourceAddresses(), connectionsForCredentials(),
+ * StreamTubeChannel::addressType()
+ */
+QHash<uint, ContactPtr> OutgoingStreamTubeChannel::contactsForConnections() const
+{
+ if (isValid() || !isDroppingConnections() ||
+ !requestedFeatures().contains(StreamTubeChannel::FeatureConnectionMonitoring)) {
+ if (!isReady(StreamTubeChannel::FeatureConnectionMonitoring)) {
+ warning() << "StreamTubeChannel::FeatureConnectionMonitoring must be ready before "
+ "calling contactsForConnections";
+ return QHash<uint, ContactPtr>();
+ }
+
+ if (state() != TubeChannelStateOpen) {
+ warning() << "OutgoingStreamTubeChannel::contactsForConnections() makes sense "
+ "just when the tube is open";
+ return QHash<uint, ContactPtr>();
+ }
+ }
+
+ return mPriv->contactsForConnections;
+}
+
+void OutgoingStreamTubeChannel::onNewRemoteConnection(
+ uint contactId,
+ const QDBusVariant &parameter,
+ uint connectionId)
+{
+ // Request the handles from our queued contact factory
+ QUuid uuid = mPriv->queuedContactFactory->appendNewRequest(UIntList() << contactId);
+
+ // Add a pending connection
+ mPriv->pendingNewConnections.insert(uuid, qMakePair(connectionId, parameter));
+}
+
+void OutgoingStreamTubeChannel::onContactsRetrieved(
+ const QUuid &uuid,
+ const QList<Tp::ContactPtr> &contacts)
+{
+ if (!isValid()) {
+ debug() << "Invalidated OutgoingStreamTubeChannel not emitting queued connection event";
+ return;
+ }
+
+ if (!mPriv->pendingNewConnections.contains(uuid)) {
+ if (mPriv->pendingClosedConnections.contains(uuid)) {
+ // closed connection
+ Private::ClosedConnection conn = mPriv->pendingClosedConnections.take(uuid);
+
+ // First, do removeConnection() so connectionClosed is emitted, and anybody connected to it
+ // (like StreamTubeServer) has a chance to recover the source address / contact
+ removeConnection(conn.id, conn.error, conn.message);
+
+ // Remove stuff from our hashes
+ mPriv->contactsForConnections.remove(conn.id);
+
+ QHash<QPair<QHostAddress, quint16>, uint>::iterator srcAddrIter =
+ mPriv->connectionsForSourceAddresses.begin();
+ while (srcAddrIter != mPriv->connectionsForSourceAddresses.end()) {
+ if (srcAddrIter.value() == conn.id) {
+ srcAddrIter = mPriv->connectionsForSourceAddresses.erase(srcAddrIter);
+ } else {
+ ++srcAddrIter;
+ }
+ }
+
+ QHash<uchar, uint>::iterator credIter = mPriv->connectionsForCredentials.begin();
+ while (credIter != mPriv->connectionsForCredentials.end()) {
+ if (credIter.value() == conn.id) {
+ credIter = mPriv->connectionsForCredentials.erase(credIter);
+ } else {
+ ++credIter;
+ }
+ }
+ } else {
+ warning() << "No pending connections found in OSTC" << objectPath() << "for contacts"
+ << contacts;
+ }
+
+ return;
+ }
+
+ // new connection
+ QPair<uint, QDBusVariant> connectionProperties = mPriv->pendingNewConnections.take(uuid);
+
+ // Add it to our connections hash
+ foreach (const Tp::ContactPtr &contact, contacts) {
+ mPriv->contactsForConnections.insert(connectionProperties.first, contact);
+ }
+
+ QPair<QHostAddress, quint16> address;
+ address.first = QHostAddress::Null;
+
+ // Now let's try to track the parameter
+ if (addressType() == SocketAddressTypeIPv4) {
+ // Try a qdbus_cast to our address struct: we're shielded from crashes
+ // thanks to our specification
+ SocketAddressIPv4 addr =
+ qdbus_cast<Tp::SocketAddressIPv4>(connectionProperties.second.variant());
+ address.first = QHostAddress(addr.address);
+ address.second = addr.port;
+ } else if (addressType() == SocketAddressTypeIPv6) {
+ SocketAddressIPv6 addr =
+ qdbus_cast<Tp::SocketAddressIPv6>(connectionProperties.second.variant());
+ address.first = QHostAddress(addr.address);
+ address.second = addr.port;
+ } else if (addressType() == SocketAddressTypeUnix ||
+ addressType() == SocketAddressTypeAbstractUnix) {
+ if (accessControl() == SocketAccessControlCredentials) {
+ uchar credentialByte = qdbus_cast<uchar>(connectionProperties.second.variant());
+ mPriv->connectionsForCredentials.insertMulti(credentialByte, connectionProperties.first);
+ }
+ }
+
+ if (address.first != QHostAddress::Null) {
+ // We can map it to a source address as well
+ mPriv->connectionsForSourceAddresses.insertMulti(address, connectionProperties.first);
+ }
+
+ // Time for us to emit the signal
+ addConnection(connectionProperties.first);
+}
+
+// This replaces the base class onConnectionClosed() slot, but unlike a virtual function, is ABI
+// compatible
+void OutgoingStreamTubeChannel::onConnectionClosed(uint connectionId,
+ const QString &errorName, const QString &errorMessage)
+{
+ // Insert a fake request to our queued contact factory to make the close events properly ordered
+ // with new connection events
+ QUuid uuid = mPriv->queuedContactFactory->appendNewRequest(UIntList());
+
+ // Add a pending connection close
+ mPriv->pendingClosedConnections.insert(uuid,
+ Private::ClosedConnection(connectionId, errorName, errorMessage));
+}
+
+}
diff --git a/TelepathyQt/outgoing-stream-tube-channel.h b/TelepathyQt/outgoing-stream-tube-channel.h
new file mode 100644
index 00000000..0a89a97b
--- /dev/null
+++ b/TelepathyQt/outgoing-stream-tube-channel.h
@@ -0,0 +1,89 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010-2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_outgoing_stream_tube_channel_h_HEADER_GUARD_
+#define _TelepathyQt_outgoing_stream_tube_channel_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/StreamTubeChannel>
+#include <TelepathyQt/PendingOperation>
+
+class QHostAddress;
+class QTcpServer;
+class QLocalServer;
+
+namespace Tp
+{
+
+class TP_QT_EXPORT OutgoingStreamTubeChannel : public StreamTubeChannel
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(OutgoingStreamTubeChannel)
+
+public:
+ static const Feature FeatureCore;
+
+ static OutgoingStreamTubeChannelPtr create(const ConnectionPtr &connection,
+ const QString &objectPath, const QVariantMap &immutableProperties);
+
+ virtual ~OutgoingStreamTubeChannel();
+
+ PendingOperation *offerTcpSocket(const QHostAddress &address, quint16 port,
+ const QVariantMap &parameters = QVariantMap());
+ PendingOperation *offerTcpSocket(const QTcpServer *server,
+ const QVariantMap &parameters = QVariantMap());
+
+ PendingOperation *offerUnixSocket(const QString &socketAddress,
+ const QVariantMap &parameters = QVariantMap(), bool requireCredentials = false);
+ PendingOperation *offerUnixSocket(const QLocalServer *server,
+ const QVariantMap &parameters = QVariantMap(), bool requireCredentials = false);
+
+ QHash<uint, Tp::ContactPtr> contactsForConnections() const;
+
+ QHash<QPair<QHostAddress,quint16>, uint> connectionsForSourceAddresses() const;
+ QHash<uchar, uint> connectionsForCredentials() const;
+
+protected:
+ OutgoingStreamTubeChannel(const ConnectionPtr &connection, const QString &objectPath,
+ const QVariantMap &immutableProperties,
+ const Feature &coreFeature = OutgoingStreamTubeChannel::FeatureCore);
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onNewRemoteConnection(uint contactId,
+ const QDBusVariant &parameter, uint connectionId);
+ TP_QT_NO_EXPORT void onContactsRetrieved(const QUuid &uuid,
+ const QList<Tp::ContactPtr> &contacts);
+ TP_QT_NO_EXPORT void onConnectionClosed(uint connectionId,
+ const QString &errorName, const QString &errorMessage);
+
+private:
+ struct Private;
+ friend struct PendingOpenTube;
+ friend struct Private;
+ Private *mPriv;
+};
+
+}
+
+#endif
diff --git a/TelepathyQt/pending-account.cpp b/TelepathyQt/pending-account.cpp
new file mode 100644
index 00000000..22f2d3d7
--- /dev/null
+++ b/TelepathyQt/pending-account.cpp
@@ -0,0 +1,184 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/PendingAccount>
+
+#include "TelepathyQt/_gen/pending-account.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/AccountManager>
+#include <TelepathyQt/PendingReady>
+
+#include <QDBusObjectPath>
+#include <QDBusPendingCallWatcher>
+#include <QDBusPendingReply>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT PendingAccount::Private
+{
+ AccountPtr account;
+};
+
+/**
+ * \class PendingAccount
+ * \ingroup clientaccount
+ * \headerfile TelepathyQt/pending-account.h <TelepathyQt/PendingAccount>
+ *
+ * \brief The PendingAccount class represents the parameters of and the reply to
+ * an asynchronous account request.
+ *
+ * Instances of this class cannot be constructed directly; the only way to get
+ * one is via AccountManager.
+ *
+ * See \ref async_model
+ */
+
+/**
+ * Construct a new PendingAccount object.
+ *
+ * \param manager AccountManager to use.
+ * \param connectionManager Name of the connection manager to create the account
+ * for.
+ * \param protocol Name of the protocol to create the account for.
+ * \param displayName Account display name.
+ * \param parameters Account parameters.
+ * \param properties An optional map from fully qualified D-Bus property
+ * names such as "org.freedesktop.Telepathy.Account.Enabled"
+ * to their values.
+ */
+PendingAccount::PendingAccount(const AccountManagerPtr &manager,
+ const QString &connectionManager, const QString &protocol,
+ const QString &displayName, const QVariantMap &parameters,
+ const QVariantMap &properties)
+ : PendingOperation(manager),
+ mPriv(new Private)
+{
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+ manager->baseInterface()->CreateAccount(connectionManager,
+ protocol, displayName, parameters, properties), this);
+ connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(onCallFinished(QDBusPendingCallWatcher*)));
+}
+
+/**
+ * Class destructor.
+ */
+PendingAccount::~PendingAccount()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the account manager through which the request was made.
+ *
+ * \return A pointer to the AccountManager object.
+ */
+AccountManagerPtr PendingAccount::manager() const
+{
+ return AccountManagerPtr(qobject_cast<AccountManager *>((AccountManager*) _object().data()));
+}
+
+/**
+ * Return the newly created account.
+ *
+ * \return A pointer to an Account object, or a null AccountPtr if an error occurred.
+ */
+AccountPtr PendingAccount::account() const
+{
+ if (!isFinished()) {
+ warning() << "PendingAccount::account called before finished, returning 0";
+ return AccountPtr();
+ } else if (!isValid()) {
+ warning() << "PendingAccount::account called when not valid, returning 0";
+ return AccountPtr();
+ }
+
+ return mPriv->account;
+}
+
+void PendingAccount::onCallFinished(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QDBusObjectPath> reply = *watcher;
+
+ if (!reply.isError()) {
+ QString objectPath = reply.value().path();
+
+ debug() << "Got reply to AccountManager.CreateAccount - object path:" << objectPath;
+
+ PendingReady *readyOp = manager()->accountFactory()->proxy(manager()->busName(),
+ objectPath, manager()->connectionFactory(),
+ manager()->channelFactory(), manager()->contactFactory());
+ mPriv->account = AccountPtr::qObjectCast(readyOp->proxy());
+ connect(readyOp,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onAccountBuilt(Tp::PendingOperation*)));
+ } else {
+ debug().nospace() <<
+ "CreateAccount failed: " <<
+ reply.error().name() << ": " << reply.error().message();
+ setFinishedWithError(reply.error());
+ }
+
+ watcher->deleteLater();
+}
+
+void PendingAccount::onAccountBuilt(Tp::PendingOperation *op)
+{
+ Q_ASSERT(op->isFinished());
+
+ if (op->isError()) {
+ warning() << "Making account ready using the factory failed:" <<
+ op->errorName() << op->errorMessage();
+ setFinishedWithError(op->errorName(), op->errorMessage());
+ } else {
+ // AM is stateless, so the only way for it to become invalid is in the introspection phase,
+ // and a PendingAccount should never be created if AM introspection hasn't succeeded
+ Q_ASSERT(!manager().isNull() && manager()->isValid());
+
+ if (manager()->allAccounts().contains(mPriv->account)) {
+ setFinished();
+ debug() << "New account" << mPriv->account->objectPath() << "built";
+ } else {
+ // Have to wait for the AM to pick up the change and signal it so the world can be
+ // assumed to be ~round when we finish
+ connect(manager().data(),
+ SIGNAL(newAccount(Tp::AccountPtr)),
+ SLOT(onNewAccount(Tp::AccountPtr)));
+ }
+ }
+}
+
+void PendingAccount::onNewAccount(const AccountPtr &account)
+{
+ if (account != mPriv->account) {
+ return;
+ }
+
+ debug() << "Account" << account->objectPath() << "added to AM, finishing PendingAccount";
+ setFinished();
+}
+
+} // Tp
diff --git a/TelepathyQt/pending-account.h b/TelepathyQt/pending-account.h
new file mode 100644
index 00000000..788d3464
--- /dev/null
+++ b/TelepathyQt/pending-account.h
@@ -0,0 +1,75 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_pending_account_h_HEADER_GUARD_
+#define _TelepathyQt_pending_account_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Account>
+#include <TelepathyQt/PendingOperation>
+
+#include <QString>
+#include <QVariantMap>
+
+class QDBusPendingCallWatcher;
+
+namespace Tp
+{
+
+class AccountManager;
+
+class TP_QT_EXPORT PendingAccount : public PendingOperation
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(PendingAccount);
+
+public:
+ ~PendingAccount();
+
+ AccountManagerPtr manager() const;
+
+ AccountPtr account() const;
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onCallFinished(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void onAccountBuilt(Tp::PendingOperation *readyOp);
+ TP_QT_NO_EXPORT void onNewAccount(const Tp::AccountPtr &account);
+
+private:
+ friend class AccountManager;
+
+ TP_QT_NO_EXPORT PendingAccount(const AccountManagerPtr &manager,
+ const QString &connectionManager, const QString &protocol,
+ const QString &displayName, const QVariantMap &parameters,
+ const QVariantMap &properties = QVariantMap());
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/pending-channel-request-internal.h b/TelepathyQt/pending-channel-request-internal.h
new file mode 100644
index 00000000..076e2b22
--- /dev/null
+++ b/TelepathyQt/pending-channel-request-internal.h
@@ -0,0 +1,73 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_pending_channel_request_internal_h_HEADER_GUARD_
+#define _TelepathyQt_pending_channel_request_internal_h_HEADER_GUARD_
+
+#include <TelepathyQt/ChannelRequest>
+#include <TelepathyQt/PendingOperation>
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+class TP_QT_NO_EXPORT PendingChannelRequestCancelOperation : public PendingOperation
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(PendingChannelRequestCancelOperation)
+
+public:
+ PendingChannelRequestCancelOperation()
+ : PendingOperation(SharedPtr<Object>())
+ {
+ }
+
+ ~PendingChannelRequestCancelOperation()
+ {
+ }
+
+ void go(const ChannelRequestPtr &channelRequest)
+ {
+ Q_ASSERT(mChannelRequest.isNull());
+ mChannelRequest = channelRequest;
+ connect(mChannelRequest->cancel(),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onCancelOperationFinished(Tp::PendingOperation*)));
+ }
+
+private Q_SLOTS:
+ void onCancelOperationFinished(Tp::PendingOperation *op)
+ {
+ if (op->isError()) {
+ setFinishedWithError(op->errorName(), op->errorMessage());
+ return;
+ }
+ setFinished();
+ }
+
+private:
+ ChannelRequestPtr mChannelRequest;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/pending-channel-request.cpp b/TelepathyQt/pending-channel-request.cpp
new file mode 100644
index 00000000..56c37ccb
--- /dev/null
+++ b/TelepathyQt/pending-channel-request.cpp
@@ -0,0 +1,276 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/PendingChannelRequest>
+#include "TelepathyQt/pending-channel-request-internal.h"
+
+#include "TelepathyQt/_gen/pending-channel-request.moc.hpp"
+#include "TelepathyQt/_gen/pending-channel-request-internal.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/Account>
+#include <TelepathyQt/AccountFactory>
+#include <TelepathyQt/ChannelDispatcher>
+#include <TelepathyQt/ChannelFactory>
+#include <TelepathyQt/ChannelRequest>
+#include <TelepathyQt/ChannelRequestHints>
+#include <TelepathyQt/ConnectionFactory>
+#include <TelepathyQt/ContactFactory>
+#include <TelepathyQt/PendingFailure>
+#include <TelepathyQt/PendingReady>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT PendingChannelRequest::Private
+{
+ Private(const QDBusConnection &dbusConnection)
+ : dbusConnection(dbusConnection),
+ cancelOperation(0)
+ {
+ }
+
+ QDBusConnection dbusConnection;
+ ChannelRequestPtr channelRequest;
+ PendingChannelRequestCancelOperation *cancelOperation;
+};
+
+/**
+ * \class PendingChannelRequest
+ * \ingroup clientchannelrequest
+ * \headerfile TelepathyQt/pending-channel-request.h <TelepathyQt/PendingChannelRequest>
+ *
+ * \brief The PendingChannelRequest class represents the parameters of and
+ * the reply to an asynchronous ChannelRequest request.
+ *
+ * Instances of this class cannot be constructed directly; the only way to get
+ * one is trough Account.
+ *
+ * See \ref async_model
+ */
+
+/**
+ * Construct a new PendingChannelRequest object.
+ *
+ * \param account Account to use.
+ * \param requestedProperties A dictionary containing the desirable properties.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \param preferredHandler Either the well-known bus name (starting with
+ * org.freedesktop.Telepathy.Client.) of the preferred
+ * handler for this channel, or an empty string to
+ * indicate that any handler would be acceptable.
+ * \param create Whether createChannel or ensureChannel should be called.
+ * \param account The account the request was made through.
+ */
+PendingChannelRequest::PendingChannelRequest(const AccountPtr &account,
+ const QVariantMap &requestedProperties, const QDateTime &userActionTime,
+ const QString &preferredHandler, bool create, const ChannelRequestHints &hints)
+ : PendingOperation(account),
+ mPriv(new Private(account->dbusConnection()))
+{
+ QString channelDispatcherObjectPath =
+ QString(QLatin1String("/%1")).arg(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_DISPATCHER));
+ channelDispatcherObjectPath.replace(QLatin1String("."), QLatin1String("/"));
+ Client::ChannelDispatcherInterface *channelDispatcherInterface =
+ account->dispatcherInterface();
+
+ QDBusPendingCallWatcher *watcher = 0;
+ if (create) {
+ if (hints.isValid()) {
+ if (account->supportsRequestHints()) {
+ watcher = new QDBusPendingCallWatcher(
+ channelDispatcherInterface->CreateChannelWithHints(
+ QDBusObjectPath(account->objectPath()),
+ requestedProperties,
+ userActionTime.isNull() ? 0 : userActionTime.toTime_t(),
+ preferredHandler, hints.allHints()), this);
+ } else {
+ warning() << "Hints passed to channel request won't have an effect"
+ << "because the Channel Dispatcher service in use is too old";
+ }
+ }
+
+ if (!watcher) {
+ watcher = new QDBusPendingCallWatcher(
+ channelDispatcherInterface->CreateChannel(
+ QDBusObjectPath(account->objectPath()),
+ requestedProperties,
+ userActionTime.isNull() ? 0 : userActionTime.toTime_t(),
+ preferredHandler), this);
+ }
+ } else {
+ if (hints.isValid()) {
+ if (account->supportsRequestHints()) {
+ watcher = new QDBusPendingCallWatcher(
+ channelDispatcherInterface->EnsureChannelWithHints(
+ QDBusObjectPath(account->objectPath()),
+ requestedProperties,
+ userActionTime.isNull() ? 0 : userActionTime.toTime_t(),
+ preferredHandler, hints.allHints()), this);
+ } else {
+ warning() << "Hints passed to channel request won't have an effect"
+ << "because the Channel Dispatcher service in use is too old";
+ }
+ }
+
+ if (!watcher) {
+ watcher = new QDBusPendingCallWatcher(
+ channelDispatcherInterface->EnsureChannel(
+ QDBusObjectPath(account->objectPath()),
+ requestedProperties,
+ userActionTime.isNull() ? 0 : userActionTime.toTime_t(),
+ preferredHandler), this);
+ }
+ }
+
+ // TODO: This is a Qt bug fixed upstream, should be in the next Qt release.
+ // We should not need to check watcher->isFinished() here, remove the
+ // check when we depend on the fixed Qt version.
+ if (watcher->isFinished()) {
+ onWatcherFinished(watcher);
+ } else {
+ connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(onWatcherFinished(QDBusPendingCallWatcher*)));
+ }
+}
+
+/**
+ * Construct a new PendingChannelRequest object that always fails.
+ *
+ * \param account Account to use.
+ * \param errorName The name of a D-Bus error.
+ * \param errorMessage The error message.
+ */
+PendingChannelRequest::PendingChannelRequest(const AccountPtr &account,
+ const QString &errorName, const QString &errorMessage)
+ : PendingOperation(ConnectionPtr()),
+ mPriv(new Private(account->dbusConnection()))
+{
+ setFinishedWithError(errorName, errorMessage);
+}
+
+/**
+ * Class destructor.
+ */
+PendingChannelRequest::~PendingChannelRequest()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the account through which the request was made.
+ *
+ * \return A pointer to the Account object.
+ */
+AccountPtr PendingChannelRequest::account() const
+{
+ return AccountPtr(qobject_cast<Account*>((Account*) _object().data()));
+}
+
+ChannelRequestPtr PendingChannelRequest::channelRequest() const
+{
+ return mPriv->channelRequest;
+}
+
+PendingOperation *PendingChannelRequest::cancel()
+{
+ if (isFinished()) {
+ // CR has already succeeded or failed, so let's just fail here
+ return new PendingFailure(TP_QT_DBUS_ERROR_UNKNOWN_METHOD,
+ QLatin1String("ChannnelRequest already finished"),
+ _object());
+ }
+
+ if (!mPriv->cancelOperation) {
+ mPriv->cancelOperation = new PendingChannelRequestCancelOperation();
+ connect(mPriv->cancelOperation,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onCancelOperationFinished(Tp::PendingOperation*)));
+
+ if (mPriv->channelRequest) {
+ mPriv->cancelOperation->go(mPriv->channelRequest);
+ }
+ }
+
+ return mPriv->cancelOperation;
+}
+
+void PendingChannelRequest::onWatcherFinished(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QDBusObjectPath> reply = *watcher;
+
+ if (!reply.isError()) {
+ QDBusObjectPath objectPath = reply.argumentAt<0>();
+ debug() << "Got reply to ChannelDispatcher.Ensure/CreateChannel "
+ "- object path:" << objectPath.path();
+
+ if (!account().isNull()) {
+ mPriv->channelRequest = ChannelRequest::create(account(),
+ objectPath.path(), QVariantMap());
+ }
+
+ if (mPriv->cancelOperation) {
+ mPriv->cancelOperation->go(mPriv->channelRequest);
+ } else {
+ emit channelRequestCreated(mPriv->channelRequest);
+
+ connect(mPriv->channelRequest.data(),
+ SIGNAL(failed(QString,QString)),
+ SLOT(setFinishedWithError(QString,QString)));
+ connect(mPriv->channelRequest.data(),
+ SIGNAL(succeeded(Tp::ChannelPtr)),
+ SLOT(setFinished()));
+
+ connect(mPriv->channelRequest->proceed(),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onProceedOperationFinished(Tp::PendingOperation*)));
+ }
+ } else {
+ debug().nospace() << "Ensure/CreateChannel failed:" <<
+ reply.error().name() << ": " << reply.error().message();
+ setFinishedWithError(reply.error());
+ }
+
+ watcher->deleteLater();
+}
+
+void PendingChannelRequest::onProceedOperationFinished(PendingOperation *op)
+{
+ if (op->isError()) {
+ setFinishedWithError(op->errorName(), op->errorMessage());
+ }
+}
+
+void PendingChannelRequest::onCancelOperationFinished(PendingOperation *op)
+{
+ mPriv->cancelOperation = 0;
+ if (!isFinished()) {
+ setFinishedWithError(QLatin1String(TELEPATHY_ERROR_CANCELLED),
+ QLatin1String("ChannelRequest cancelled"));
+ }
+}
+
+} // Tp
diff --git a/TelepathyQt/pending-channel-request.h b/TelepathyQt/pending-channel-request.h
new file mode 100644
index 00000000..1c62c21c
--- /dev/null
+++ b/TelepathyQt/pending-channel-request.h
@@ -0,0 +1,84 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_pending_channel_request_h_HEADER_GUARD_
+#define _TelepathyQt_pending_channel_request_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Global>
+#include <TelepathyQt/PendingOperation>
+#include <TelepathyQt/Types>
+
+#include <QDateTime>
+#include <QString>
+#include <QVariantMap>
+
+class QDBusPendingCallWatcher;
+
+namespace Tp
+{
+
+class Account;
+class ChannelRequestHints;
+
+class TP_QT_EXPORT PendingChannelRequest : public PendingOperation
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(PendingChannelRequest)
+
+public:
+ ~PendingChannelRequest();
+
+ AccountPtr account() const;
+
+ ChannelRequestPtr channelRequest() const;
+
+ PendingOperation *cancel();
+
+Q_SIGNALS:
+ void channelRequestCreated(const Tp::ChannelRequestPtr &channelRequest);
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onWatcherFinished(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void onProceedOperationFinished(Tp::PendingOperation *op);
+ TP_QT_NO_EXPORT void onCancelOperationFinished(Tp::PendingOperation *op);
+
+private:
+ friend class Account;
+
+ TP_QT_NO_EXPORT PendingChannelRequest(const AccountPtr &account,
+ const QVariantMap &requestedProperties, const QDateTime &userActionTime,
+ const QString &preferredHandler, bool create, const ChannelRequestHints &hints);
+ TP_QT_NO_EXPORT PendingChannelRequest(const AccountPtr &account,
+ const QString &errorName, const QString &errorMessage);
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/pending-channel.cpp b/TelepathyQt/pending-channel.cpp
new file mode 100644
index 00000000..3207700b
--- /dev/null
+++ b/TelepathyQt/pending-channel.cpp
@@ -0,0 +1,555 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/PendingChannel>
+
+#include "TelepathyQt/_gen/pending-channel.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+#include "TelepathyQt/fake-handler-manager-internal.h"
+#include "TelepathyQt/request-temporary-handler-internal.h"
+
+#include <TelepathyQt/Channel>
+#include <TelepathyQt/ChannelFactory>
+#include <TelepathyQt/ClientRegistrar>
+#include <TelepathyQt/Connection>
+#include <TelepathyQt/ConnectionLowlevel>
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/HandledChannelNotifier>
+#include <TelepathyQt/PendingChannelRequest>
+#include <TelepathyQt/PendingReady>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT PendingChannel::Private
+{
+ class FakeAccountFactory;
+
+ ConnectionPtr connection;
+ bool create;
+ bool yours;
+ QString channelType;
+ uint handleType;
+ uint handle;
+ QVariantMap immutableProperties;
+ ChannelPtr channel;
+
+ ClientRegistrarPtr cr;
+ SharedPtr<RequestTemporaryHandler> handler;
+ HandledChannelNotifier *notifier;
+ static uint numHandlers;
+};
+
+uint PendingChannel::Private::numHandlers = 0;
+
+class TP_QT_NO_EXPORT PendingChannel::Private::FakeAccountFactory : public AccountFactory
+{
+public:
+ static AccountFactoryPtr create(const AccountPtr &account)
+ {
+ return AccountFactoryPtr(new FakeAccountFactory(account));
+ }
+
+ ~FakeAccountFactory() { }
+
+ AccountPtr account() const { return mAccount; }
+
+protected:
+ AccountPtr construct(const QString &busName, const QString &objectPath,
+ const ConnectionFactoryConstPtr &connFactory,
+ const ChannelFactoryConstPtr &chanFactory,
+ const ContactFactoryConstPtr &contactFactory) const
+ {
+ if (mAccount->objectPath() != objectPath) {
+ warning() << "Account received by the fake factory is different from original account";
+ }
+ return mAccount;
+ }
+
+private:
+ FakeAccountFactory(const AccountPtr &account)
+ : AccountFactory(account->dbusConnection(), Features()),
+ mAccount(account)
+ {
+ }
+
+ AccountPtr mAccount;
+};
+
+/**
+ * \class PendingChannel
+ * \ingroup clientchannel
+ * \headerfile TelepathyQt/pending-channel.h <TelepathyQt/PendingChannel>
+ *
+ * \brief The PendingChannel class represents the parameters of and the reply to
+ * an asynchronous channel request.
+ *
+ * Instances of this class cannot be constructed directly; the only way to get
+ * one is trough Connection or Account.
+ *
+ * See \ref async_model
+ */
+
+/**
+ * Construct a new PendingChannel object that will fail immediately.
+ *
+ * \param connection Connection to use.
+ * \param errorName The error name.
+ * \param errorMessage The error message.
+ */
+PendingChannel::PendingChannel(const ConnectionPtr &connection, const QString &errorName,
+ const QString &errorMessage)
+ : PendingOperation(connection),
+ mPriv(new Private)
+{
+ mPriv->connection = connection;
+ mPriv->yours = false;
+ mPriv->handleType = 0;
+ mPriv->handle = 0;
+ mPriv->notifier = 0;
+ mPriv->create = false;
+
+ setFinishedWithError(errorName, errorMessage);
+}
+
+/**
+ * Construct a new PendingChannel object.
+ *
+ * \param connection Connection to use.
+ * \param request A dictionary containing the desirable properties.
+ * \param create Whether createChannel or ensureChannel should be called.
+ */
+PendingChannel::PendingChannel(const ConnectionPtr &connection,
+ const QVariantMap &request, bool create, int timeout)
+ : PendingOperation(connection),
+ mPriv(new Private)
+{
+ mPriv->connection = connection;
+ mPriv->yours = create;
+ mPriv->channelType = request.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType")).toString();
+ mPriv->handleType = request.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType")).toUInt();
+ mPriv->handle = request.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandle")).toUInt();
+ mPriv->notifier = 0;
+ mPriv->create = create;
+
+ Client::ConnectionInterfaceRequestsInterface *requestsInterface =
+ connection->interface<Client::ConnectionInterfaceRequestsInterface>();
+ if (create) {
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+ requestsInterface->CreateChannel(request, timeout), this);
+ connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(onConnectionCreateChannelFinished(QDBusPendingCallWatcher*)));
+ }
+ else {
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+ requestsInterface->EnsureChannel(request, timeout), this);
+ connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(onConnectionEnsureChannelFinished(QDBusPendingCallWatcher*)));
+ }
+}
+
+PendingChannel::PendingChannel(const AccountPtr &account,
+ const QVariantMap &request, const QDateTime &userActionTime,
+ bool create)
+ : PendingOperation(account),
+ mPriv(new Private)
+{
+ mPriv->yours = true;
+ mPriv->channelType = request.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType")).toString();
+ mPriv->handleType = request.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType")).toUInt();
+ mPriv->handle = request.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandle")).toUInt();
+
+ mPriv->cr = ClientRegistrar::create(
+ Private::FakeAccountFactory::create(account),
+ account->connectionFactory(),
+ account->channelFactory(),
+ account->contactFactory());
+ mPriv->handler = RequestTemporaryHandler::create(account);
+ mPriv->notifier = 0;
+ mPriv->create = create;
+
+ QString handlerName = QString(QLatin1String("TpQt4RaH_%1_%2"))
+ .arg(account->dbusConnection().baseService()
+ .replace(QLatin1String(":"), QLatin1String("_"))
+ .replace(QLatin1String("."), QLatin1String("_")))
+ .arg(Private::numHandlers++);
+ if (!mPriv->cr->registerClient(mPriv->handler, handlerName, false)) {
+ warning() << "Unable to register handler" << handlerName;
+ setFinishedWithError(TP_QT_ERROR_NOT_AVAILABLE,
+ QLatin1String("Unable to register handler"));
+ return;
+ }
+
+ connect(mPriv->handler.data(),
+ SIGNAL(error(QString,QString)),
+ SLOT(onHandlerError(QString,QString)));
+ connect(mPriv->handler.data(),
+ SIGNAL(channelReceived(Tp::ChannelPtr,QDateTime,Tp::ChannelRequestHints)),
+ SLOT(onHandlerChannelReceived(Tp::ChannelPtr)));
+
+ handlerName = QString(QLatin1String("org.freedesktop.Telepathy.Client.%1")).arg(handlerName);
+
+ debug() << "Requesting channel through account using handler" << handlerName;
+ PendingChannelRequest *pcr;
+ if (create) {
+ pcr = account->createChannel(request, userActionTime, handlerName, ChannelRequestHints());
+ } else {
+ pcr = account->ensureChannel(request, userActionTime, handlerName, ChannelRequestHints());
+ }
+ connect(pcr,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onAccountCreateChannelFinished(Tp::PendingOperation*)));
+}
+
+/**
+ * Construct a new PendingChannel object that will fail immediately.
+ *
+ * \param errorName The name of a D-Bus error.
+ * \param errorMessage The error message.
+ */
+PendingChannel::PendingChannel(const QString &errorName, const QString &errorMessage)
+ : PendingOperation(ConnectionPtr()), mPriv(new Private)
+{
+ setFinishedWithError(errorName, errorMessage);
+}
+
+/**
+ * Class destructor.
+ */
+PendingChannel::~PendingChannel()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the connection through which the channel request was made.
+ *
+ * Note that if this channel request was created through Account, a null ConnectionPtr will be
+ * returned.
+ *
+ * \return A pointer to the Connection object.
+ */
+ConnectionPtr PendingChannel::connection() const
+{
+ return mPriv->connection;
+}
+
+/**
+ * Return whether this channel belongs to this process.
+ *
+ * If \c false, the caller must assume that some other process is
+ * handling this channel; if \c true, the caller should handle it
+ * themselves or delegate it to another client.
+ *
+ * \return \c true if it belongs, \c false otherwise.
+ */
+bool PendingChannel::yours() const
+{
+ if (!isFinished()) {
+ warning() << "PendingChannel::yours called before finished, returning undefined value";
+ }
+ else if (!isValid()) {
+ warning() << "PendingChannel::yours called when not valid, returning undefined value";
+ }
+
+ return mPriv->yours;
+}
+
+/**
+ * Return the channel type specified in the channel request.
+ *
+ * \return The D-Bus interface name for the type of the channel.
+ */
+const QString &PendingChannel::channelType() const
+{
+ return mPriv->channelType;
+}
+
+/**
+ * If the channel request has finished, return the handle type of the resulting
+ * channel. Otherwise, return the handle type that was requested.
+ *
+ * (One example of a request producing a different target handle type is that
+ * on protocols like MSN, one-to-one conversations don't really exist, and if
+ * you request a text channel with handle type HandleTypeContact, what you
+ * will actually get is a text channel with handle type HandleTypeNone, with
+ * the requested contact as a member.)
+ *
+ * \return The target handle type as #HandleType.
+ * \sa targetHandle()
+ */
+uint PendingChannel::targetHandleType() const
+{
+ return mPriv->handleType;
+}
+
+/**
+ * If the channel request has finished, return the target handle of the
+ * resulting channel. Otherwise, return the target handle that was requested
+ * (which might be different in some situations - see targetHandleType()).
+ *
+ * \return An integer representing the target handle, which is of the type
+ * targetHandleType() indicates.
+ * \sa targetHandleType()
+ */
+uint PendingChannel::targetHandle() const
+{
+ return mPriv->handle;
+}
+
+/**
+ * If this channel request has finished, return the immutable properties of
+ * the resulting channel. Otherwise, return an empty map.
+ *
+ * The keys and values in this map are defined by the \telepathy_spec,
+ * or by third-party extensions to that specification.
+ * These are the properties that cannot change over the lifetime of the
+ * channel; they're announced in the result of the request, for efficiency.
+ * This map should be passed to the constructor of Channel or its subclasses
+ * (such as TextChannel).
+ *
+ * These properties can also be used to process channels in a way that does
+ * not require the creation of a Channel object - for instance, a
+ * ChannelDispatcher implementation should be able to classify and process
+ * channels based on their immutable properties, without needing to create
+ * Channel objects.
+ *
+ * \return The immutable properties as QVariantMap.
+ */
+QVariantMap PendingChannel::immutableProperties() const
+{
+ QVariantMap props = mPriv->immutableProperties;
+
+ // This is a reasonable guess - if it's Yours it's guaranteedly Requested by us, and if it's not
+ // it could be either Requested by somebody else but also an incoming channel just as well.
+ if (!props.contains(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".Requested"))) {
+ debug() << "CM didn't provide Requested in channel immutable props, guessing"
+ << mPriv->yours;
+ props[QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".Requested")] =
+ mPriv->yours;
+ }
+
+ // Also, the spec says that if the channel was Requested by the local user, InitiatorHandle must
+ // be the Connection's self handle
+ if (!props.contains(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".InitiatorHandle"))) {
+ if (qdbus_cast<bool>(props.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".Requested")))) {
+ if (connection() && connection()->isReady(Connection::FeatureCore)) {
+ debug() << "CM didn't provide InitiatorHandle in channel immutable props, but we "
+ "know it's the conn's self handle (and have it)";
+ props[QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".InitiatorHandle")] =
+ connection()->selfHandle();
+ }
+ }
+ }
+
+ return props;
+}
+
+/**
+ * Return the channel resulting from the channel request.
+ *
+ * \return A pointer to the Channel object.
+ */
+ChannelPtr PendingChannel::channel() const
+{
+ if (!isFinished()) {
+ warning() << "PendingChannel::channel called before finished, returning 0";
+ return ChannelPtr();
+ } else if (!isValid()) {
+ warning() << "PendingChannel::channel called when not valid, returning 0";
+ return ChannelPtr();
+ }
+
+ return mPriv->channel;
+}
+
+/**
+ * If this channel request has finished and was created through Account,
+ * return a HandledChannelNotifier object that will keep track of channel() being re-requested.
+ *
+ * \return A HandledChannelNotifier instance, or 0 if an error occurred.
+ */
+HandledChannelNotifier *PendingChannel::handledChannelNotifier() const
+{
+ if (!isFinished()) {
+ warning() << "PendingChannel::handledChannelNotifier called before finished, returning 0";
+ return 0;
+ } else if (!isValid()) {
+ warning() << "PendingChannel::handledChannelNotifier called when not valid, returning 0";
+ return 0;
+ }
+
+ if (mPriv->cr && !mPriv->notifier) {
+ mPriv->notifier = new HandledChannelNotifier(mPriv->cr, mPriv->handler);
+ }
+ return mPriv->notifier;
+}
+
+void PendingChannel::onConnectionCreateChannelFinished(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QDBusObjectPath, QVariantMap> reply = *watcher;
+
+ if (!reply.isError()) {
+ QString objectPath = reply.argumentAt<0>().path();
+ QVariantMap map = reply.argumentAt<1>();
+
+ debug() << "Got reply to Connection.CreateChannel - object path:" << objectPath;
+
+ PendingReady *channelReady =
+ connection()->channelFactory()->proxy(connection(), objectPath, map);
+ mPriv->channel = ChannelPtr::qObjectCast(channelReady->proxy());
+
+ mPriv->immutableProperties = map;
+ mPriv->channelType = map.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType")).toString();
+ mPriv->handleType = map.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType")).toUInt();
+ mPriv->handle = map.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandle")).toUInt();
+
+ connect(channelReady,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onChannelReady(Tp::PendingOperation*)));
+ } else {
+ debug().nospace() << "CreateChannel failed:" <<
+ reply.error().name() << ": " << reply.error().message();
+ setFinishedWithError(reply.error());
+ }
+
+ watcher->deleteLater();
+}
+
+void PendingChannel::onConnectionEnsureChannelFinished(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<bool, QDBusObjectPath, QVariantMap> reply = *watcher;
+
+ if (!reply.isError()) {
+ mPriv->yours = reply.argumentAt<0>();
+ QString objectPath = reply.argumentAt<1>().path();
+ QVariantMap map = reply.argumentAt<2>();
+
+ debug() << "Got reply to Connection.EnsureChannel - object path:" << objectPath;
+
+ PendingReady *channelReady =
+ connection()->channelFactory()->proxy(connection(), objectPath, map);
+ mPriv->channel = ChannelPtr::qObjectCast(channelReady->proxy());
+
+ mPriv->immutableProperties = map;
+ mPriv->channelType = map.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType")).toString();
+ mPriv->handleType = map.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType")).toUInt();
+ mPriv->handle = map.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandle")).toUInt();
+
+ connect(channelReady,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onChannelReady(Tp::PendingOperation*)));
+ } else {
+ debug().nospace() << "EnsureChannel failed:" <<
+ reply.error().name() << ": " << reply.error().message();
+ setFinishedWithError(reply.error());
+ }
+
+ watcher->deleteLater();
+}
+
+void PendingChannel::onChannelReady(PendingOperation *op)
+{
+ if (!op->isError()) {
+ setFinished();
+ } else {
+ debug() << "Making the channel ready for" << this << "failed with" << op->errorName()
+ << ":" << op->errorMessage();
+ setFinishedWithError(op->errorName(), op->errorMessage());
+ }
+}
+
+void PendingChannel::onHandlerError(const QString &errorName, const QString &errorMessage)
+{
+ if (isFinished()) {
+ return;
+ }
+
+ warning() << "Creating/ensuring channel failed with" << errorName
+ << ":" << errorMessage;
+ setFinishedWithError(errorName, errorMessage);
+}
+
+void PendingChannel::onHandlerChannelReceived(const ChannelPtr &channel)
+{
+ if (isFinished()) {
+ warning() << "Handler received the channel but this operation already finished due "
+ "to failure in the channel request";
+ return;
+ }
+
+ mPriv->handleType = channel->targetHandleType();
+ mPriv->handle = channel->targetHandle();
+ mPriv->immutableProperties = channel->immutableProperties();
+ mPriv->channel = channel;
+
+ // register the CR in FakeHandlerManager so that at least one handler per bus stays alive
+ // until all channels requested using R&H gets invalidated/destroyed.
+ // This is important in case Mission Control happens to restart while
+ // the channel is still in use, since it will close each channel it
+ // doesn't find a handler for it.
+ FakeHandlerManager::instance()->registerClientRegistrar(mPriv->cr);
+
+ setFinished();
+}
+
+void PendingChannel::onAccountCreateChannelFinished(PendingOperation *op)
+{
+ if (isFinished()) {
+ if (isError()) {
+ warning() << "Creating/ensuring channel finished with a failure after the internal "
+ "handler already got a channel, ignoring";
+ }
+ return;
+ }
+
+ if (op->isError()) {
+ warning() << "Creating/ensuring channel failed with" << op->errorName()
+ << ":" << op->errorMessage();
+ setFinishedWithError(op->errorName(), op->errorMessage());
+ return;
+ }
+
+ if (!mPriv->handler->isDBusHandlerInvoked()) {
+ // Our handler hasn't be called but the channel request is complete.
+ // That means another handler handled the channels so we don't own it.
+ if (mPriv->create) {
+ warning() << "Creating/ensuring channel failed with" << TP_QT_ERROR_SERVICE_CONFUSED
+ << ":" << QLatin1String("CD.CreateChannel/WithHints returned successfully and "
+ "the handler didn't receive the channel yet");
+ setFinishedWithError(TP_QT_ERROR_SERVICE_CONFUSED,
+ QLatin1String("CD.CreateChannel/WithHints returned successfully and "
+ "the handler didn't receive the channel yet"));
+ } else {
+ warning() << "Creating/ensuring channel failed with" << TP_QT_ERROR_NOT_YOURS
+ << ":" << QLatin1String("Another handler is handling this channel");
+ setFinishedWithError(TP_QT_ERROR_NOT_YOURS,
+ QLatin1String("Another handler is handling this channel"));
+ }
+ return;
+ }
+}
+
+} // Tp
diff --git a/TelepathyQt/pending-channel.h b/TelepathyQt/pending-channel.h
new file mode 100644
index 00000000..2480e431
--- /dev/null
+++ b/TelepathyQt/pending-channel.h
@@ -0,0 +1,103 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_pending_channel_h_HEADER_GUARD_
+#define _TelepathyQt_pending_channel_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Channel>
+#include <TelepathyQt/PendingOperation>
+
+#include <QString>
+#include <QVariantMap>
+
+#include <QDBusPendingCallWatcher>
+
+namespace Tp
+{
+
+class Connection;
+class HandledChannelNotifier;
+
+class TP_QT_EXPORT PendingChannel : public PendingOperation
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(PendingChannel)
+
+public:
+ ~PendingChannel();
+
+ ConnectionPtr connection() const;
+
+ bool yours() const;
+
+ const QString &channelType() const;
+
+ uint targetHandleType() const;
+
+ uint targetHandle() const;
+
+ QVariantMap immutableProperties() const;
+
+ ChannelPtr channel() const;
+
+ HandledChannelNotifier *handledChannelNotifier() const;
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onConnectionCreateChannelFinished(
+ QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void onConnectionEnsureChannelFinished(
+ QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void onChannelReady(Tp::PendingOperation *op);
+
+ TP_QT_NO_EXPORT void onHandlerError(const QString &errorName,
+ const QString &errorMessage);
+ TP_QT_NO_EXPORT void onHandlerChannelReceived(
+ const Tp::ChannelPtr &channel);
+ TP_QT_NO_EXPORT void onAccountCreateChannelFinished(
+ Tp::PendingOperation *op);
+
+private:
+ friend class Account;
+ friend class ConnectionLowlevel;
+
+ TP_QT_NO_EXPORT PendingChannel(const ConnectionPtr &connection,
+ const QString &errorName, const QString &errorMessage);
+ TP_QT_NO_EXPORT PendingChannel(const ConnectionPtr &connection,
+ const QVariantMap &request, bool create, int timeout = -1);
+ TP_QT_NO_EXPORT PendingChannel(const AccountPtr &account,
+ const QVariantMap &request, const QDateTime &userActionTime,
+ bool create);
+ TP_QT_NO_EXPORT PendingChannel(const QString &errorName,
+ const QString &errorMessage);
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/pending-connection.cpp b/TelepathyQt/pending-connection.cpp
new file mode 100644
index 00000000..e1e16728
--- /dev/null
+++ b/TelepathyQt/pending-connection.cpp
@@ -0,0 +1,171 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/PendingConnection>
+
+#include "TelepathyQt/_gen/pending-connection.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/ChannelFactory>
+#include <TelepathyQt/ConnectionManager>
+#include <TelepathyQt/Connection>
+#include <TelepathyQt/ContactFactory>
+#include <TelepathyQt/PendingReady>
+
+#include <QDBusObjectPath>
+#include <QDBusPendingCallWatcher>
+#include <QDBusPendingReply>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT PendingConnection::Private
+{
+ ConnectionPtr connection;
+};
+
+/**
+ * \class PendingConnection
+ * \ingroup clientconn
+ * \headerfile TelepathyQt/pending-connection.h <TelepathyQt/PendingConnection>
+ *
+ * \brief The PendingConnection class represents the parameters of and the reply
+ * to an asynchronous connection request.
+ *
+ * Instances of this class cannot be constructed directly; the only way to get
+ * one is via ConnectionManager.
+ *
+ * See \ref async_model
+ */
+
+/**
+ * Construct a new PendingConnection object.
+ *
+ * \param manager ConnectionManager to use.
+ * \param protocol Name of the protocol to create the connection for.
+ * \param parameters Connection parameters.
+ */
+PendingConnection::PendingConnection(const ConnectionManagerPtr &manager,
+ const QString &protocol, const QVariantMap &parameters)
+ : PendingOperation(manager),
+ mPriv(new Private)
+{
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+ manager->baseInterface()->RequestConnection(protocol,
+ parameters), this);
+ connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(onCallFinished(QDBusPendingCallWatcher*)));
+}
+
+/**
+ * Construct a new PendingConnection object that will fail immediately.
+ *
+ * \param error Name of the error to fail with.
+ * \param errorMessage Detail message for the error.
+ */
+PendingConnection::PendingConnection(const QString &error, const QString &errorMessage)
+ : PendingOperation(ConnectionManagerPtr()),
+ mPriv(new Private)
+{
+ setFinishedWithError(error, errorMessage);
+}
+
+/**
+ * Class destructor.
+ */
+PendingConnection::~PendingConnection()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the connection manager through which the request was made.
+ *
+ * \return A pointer to the ConnectionManager object.
+ */
+ConnectionManagerPtr PendingConnection::manager() const
+{
+ return ConnectionManagerPtr(qobject_cast<ConnectionManager*>((ConnectionManager*) _object().data()));
+}
+
+/**
+ * Return the connection resulting from the connection request.
+ *
+ * \return A pointer to the Connection object.
+ */
+ConnectionPtr PendingConnection::connection() const
+{
+ if (!isFinished()) {
+ warning() << "PendingConnection::connection called before finished, returning 0";
+ return ConnectionPtr();
+ } else if (!isValid()) {
+ warning() << "PendingConnection::connection called when not valid, returning 0";
+ return ConnectionPtr();
+ }
+
+ return mPriv->connection;
+}
+
+void PendingConnection::onCallFinished(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QString, QDBusObjectPath> reply = *watcher;
+
+ if (!reply.isError()) {
+ QString busName = reply.argumentAt<0>();
+ QString objectPath = reply.argumentAt<1>().path();
+
+ debug() << "Got reply to ConnectionManager.CreateConnection - bus name:" <<
+ busName << "- object path:" << objectPath;
+
+ PendingReady *readyOp = manager()->connectionFactory()->proxy(busName,
+ objectPath, manager()->channelFactory(), manager()->contactFactory());
+ mPriv->connection = ConnectionPtr::qObjectCast(readyOp->proxy());
+ connect(readyOp,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onConnectionBuilt(Tp::PendingOperation*)));
+ } else {
+ debug().nospace() <<
+ "CreateConnection failed: " <<
+ reply.error().name() << ": " << reply.error().message();
+ setFinishedWithError(reply.error());
+ }
+
+ watcher->deleteLater();
+}
+
+void PendingConnection::onConnectionBuilt(Tp::PendingOperation *op)
+{
+ Q_ASSERT(op->isFinished());
+
+ if (op->isError()) {
+ warning() << "Making connection ready using the factory failed:" <<
+ op->errorName() << op->errorMessage();
+ setFinishedWithError(op->errorName(), op->errorMessage());
+ } else {
+ setFinished();
+ debug() << "New connection" << mPriv->connection->objectPath() << "built";
+ }
+}
+
+} // Tp
diff --git a/TelepathyQt/pending-connection.h b/TelepathyQt/pending-connection.h
new file mode 100644
index 00000000..1f9c6c09
--- /dev/null
+++ b/TelepathyQt/pending-connection.h
@@ -0,0 +1,73 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_pending_connection_h_HEADER_GUARD_
+#define _TelepathyQt_pending_connection_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Connection>
+#include <TelepathyQt/PendingOperation>
+
+#include <QString>
+#include <QVariantMap>
+
+class QDBusPendingCallWatcher;
+
+namespace Tp
+{
+
+class ConnectionManager;
+
+class TP_QT_EXPORT PendingConnection : public PendingOperation
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(PendingConnection);
+
+public:
+ ~PendingConnection();
+
+ ConnectionManagerPtr manager() const;
+
+ ConnectionPtr connection() const;
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onCallFinished(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void onConnectionBuilt(Tp::PendingOperation *op);
+
+private:
+ friend class ConnectionManagerLowlevel;
+
+ TP_QT_NO_EXPORT PendingConnection(const ConnectionManagerPtr &manager,
+ const QString &protocol, const QVariantMap &parameters);
+ TP_QT_NO_EXPORT PendingConnection(const QString &error, const QString &errorMessage);
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/pending-contact-attributes.cpp b/TelepathyQt/pending-contact-attributes.cpp
new file mode 100644
index 00000000..2b9cbd17
--- /dev/null
+++ b/TelepathyQt/pending-contact-attributes.cpp
@@ -0,0 +1,219 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/PendingContactAttributes>
+
+#include "TelepathyQt/_gen/pending-contact-attributes.moc.hpp"
+
+#include <TelepathyQt/Connection>
+#include <TelepathyQt/ReferencedHandles>
+
+#include "TelepathyQt/debug-internal.h"
+
+namespace Tp
+{
+
+/**
+ * \class PendingContactAttributes
+ * \ingroup clientconn
+ * \headerfile TelepathyQt/pending-contact-attributes.h <TelepathyQt/PendingContactAttributes>
+ *
+ * \brief The PendingContactAttributes class represents the parameters of and
+ * the reply to an asynchronous request for raw contact attributes, as used in
+ * the ConnectionLowlevel::contactAttributes() low-level convenience method wrapping the
+ * Client::ConnectionInterfaceContactsInterface::GetContactAttributes() D-Bus
+ * method.
+ *
+ * See \ref async_model
+ */
+
+struct TP_QT_NO_EXPORT PendingContactAttributes::Private
+{
+ UIntList contactsRequested;
+ QStringList interfacesRequested;
+ bool shouldReference;
+ ReferencedHandles validHandles;
+ UIntList invalidHandles;
+ ContactAttributesMap attributes;
+};
+
+PendingContactAttributes::PendingContactAttributes(const ConnectionPtr &connection,
+ const UIntList &handles, const QStringList &interfaces, bool reference)
+ : PendingOperation(connection),
+ mPriv(new Private)
+{
+ mPriv->contactsRequested = handles;
+ mPriv->interfacesRequested = interfaces;
+ mPriv->shouldReference = reference;
+}
+
+/**
+ * Class destructor.
+ */
+PendingContactAttributes::~PendingContactAttributes()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the connection through which the request was made.
+ *
+ * \return A pointer to the Connection object.
+ */
+ConnectionPtr PendingContactAttributes::connection() const
+{
+ return ConnectionPtr(qobject_cast<Connection*>((Connection*) _object().data()));
+}
+
+/**
+ * Return the contacts for which attributes were requested.
+ *
+ * \return Reference to a list with the handles of the contacts.
+ */
+const UIntList &PendingContactAttributes::contactsRequested() const
+{
+ return mPriv->contactsRequested;
+}
+
+/**
+ * Return the interfaces the corresponding attributes of which were requested.
+ *
+ * \return Reference to a list of D-Bus interface names.
+ */
+const QStringList &PendingContactAttributes::interfacesRequested() const
+{
+ return mPriv->interfacesRequested;
+}
+
+/**
+ * Return whether it was requested that the contact handles should be referenced in addition to
+ * fetching their attributes. This corresponds to the <code>reference</code> argument to
+ * Connection::contactAttributes().
+ *
+ * \return Whether the handles should be referenced or not.
+ */
+bool PendingContactAttributes::shouldReference() const
+{
+ return mPriv->shouldReference;
+}
+
+/**
+ * If referencing the handles was requested (as indicated by shouldReference()), returns the
+ * now-referenced handles resulting from the operation. If the operation has not (yet) finished
+ * successfully (isFinished() returns <code>false</code>), or referencing was not requested, the
+ * return value is undefined.
+ *
+ * Even if referencing was requested, the list will not always contain all of the handles in
+ * contactsRequested(), only the ones which were valid. The valid handles will be in the same order
+ * as in contactsRequested(), though.
+ *
+ * \return ReferencedHandles instance containing the handles.
+ */
+ReferencedHandles PendingContactAttributes::validHandles() const
+{
+ if (!isFinished()) {
+ warning() << "PendingContactAttributes::validHandles() called before finished";
+ } else if (isError()) {
+ warning() << "PendingContactAttributes::validHandles() called when errored";
+ } else if (!shouldReference()) {
+ warning() << "PendingContactAttributes::validHandles() called but weren't asked to"
+ << "reference handles";
+ }
+
+ return mPriv->validHandles;
+}
+
+/**
+ * Return the handles which were found to be invalid while processing the operation. If the
+ * operation has not (yet) finished successfully (isFinished() returns <code>false</code>), the
+ * return value is undefined.
+ *
+ * \return A list with the invalid handles.
+ */
+UIntList PendingContactAttributes::invalidHandles() const
+{
+ if (!isFinished()) {
+ warning() << "PendingContactAttributes::validHandles() called before finished";
+ } else if (isError()) {
+ warning() << "PendingContactAttributes::validHandles() called when errored";
+ }
+
+ return mPriv->invalidHandles;
+}
+
+/**
+ * Return a dictionary mapping the valid contact handles in contactsRequested() (when also
+ * referencing, this means the contents of validHandles()) to contact attributes. If the operation
+ * has not (yet) finished successfully (isFinished() returns <code>false</code>), the return value
+ * is undefined.
+ *
+ * \return Mapping from handles to variant maps containing the attributes.
+ */
+ContactAttributesMap PendingContactAttributes::attributes() const
+{
+ if (!isFinished()) {
+ warning() << "PendingContactAttributes::validHandles() called before finished";
+ } else if (isError()) {
+ warning() << "PendingContactAttributes::validHandles() called when errored";
+ }
+
+ return mPriv->attributes;
+}
+
+void PendingContactAttributes::onCallFinished(QDBusPendingCallWatcher* watcher)
+{
+ QDBusPendingReply<ContactAttributesMap> reply = *watcher;
+
+ if (reply.isError()) {
+ debug().nospace() << "GetCAs: error " << reply.error().name() << ": " << reply.error().message();
+ setFinishedWithError(reply.error());
+ } else {
+ mPriv->attributes = reply.value();
+
+ UIntList validHandles;
+ foreach (uint contact, mPriv->contactsRequested) {
+ if (mPriv->attributes.contains(contact)) {
+ validHandles << contact;
+ } else {
+ mPriv->invalidHandles << contact;
+ }
+ }
+
+ if (shouldReference()) {
+ mPriv->validHandles = ReferencedHandles(connection(), HandleTypeContact,
+ validHandles);
+ }
+
+ setFinished();
+ }
+
+ connection()->handleRequestLanded(HandleTypeContact);
+
+ watcher->deleteLater();
+}
+
+void PendingContactAttributes::failImmediately(const QString &error, const QString &errorMessage)
+{
+ setFinishedWithError(error, errorMessage);
+}
+
+} // Tp
diff --git a/TelepathyQt/pending-contact-attributes.h b/TelepathyQt/pending-contact-attributes.h
new file mode 100644
index 00000000..2f1fa14a
--- /dev/null
+++ b/TelepathyQt/pending-contact-attributes.h
@@ -0,0 +1,77 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_pending_contact_attributes_h_HEADER_GUARD_
+#define _TelepathyQt_pending_contact_attributes_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/PendingOperation>
+#include <TelepathyQt/Types>
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+class ReferencedHandles;
+
+class TP_QT_EXPORT PendingContactAttributes : public PendingOperation
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(PendingContactAttributes)
+
+public:
+ ~PendingContactAttributes();
+
+ ConnectionPtr connection() const;
+
+ const UIntList &contactsRequested() const;
+ const QStringList &interfacesRequested() const;
+ bool shouldReference() const;
+
+ ReferencedHandles validHandles() const;
+ UIntList invalidHandles() const;
+ ContactAttributesMap attributes() const;
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onCallFinished(QDBusPendingCallWatcher *watcher);
+
+private:
+ friend class ConnectionLowlevel;
+
+ TP_QT_NO_EXPORT PendingContactAttributes(const ConnectionPtr &connection,
+ const UIntList &handles,
+ const QStringList &interfaces, bool reference);
+
+ TP_QT_NO_EXPORT void failImmediately(const QString &error, const QString &errorMessage);
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/pending-contact-info.cpp b/TelepathyQt/pending-contact-info.cpp
new file mode 100644
index 00000000..9a4c08d8
--- /dev/null
+++ b/TelepathyQt/pending-contact-info.cpp
@@ -0,0 +1,128 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/PendingContactInfo>
+
+#include "TelepathyQt/_gen/pending-contact-info.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/Connection>
+#include <TelepathyQt/ContactManager>
+
+#include <QDBusPendingReply>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT PendingContactInfo::Private
+{
+ Contact::InfoFields info;
+};
+
+/**
+ * \class PendingContactInfo
+ * \ingroup clientconn
+ * \headerfile TelepathyQt/pending-contact-info.h <TelepathyQt/PendingContactInfo>
+ *
+ * \brief The PendingContactInfo class represents the parameters of and the
+ * reply to an asynchronous contact info request.
+ *
+ * Instances of this class cannot be constructed directly; the only way to get
+ * one is via Contact.
+ *
+ * See \ref async_model
+ */
+
+/**
+ * Construct a new PendingContactInfo object.
+ *
+ * \param contact Contact to use.
+ */
+PendingContactInfo::PendingContactInfo(const ContactPtr &contact)
+ : PendingOperation(contact),
+ mPriv(new Private)
+{
+ ConnectionPtr connection = contact->manager()->connection();
+ Client::ConnectionInterfaceContactInfoInterface *contactInfoInterface =
+ connection->interface<Client::ConnectionInterfaceContactInfoInterface>();
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+ contactInfoInterface->RequestContactInfo(
+ contact->handle()[0]), this);
+ connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(onCallFinished(QDBusPendingCallWatcher*)));
+}
+
+/**
+ * Class destructor.
+ */
+PendingContactInfo::~PendingContactInfo()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the contact through which the request was made.
+ *
+ * \return A pointer to the Contact object.
+ */
+ContactPtr PendingContactInfo::contact() const
+{
+ return ContactPtr(qobject_cast<Contact*>((Contact*) _object().data()));
+}
+
+/**
+ * Return the information for contact().
+ *
+ * \return The contact infor as a Contact::InfoFields object.
+ */
+Contact::InfoFields PendingContactInfo::infoFields() const
+{
+ if (!isFinished()) {
+ warning() << "PendingContactInfo::info called before finished";
+ } else if (!isValid()) {
+ warning() << "PendingContactInfo::info called when not valid";
+ }
+
+ return mPriv->info;
+}
+
+void PendingContactInfo::onCallFinished(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<Tp::ContactInfoFieldList> reply = *watcher;
+
+ if (!reply.isError()) {
+ mPriv->info = Contact::InfoFields(reply.value());
+ debug() << "Got reply to ContactInfo.RequestContactInfo";
+ setFinished();
+ } else {
+ debug().nospace() <<
+ "ContactInfo.RequestContactInfo failed: " <<
+ reply.error().name() << ": " << reply.error().message();
+ setFinishedWithError(reply.error());
+ }
+
+ watcher->deleteLater();
+}
+
+} // Tp
diff --git a/TelepathyQt/pending-contact-info.h b/TelepathyQt/pending-contact-info.h
new file mode 100644
index 00000000..15e0686e
--- /dev/null
+++ b/TelepathyQt/pending-contact-info.h
@@ -0,0 +1,66 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_pending_contact_info_h_HEADER_GUARD_
+#define _TelepathyQt_pending_contact_info_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Contact>
+#include <TelepathyQt/PendingOperation>
+#include <TelepathyQt/Types>
+
+class QDBusPendingCallWatcher;
+
+namespace Tp
+{
+
+class TP_QT_EXPORT PendingContactInfo : public PendingOperation
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(PendingContactInfo);
+
+public:
+ ~PendingContactInfo();
+
+ ContactPtr contact() const;
+
+ Contact::InfoFields infoFields() const;
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onCallFinished(QDBusPendingCallWatcher *watcher);
+
+private:
+ friend class Contact;
+
+ TP_QT_NO_EXPORT PendingContactInfo(const ContactPtr &contact);
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/pending-contacts.cpp b/TelepathyQt/pending-contacts.cpp
new file mode 100644
index 00000000..6bbf68c4
--- /dev/null
+++ b/TelepathyQt/pending-contacts.cpp
@@ -0,0 +1,468 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/PendingContacts>
+#include "TelepathyQt/_gen/pending-contacts.moc.hpp"
+
+#include <TelepathyQt/Connection>
+#include <TelepathyQt/ConnectionLowlevel>
+#include <TelepathyQt/ContactManager>
+#include <TelepathyQt/ContactFactory>
+#include <TelepathyQt/PendingContactAttributes>
+#include <TelepathyQt/PendingHandles>
+#include <TelepathyQt/ReferencedHandles>
+
+#include "TelepathyQt/debug-internal.h"
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT PendingContacts::Private
+{
+ enum RequestType
+ {
+ ForHandles,
+ ForIdentifiers,
+ Upgrade
+ };
+
+ Private(PendingContacts *parent, const ContactManagerPtr &manager, const UIntList &handles,
+ const Features &features, const Features &missingFeatures,
+ const QMap<uint, ContactPtr> &satisfyingContacts)
+ : parent(parent),
+ manager(manager),
+ features(features),
+ missingFeatures(missingFeatures),
+ satisfyingContacts(satisfyingContacts),
+ requestType(ForHandles),
+ handles(handles),
+ nested(0)
+ {
+ }
+
+ Private(PendingContacts *parent, const ContactManagerPtr &manager, const QStringList &identifiers,
+ const Features &features)
+ : parent(parent),
+ manager(manager),
+ features(features),
+ requestType(ForIdentifiers),
+ identifiers(identifiers),
+ nested(0)
+ {
+ }
+
+ Private(PendingContacts *parent,
+ const ContactManagerPtr &manager, const QList<ContactPtr> &contactsToUpgrade,
+ const Features &features)
+ : parent(parent),
+ manager(manager),
+ features(features),
+ requestType(Upgrade),
+ contactsToUpgrade(contactsToUpgrade),
+ nested(0)
+ {
+ }
+
+ void setFinished();
+
+ // Public object
+ PendingContacts *parent;
+
+ // Generic parameters
+ ContactManagerPtr manager;
+ Features features;
+ Features missingFeatures;
+ QMap<uint, ContactPtr> satisfyingContacts;
+
+ // Request type specific parameters
+ RequestType requestType;
+ UIntList handles;
+ QStringList identifiers;
+ QList<ContactPtr> contactsToUpgrade;
+ PendingContacts *nested;
+
+ // Results
+ QList<ContactPtr> contacts;
+ UIntList invalidHandles;
+ QStringList validIds;
+ QHash<QString, QPair<QString, QString> > invalidIds;
+
+ ReferencedHandles handlesToInspect;
+};
+
+void PendingContacts::Private::setFinished()
+{
+ ConnectionLowlevelPtr connLowlevel = manager->connection()->lowlevel();
+ UIntList handles = invalidHandles;
+ foreach (uint handle, handles) {
+ if (connLowlevel->hasContactId(handle)) {
+ satisfyingContacts.insert(handle, manager->ensureContact(handle,
+ connLowlevel->contactId(handle), missingFeatures));
+ invalidHandles.removeOne(handle);
+ }
+ }
+
+ parent->setFinished();
+}
+
+/**
+ * \class PendingContacts
+ * \ingroup clientconn
+ * \headerfile TelepathyQt/pending-contacts.h <TelepathyQt/PendingContacts>
+ *
+ * \brief The PendingContacts class is used by ContactManager when
+ * creating/updating Contact objects.
+ *
+ * See \ref async_model
+ */
+
+PendingContacts::PendingContacts(const ContactManagerPtr &manager,
+ const UIntList &handles,
+ const Features &features,
+ const Features &missingFeatures,
+ const QStringList &interfaces,
+ const QMap<uint, ContactPtr> &satisfyingContacts,
+ const QSet<uint> &otherContacts,
+ const QString &errorName,
+ const QString &errorMessage)
+ : PendingOperation(manager->connection()),
+ mPriv(new Private(this, manager, handles, features, missingFeatures, satisfyingContacts))
+{
+ if (!errorName.isEmpty()) {
+ setFinishedWithError(errorName, errorMessage);
+ return;
+ }
+
+ if (!otherContacts.isEmpty()) {
+ ConnectionPtr conn = manager->connection();
+ if (conn->interfaces().contains(QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_CONTACTS))) {
+ PendingContactAttributes *attributes =
+ conn->lowlevel()->contactAttributes(otherContacts.toList(),
+ interfaces, true);
+
+ connect(attributes,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onAttributesFinished(Tp::PendingOperation*)));
+ } else {
+ // fallback to just create the contacts
+ PendingHandles *handles = conn->lowlevel()->referenceHandles(HandleTypeContact,
+ otherContacts.toList());
+ connect(handles,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onReferenceHandlesFinished(Tp::PendingOperation*)));
+ }
+ } else {
+ allAttributesFetched();
+ }
+}
+
+PendingContacts::PendingContacts(const ContactManagerPtr &manager,
+ const QStringList &identifiers, const Features &features,
+ const QString &errorName, const QString &errorMessage)
+ : PendingOperation(manager->connection()),
+ mPriv(new Private(this, manager, identifiers, features))
+{
+ if (!errorName.isEmpty()) {
+ setFinishedWithError(errorName, errorMessage);
+ return;
+ }
+
+ ConnectionPtr conn = manager->connection();
+ PendingHandles *handles = conn->lowlevel()->requestHandles(HandleTypeContact, identifiers);
+
+ connect(handles,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onRequestHandlesFinished(Tp::PendingOperation*)));
+}
+
+PendingContacts::PendingContacts(const ContactManagerPtr &manager,
+ const QList<ContactPtr> &contacts, const Features &features,
+ const QString &errorName, const QString &errorMessage)
+ : PendingOperation(manager->connection()),
+ mPriv(new Private(this, manager, contacts, features))
+{
+ if (!errorName.isEmpty()) {
+ setFinishedWithError(errorName, errorMessage);
+ return;
+ }
+
+ UIntList handles;
+ foreach (const ContactPtr &contact, contacts) {
+ handles.push_back(contact->handle()[0]);
+ }
+
+ mPriv->nested = manager->contactsForHandles(handles, features);
+ connect(mPriv->nested,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onNestedFinished(Tp::PendingOperation*)));
+}
+
+/**
+ * Class destructor.
+ */
+PendingContacts::~PendingContacts()
+{
+ delete mPriv;
+}
+
+ContactManagerPtr PendingContacts::manager() const
+{
+ return mPriv->manager;
+}
+
+Features PendingContacts::features() const
+{
+ return mPriv->features;
+}
+
+bool PendingContacts::isForHandles() const
+{
+ return mPriv->requestType == Private::ForHandles;
+}
+
+UIntList PendingContacts::handles() const
+{
+ if (!isForHandles()) {
+ warning() << "Tried to get handles from" << this << "which is not for handles!";
+ }
+
+ return mPriv->handles;
+}
+
+bool PendingContacts::isForIdentifiers() const
+{
+ return mPriv->requestType == Private::ForIdentifiers;
+}
+
+QStringList PendingContacts::identifiers() const
+{
+ if (!isForIdentifiers()) {
+ warning() << "Tried to get identifiers from" << this << "which is not for identifiers!";
+ }
+
+ return mPriv->identifiers;
+}
+
+bool PendingContacts::isUpgrade() const
+{
+ return mPriv->requestType == Private::Upgrade;
+}
+
+QList<ContactPtr> PendingContacts::contactsToUpgrade() const
+{
+ if (!isUpgrade()) {
+ warning() << "Tried to get contacts to upgrade from" << this << "which is not an upgrade!";
+ }
+
+ return mPriv->contactsToUpgrade;
+}
+
+QList<ContactPtr> PendingContacts::contacts() const
+{
+ if (!isFinished()) {
+ warning() << "PendingContacts::contacts() called before finished";
+ } else if (isError()) {
+ warning() << "PendingContacts::contacts() called when errored";
+ }
+
+ return mPriv->contacts;
+}
+
+UIntList PendingContacts::invalidHandles() const
+{
+ if (!isFinished()) {
+ warning() << "PendingContacts::invalidHandles() called before finished";
+ } else if (isError()) {
+ warning() << "PendingContacts::invalidHandles() called when errored";
+ } else if (!isForHandles()) {
+ warning() << "PendingContacts::invalidHandles() called for" << this << "which is for IDs!";
+ }
+
+ return mPriv->invalidHandles;
+}
+
+QStringList PendingContacts::validIdentifiers() const
+{
+ if (!isFinished()) {
+ warning() << "PendingContacts::validIdentifiers called before finished";
+ } else if (!isValid()) {
+ warning() << "PendingContacts::validIdentifiers called when not valid";
+ }
+
+ return mPriv->validIds;
+}
+
+QHash<QString, QPair<QString, QString> > PendingContacts::invalidIdentifiers() const
+{
+ if (!isFinished()) {
+ warning() << "PendingContacts::invalidIdentifiers called before finished";
+ }
+
+ return mPriv->invalidIds;
+}
+
+void PendingContacts::onAttributesFinished(PendingOperation *operation)
+{
+ PendingContactAttributes *pendingAttributes =
+ qobject_cast<PendingContactAttributes *>(operation);
+
+ if (pendingAttributes->isError()) {
+ debug() << "PendingAttrs error" << pendingAttributes->errorName()
+ << "message" << pendingAttributes->errorMessage();
+ setFinishedWithError(pendingAttributes->errorName(), pendingAttributes->errorMessage());
+ return;
+ }
+
+ ReferencedHandles validHandles = pendingAttributes->validHandles();
+ ContactAttributesMap attributes = pendingAttributes->attributes();
+
+ foreach (uint handle, mPriv->handles) {
+ if (!mPriv->satisfyingContacts.contains(handle)) {
+ int indexInValid = validHandles.indexOf(handle);
+ if (indexInValid >= 0) {
+ ReferencedHandles referencedHandle = validHandles.mid(indexInValid, 1);
+ QVariantMap handleAttributes = attributes[handle];
+ mPriv->satisfyingContacts.insert(handle, manager()->ensureContact(referencedHandle,
+ mPriv->missingFeatures, handleAttributes));
+ } else {
+ mPriv->invalidHandles.push_back(handle);
+ }
+ }
+ }
+
+ allAttributesFetched();
+}
+
+void PendingContacts::onRequestHandlesFinished(PendingOperation *operation)
+{
+ PendingHandles *pendingHandles = qobject_cast<PendingHandles *>(operation);
+
+ mPriv->validIds = pendingHandles->validNames();
+ mPriv->invalidIds = pendingHandles->invalidNames();
+
+ if (pendingHandles->isError()) {
+ debug() << "RequestHandles error" << operation->errorName()
+ << "message" << operation->errorMessage();
+ setFinishedWithError(operation->errorName(), operation->errorMessage());
+ return;
+ }
+
+ mPriv->nested = manager()->contactsForHandles(pendingHandles->handles(), features());
+ connect(mPriv->nested,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onNestedFinished(Tp::PendingOperation*)));
+}
+
+void PendingContacts::onReferenceHandlesFinished(PendingOperation *operation)
+{
+ PendingHandles *pendingHandles = qobject_cast<PendingHandles *>(operation);
+
+ if (pendingHandles->isError()) {
+ debug() << "ReferenceHandles error" << operation->errorName()
+ << "message" << operation->errorMessage();
+ setFinishedWithError(operation->errorName(), operation->errorMessage());
+ return;
+ }
+
+ ReferencedHandles validHandles = pendingHandles->handles();
+ UIntList invalidHandles = pendingHandles->invalidHandles();
+ ConnectionPtr conn = mPriv->manager->connection();
+ mPriv->handlesToInspect = ReferencedHandles(conn, HandleTypeContact, UIntList());
+ foreach (uint handle, mPriv->handles) {
+ if (!mPriv->satisfyingContacts.contains(handle)) {
+ int indexInValid = validHandles.indexOf(handle);
+ if (indexInValid >= 0) {
+ ReferencedHandles referencedHandle = validHandles.mid(indexInValid, 1);
+ mPriv->handlesToInspect.append(referencedHandle);
+ } else {
+ mPriv->invalidHandles.push_back(handle);
+ }
+ }
+ }
+
+ QDBusPendingCallWatcher *watcher =
+ new QDBusPendingCallWatcher(
+ conn->baseInterface()->InspectHandles(HandleTypeContact,
+ mPriv->handlesToInspect.toList()),
+ this);
+ connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(onInspectHandlesFinished(QDBusPendingCallWatcher*)));
+}
+
+void PendingContacts::onNestedFinished(PendingOperation *operation)
+{
+ Q_ASSERT(operation == mPriv->nested);
+
+ if (operation->isError()) {
+ debug() << " error" << operation->errorName()
+ << "message" << operation->errorMessage();
+ setFinishedWithError(operation->errorName(), operation->errorMessage());
+ return;
+ }
+
+ mPriv->contacts = mPriv->nested->contacts();
+ mPriv->nested = 0;
+ mPriv->setFinished();
+}
+
+void PendingContacts::onInspectHandlesFinished(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QStringList> reply = *watcher;
+
+ if (reply.isError()) {
+ debug().nospace() << "InspectHandles: error " << reply.error().name() << ": "
+ << reply.error().message();
+ setFinishedWithError(reply.error());
+ return;
+ }
+
+ QStringList names = reply.value();
+ int i = 0;
+ ConnectionPtr conn = mPriv->manager->connection();
+ foreach (uint handle, mPriv->handlesToInspect) {
+ QVariantMap handleAttributes;
+ handleAttributes.insert(QLatin1String(TELEPATHY_INTERFACE_CONNECTION "/contact-id"),
+ names[i++]);
+ ReferencedHandles referencedHandle(conn, HandleTypeContact,
+ UIntList() << handle);
+ mPriv->satisfyingContacts.insert(handle, manager()->ensureContact(referencedHandle,
+ mPriv->missingFeatures, handleAttributes));
+ }
+
+ allAttributesFetched();
+
+ watcher->deleteLater();
+}
+
+void PendingContacts::allAttributesFetched()
+{
+ foreach (uint handle, mPriv->handles) {
+ if (mPriv->satisfyingContacts.contains(handle)) {
+ mPriv->contacts.push_back(mPriv->satisfyingContacts[handle]);
+ }
+ }
+
+ mPriv->setFinished();
+}
+
+} // Tp
diff --git a/TelepathyQt/pending-contacts.h b/TelepathyQt/pending-contacts.h
new file mode 100644
index 00000000..0e03a8f2
--- /dev/null
+++ b/TelepathyQt/pending-contacts.h
@@ -0,0 +1,108 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_pending_contacts_h_HEADER_GUARD_
+#define _TelepathyQt_pending_contacts_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/PendingOperation>
+
+#include <QHash>
+#include <QList>
+#include <QMap>
+#include <QSet>
+#include <QStringList>
+
+#include <TelepathyQt/Types>
+#include <TelepathyQt/Contact>
+
+namespace Tp
+{
+
+class ContactManager;
+
+class TP_QT_EXPORT PendingContacts : public PendingOperation
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(PendingContacts);
+
+public:
+ ~PendingContacts();
+
+ ContactManagerPtr manager() const;
+ Features features() const;
+
+ bool isForHandles() const;
+ UIntList handles() const;
+
+ bool isForIdentifiers() const;
+ QStringList identifiers() const;
+
+ bool isUpgrade() const;
+ QList<ContactPtr> contactsToUpgrade() const;
+
+ QList<ContactPtr> contacts() const;
+ UIntList invalidHandles() const;
+ QStringList validIdentifiers() const;
+ QHash<QString, QPair<QString, QString> > invalidIdentifiers() const;
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onAttributesFinished(Tp::PendingOperation *);
+ TP_QT_NO_EXPORT void onRequestHandlesFinished(Tp::PendingOperation *);
+ TP_QT_NO_EXPORT void onReferenceHandlesFinished(Tp::PendingOperation *);
+ TP_QT_NO_EXPORT void onNestedFinished(Tp::PendingOperation *);
+ TP_QT_NO_EXPORT void onInspectHandlesFinished(QDBusPendingCallWatcher *);
+
+private:
+ friend class ContactManager;
+
+ // If errorName is non-empty, these will fail instantly
+ TP_QT_NO_EXPORT PendingContacts(const ContactManagerPtr &manager, const UIntList &handles,
+ const Features &features,
+ const Features &missingFeatures,
+ const QStringList &interfaces,
+ const QMap<uint, ContactPtr> &satisfyingContacts,
+ const QSet<uint> &otherContacts,
+ const QString &errorName = QString(),
+ const QString &errorMessage = QString());
+ TP_QT_NO_EXPORT PendingContacts(const ContactManagerPtr &manager, const QStringList &identifiers,
+ const Features &features,
+ const QString &errorName = QString(),
+ const QString &errorMessage = QString());
+ TP_QT_NO_EXPORT PendingContacts(const ContactManagerPtr &manager, const QList<ContactPtr> &contacts,
+ const Features &features,
+ const QString &errorName = QString(),
+ const QString &errorMessage = QString());
+
+ TP_QT_NO_EXPORT void allAttributesFetched();
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/pending-handles.cpp b/TelepathyQt/pending-handles.cpp
new file mode 100644
index 00000000..4a6625d2
--- /dev/null
+++ b/TelepathyQt/pending-handles.cpp
@@ -0,0 +1,490 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/PendingHandles>
+
+#include "TelepathyQt/_gen/pending-handles.moc.hpp"
+
+#include <TelepathyQt/Connection>
+#include <TelepathyQt/ReferencedHandles>
+
+#include "TelepathyQt/debug-internal.h"
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT PendingHandles::Private
+{
+ HandleType handleType;
+ bool isRequest;
+ QStringList namesRequested;
+ UIntList handlesToReference;
+ ReferencedHandles handles;
+ ReferencedHandles alreadyHeld;
+ UIntList invalidHandles;
+ QStringList validNames;
+ QHash<QString, QPair<QString, QString> > invalidNames;
+
+ // one to one requests (ids)
+ QHash<QDBusPendingCallWatcher *, uint> handlesForWatchers;
+ QHash<QDBusPendingCallWatcher *, QString> idsForWatchers;
+ QHash<QString, uint> handlesForIds;
+ int requestsFinished;
+};
+
+/**
+ * \class PendingHandles
+ * \ingroup clientconn
+ * \headerfile TelepathyQt/pending-handles.h <TelepathyQt/PendingHandles>
+ *
+ * \brief The PendingHandles class represents the parameters of and the reply to
+ * an asynchronous handle request/hold.
+ *
+ * Instances of this class cannot be constructed directly; the only way to get
+ * one is to use Connection::requestHandles() or Connection::referenceHandles().
+ *
+ * See \ref async_model
+ */
+
+PendingHandles::PendingHandles(const ConnectionPtr &connection, HandleType handleType,
+ const QStringList &names)
+ : PendingOperation(connection),
+ mPriv(new Private)
+{
+ debug() << "PendingHandles(request)";
+
+ mPriv->handleType = handleType;
+ mPriv->isRequest = true;
+ mPriv->namesRequested = names;
+ mPriv->requestsFinished = 0;
+
+ // try to request all handles at once
+ QDBusPendingCallWatcher *watcher =
+ new QDBusPendingCallWatcher(
+ connection->baseInterface()->RequestHandles(mPriv->handleType, names),
+ this);
+ connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(onRequestHandlesFinished(QDBusPendingCallWatcher*)));
+}
+
+PendingHandles::PendingHandles(const ConnectionPtr &connection, HandleType handleType,
+ const UIntList &handles, const UIntList &alreadyHeld,
+ const UIntList &notYetHeld)
+ : PendingOperation(connection),
+ mPriv(new Private)
+{
+ debug() << "PendingHandles(reference)";
+
+ mPriv->handleType = handleType;
+ mPriv->isRequest = false;
+ mPriv->handlesToReference = handles;
+ mPriv->alreadyHeld = ReferencedHandles(connection, mPriv->handleType, alreadyHeld);
+ mPriv->requestsFinished = 0;
+
+ if (notYetHeld.isEmpty()) {
+ debug() << " All handles already held, finishing up instantly";
+ mPriv->handles = mPriv->alreadyHeld;
+ setFinished();
+ } else {
+ debug() << " Calling HoldHandles";
+
+ QDBusPendingCallWatcher *watcher =
+ new QDBusPendingCallWatcher(
+ connection->baseInterface()->HoldHandles(mPriv->handleType, notYetHeld),
+ this);
+ connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(onHoldHandlesFinished(QDBusPendingCallWatcher*)));
+ }
+}
+
+PendingHandles::PendingHandles(const QString &errorName, const QString &errorMessage)
+ : PendingOperation(ConnectionPtr()), mPriv(new Private)
+{
+ setFinishedWithError(errorName, errorMessage);
+}
+
+/**
+ * Class destructor.
+ */
+PendingHandles::~PendingHandles()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the connection through which the operation was made.
+ *
+ * \return A pointer to the Connection object.
+ */
+ConnectionPtr PendingHandles::connection() const
+{
+ return ConnectionPtr(qobject_cast<Connection*>((Connection*) _object().data()));
+}
+
+/**
+ * Return the handle type specified in the operation.
+ *
+ * \return The target handle type as #HandleType.
+ */
+HandleType PendingHandles::handleType() const
+{
+ return mPriv->handleType;
+}
+
+/**
+ * Return whether the operation was a handle request (as opposed to a
+ * reference of existing handles).
+ *
+ * \return \c true if the operation was a request (== !isReference()), \c false otherwise.
+ * \sa isReference()
+ */
+bool PendingHandles::isRequest() const
+{
+ return mPriv->isRequest;
+}
+
+/**
+ * Return whether the operation was a handle reference (as opposed to a
+ * request for new handles).
+ *
+ * \return \c true if the operation was a reference (== !isRequest()), \c false otherwise.
+ * \sa isRequest()
+ */
+bool PendingHandles::isReference() const
+{
+ return !mPriv->isRequest;
+}
+
+/**
+ * If the operation was a request (as returned by isRequest()), returns the
+ * names of the entities for which handles were requested for. Otherwise,
+ * returns an empty list.
+ *
+ * \return Reference to a list of the names of the entities.
+ */
+const QStringList &PendingHandles::namesRequested() const
+{
+ return mPriv->namesRequested;
+}
+
+QStringList PendingHandles::validNames() const
+{
+ if (!isFinished()) {
+ warning() << "PendingHandles::validNames called before finished";
+ return QStringList();
+ } else if (!isValid()) {
+ warning() << "PendingHandles::validNames called when not valid";
+ return QStringList();
+ }
+
+ return mPriv->validNames;
+}
+
+QHash<QString, QPair<QString, QString> > PendingHandles::invalidNames() const
+{
+ if (!isFinished()) {
+ warning() << "PendingHandles::invalidNames called before finished";
+ return QHash<QString, QPair<QString, QString> >();
+ }
+
+ return mPriv->invalidNames;
+}
+
+/**
+ * If the operation was a reference (as returned by isReference()), returns
+ * the handles which were to be referenced. Otherwise, returns an empty
+ * list.
+ *
+ * \return Reference to a list of the handles specified to be referenced.
+ */
+const UIntList &PendingHandles::handlesToReference() const
+{
+ return mPriv->handlesToReference;
+}
+
+/**
+ * Return the now-referenced handles resulting from the operation. If the
+ * operation has not (yet) finished successfully (isFinished() returns
+ * <code>false</code>), the return value is undefined.
+ *
+ * For requests of new handles, <code>handles()[i]</code> will be the handle
+ * corresponding to the entity name <code>namesToRequest()[i]</code>. For
+ * references of existing handles, <code>handles()[i] ==
+ * handlesToReference()[i]</code> will be true for any <code>i</code>.
+ *
+ * \return ReferencedHandles object containing the handles.
+ */
+ReferencedHandles PendingHandles::handles() const
+{
+ if (!isFinished()) {
+ warning() << "PendingHandles::handles() called before finished";
+ return ReferencedHandles();
+ } else if (!isValid()) {
+ warning() << "PendingHandles::handles() called when not valid";
+ return ReferencedHandles();
+ }
+
+ return mPriv->handles;
+}
+
+UIntList PendingHandles::invalidHandles() const
+{
+ if (!isFinished()) {
+ warning() << "PendingHandles::invalidHandles called before finished";
+ }
+
+ return mPriv->invalidHandles;
+}
+
+void PendingHandles::onRequestHandlesFinished(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<UIntList> reply = *watcher;
+
+ if (reply.isError()) {
+ QDBusError error = reply.error();
+ if (error.name() != QLatin1String(TELEPATHY_ERROR_INVALID_HANDLE) &&
+ error.name() != QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT) &&
+ error.name() != QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE)) {
+ // do not fallback
+ foreach (const QString &name, mPriv->namesRequested) {
+ mPriv->invalidNames.insert(name,
+ QPair<QString, QString>(error.name(),
+ error.message()));
+ }
+ setFinishedWithError(error);
+ connection()->handleRequestLanded(mPriv->handleType);
+ watcher->deleteLater();
+ return;
+ }
+
+ if (mPriv->namesRequested.size() == 1) {
+ debug().nospace() << " Failure: error " <<
+ reply.error().name() << ": " <<
+ reply.error().message();
+
+ mPriv->invalidNames.insert(mPriv->namesRequested.first(),
+ QPair<QString, QString>(error.name(),
+ error.message()));
+ setFinished();
+ connection()->handleRequestLanded(mPriv->handleType);
+ watcher->deleteLater();
+ return;
+ }
+
+ // try to request one handles at a time
+ foreach (const QString &name, mPriv->namesRequested) {
+ QDBusPendingCallWatcher *watcher =
+ new QDBusPendingCallWatcher(
+ connection()->baseInterface()->RequestHandles(
+ mPriv->handleType,
+ QStringList() << name),
+ this);
+ connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(onRequestHandlesFallbackFinished(QDBusPendingCallWatcher*)));
+ mPriv->idsForWatchers.insert(watcher, name);
+ }
+ } else {
+ debug() << "Received reply to RequestHandles";
+ mPriv->handles = ReferencedHandles(connection(),
+ mPriv->handleType, reply.value());
+ mPriv->validNames.append(mPriv->namesRequested);
+ setFinished();
+ connection()->handleRequestLanded(mPriv->handleType);
+ }
+}
+
+void PendingHandles::onHoldHandlesFinished(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<void> reply = *watcher;
+
+ debug() << "Received reply to HoldHandles";
+
+ if (reply.isError()) {
+ debug().nospace() << " Failure: error " <<
+ reply.error().name() << ": " <<
+ reply.error().message();
+
+ QDBusError error = reply.error();
+ if (error.name() != QLatin1String(TELEPATHY_ERROR_INVALID_HANDLE) &&
+ error.name() != QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT) &&
+ error.name() != QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE)) {
+ // do not fallback
+ mPriv->invalidHandles = mPriv->handlesToReference;
+ setFinishedWithError(error);
+ watcher->deleteLater();
+ return;
+ }
+
+ if (mPriv->handlesToReference.size() == 1) {
+ debug().nospace() << " Failure: error " <<
+ reply.error().name() << ": " <<
+ reply.error().message();
+
+ mPriv->invalidHandles = mPriv->handlesToReference;
+ setFinished();
+ watcher->deleteLater();
+ return;
+ }
+
+ // try to request one handles at a time
+ foreach (uint handle, mPriv->handlesToReference) {
+ QDBusPendingCallWatcher *watcher =
+ new QDBusPendingCallWatcher(
+ connection()->baseInterface()->HoldHandles(
+ mPriv->handleType,
+ UIntList() << handle),
+ this);
+ connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(onHoldHandlesFallbackFinished(QDBusPendingCallWatcher*)));
+ mPriv->handlesForWatchers.insert(watcher, handle);
+ }
+ } else {
+ mPriv->handles = ReferencedHandles(connection(),
+ mPriv->handleType, mPriv->handlesToReference);
+ setFinished();
+ }
+
+ watcher->deleteLater();
+}
+
+void PendingHandles::onRequestHandlesFallbackFinished(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<UIntList> reply = *watcher;
+
+ Q_ASSERT(mPriv->idsForWatchers.contains(watcher));
+ QString id = mPriv->idsForWatchers.value(watcher);
+
+ debug() << "Received reply to RequestHandles(" << id << ")";
+
+ if (reply.isError()) {
+ debug().nospace() << " Failure: error " << reply.error().name() << ": "
+ << reply.error().message();
+
+ // if the error is disconnected for example, fail immediately
+ QDBusError error = reply.error();
+ if (error.name() != QLatin1String(TELEPATHY_ERROR_INVALID_HANDLE) &&
+ error.name() != QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT) &&
+ error.name() != QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE)) {
+ foreach (const QString &name, mPriv->namesRequested) {
+ mPriv->invalidNames.insert(name,
+ QPair<QString, QString>(error.name(),
+ error.message()));
+ }
+ setFinishedWithError(error);
+ connection()->handleRequestLanded(mPriv->handleType);
+ watcher->deleteLater();
+ return;
+ }
+
+ mPriv->invalidNames.insert(id,
+ QPair<QString, QString>(reply.error().name(),
+ reply.error().message()));
+ } else {
+ Q_ASSERT(reply.value().size() == 1);
+ uint handle = reply.value().first();
+ mPriv->handlesForIds.insert(id, handle);
+ }
+
+ if (++mPriv->requestsFinished == mPriv->namesRequested.size()) {
+ if (mPriv->handlesForIds.size() == 0) {
+ // all requests failed
+ setFinished();
+ } else {
+ // all requests either failed or finished successfully
+
+ // we need to return the handles in the same order as requested
+ UIntList handles;
+ foreach (const QString &name, mPriv->namesRequested) {
+ if (!mPriv->invalidNames.contains(name)) {
+ Q_ASSERT(mPriv->handlesForIds.contains(name));
+ handles.append(mPriv->handlesForIds.value(name));
+ mPriv->validNames.append(name);
+ }
+ }
+ mPriv->handles = ReferencedHandles(connection(),
+ mPriv->handleType, handles);
+
+ setFinished();
+ }
+
+ debug() << " namesRequested:" << mPriv->namesRequested;
+ debug() << " invalidNames :" << mPriv->invalidNames;
+ debug() << " validNames :" << mPriv->validNames;
+
+ connection()->handleRequestLanded(mPriv->handleType);
+ }
+
+ watcher->deleteLater();
+}
+
+void PendingHandles::onHoldHandlesFallbackFinished(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<void> reply = *watcher;
+
+ Q_ASSERT(mPriv->handlesForWatchers.contains(watcher));
+ uint handle = mPriv->handlesForWatchers.value(watcher);
+
+ debug() << "Received reply to HoldHandles(" << handle << ")";
+
+ if (reply.isError()) {
+ debug().nospace() << " Failure: error " << reply.error().name() << ": "
+ << reply.error().message();
+
+ // if the error is disconnected for example, fail immediately
+ QDBusError error = reply.error();
+ if (error.name() != QLatin1String(TELEPATHY_ERROR_INVALID_HANDLE) &&
+ error.name() != QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT) &&
+ error.name() != QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE)) {
+ mPriv->invalidHandles = mPriv->handlesToReference;
+ setFinishedWithError(error);
+ watcher->deleteLater();
+ return;
+ }
+
+ mPriv->invalidHandles.append(handle);
+ }
+
+ if (++mPriv->requestsFinished == mPriv->namesRequested.size()) {
+ // we need to return the handles in the same order as requested
+ UIntList handles;
+ foreach (uint handle, mPriv->handlesToReference) {
+ if (!mPriv->invalidHandles.contains(handle)) {
+ handles.append(handle);
+ }
+ }
+
+ if (handles.size() != 0) {
+ mPriv->handles = ReferencedHandles(connection(),
+ mPriv->handleType, handles);
+ }
+
+ setFinished();
+ }
+
+ watcher->deleteLater();
+}
+
+} // Tp
diff --git a/TelepathyQt/pending-handles.h b/TelepathyQt/pending-handles.h
new file mode 100644
index 00000000..4a5dc0c2
--- /dev/null
+++ b/TelepathyQt/pending-handles.h
@@ -0,0 +1,96 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_pending_handles_h_HEADER_GUARD_
+#define _TelepathyQt_pending_handles_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/PendingOperation>
+#include <TelepathyQt/Types>
+
+#include <QHash>
+#include <QString>
+#include <QStringList>
+
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+class PendingHandles;
+class ReferencedHandles;
+
+class TP_QT_EXPORT PendingHandles : public PendingOperation
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(PendingHandles)
+
+public:
+ ~PendingHandles();
+
+ ConnectionPtr connection() const;
+
+ HandleType handleType() const;
+
+ bool isRequest() const;
+
+ bool isReference() const;
+
+ const QStringList &namesRequested() const;
+
+ QStringList validNames() const;
+
+ QHash<QString, QPair<QString, QString> > invalidNames() const;
+
+ const UIntList &handlesToReference() const;
+
+ ReferencedHandles handles() const;
+
+ UIntList invalidHandles() const;
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onRequestHandlesFinished(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void onHoldHandlesFinished(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void onRequestHandlesFallbackFinished(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void onHoldHandlesFallbackFinished(QDBusPendingCallWatcher *watcher);
+
+private:
+ friend class ConnectionLowlevel;
+
+ TP_QT_NO_EXPORT PendingHandles(const ConnectionPtr &connection, HandleType handleType,
+ const QStringList &names);
+ TP_QT_NO_EXPORT PendingHandles(const ConnectionPtr &connection, HandleType handleType,
+ const UIntList &handles, const UIntList &alreadyHeld, const UIntList &notYetHeld);
+ TP_QT_NO_EXPORT PendingHandles(const QString &errorName, const QString &errorMessage);
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/pending-operation.cpp b/TelepathyQt/pending-operation.cpp
new file mode 100644
index 00000000..139821b3
--- /dev/null
+++ b/TelepathyQt/pending-operation.cpp
@@ -0,0 +1,424 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/PendingOperation>
+
+#define IN_TP_QT_HEADER
+#include "simple-pending-operations.h"
+#undef IN_TP_QT_HEADER
+
+#include "TelepathyQt/_gen/pending-operation.moc.hpp"
+#include "TelepathyQt/_gen/simple-pending-operations.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <QDBusPendingCall>
+#include <QDBusPendingCallWatcher>
+#include <QTimer>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT PendingOperation::Private
+{
+ Private(const SharedPtr<RefCounted> &object)
+ : object(object),
+ finished(false)
+ {
+ }
+
+ SharedPtr<RefCounted> object;
+ QString errorName;
+ QString errorMessage;
+ bool finished;
+};
+
+/**
+ * \class PendingOperation
+ * \headerfile TelepathyQt/pending-operation.h <TelepathyQt/PendingOperation>
+ *
+ * \brief The PendingOperation class is a base class for pending asynchronous
+ * operations.
+ *
+ * This class represents an incomplete asynchronous operation, such as a
+ * D-Bus method call. When the operation has finished, it emits
+ * finished(). The slot or slots connected to the finished() signal may obtain
+ * additional information from the pending operation.
+ *
+ * In simple cases, like a D-Bus method with no 'out' arguments or for which
+ * all 'out' arguments are to be ignored (so the possible results are
+ * success with no extra information, or failure with an error code), the
+ * trivial subclass PendingVoid can be used.
+ *
+ * For pending operations that produce a result, another subclass of
+ * PendingOperation can be used, with additional methods that provide that
+ * result to the library user.
+ *
+ * After finished() is emitted, the PendingOperation is automatically
+ * deleted using deleteLater(), so library users must not explicitly
+ * delete this object.
+ *
+ * The design is loosely based on KDE's KJob.
+ *
+ * See \ref async_model
+ */
+
+/**
+ * Construct a new PendingOperation object.
+ *
+ * \param object The object on which this pending operation takes place.
+ */
+PendingOperation::PendingOperation(const SharedPtr<RefCounted> &object)
+ : QObject(),
+ mPriv(new Private(object))
+{
+}
+
+/**
+ * Class destructor.
+ */
+PendingOperation::~PendingOperation()
+{
+ if (!mPriv->finished) {
+ warning() << this <<
+ "still pending when it was deleted - finished will "
+ "never be emitted";
+ }
+
+ delete mPriv;
+}
+
+/**
+ * Return the object on which this pending operation takes place.
+ *
+ * \return A pointer to a RefCounted object.
+ * \deprecated Will be made protected in the next API break, because using it outside the
+ * PendingOperation requires unsafe type conversions, and it's not always clear just which
+ * object the operation "takes place on", and we don't want to commit to keeping the objects
+ * fixed.
+ */
+SharedPtr<RefCounted> PendingOperation::object() const
+{
+ return _object();
+}
+
+// Temporary, to allow internal access to the object not warn of deprecation
+SharedPtr<RefCounted> PendingOperation::_object() const
+{
+ return mPriv->object;
+}
+
+void PendingOperation::emitFinished()
+{
+ Q_ASSERT(mPriv->finished);
+ emit finished(this);
+ deleteLater();
+}
+
+/**
+ * Record that this pending operation has finished successfully, and
+ * emit the finished() signal next time the event loop runs.
+ */
+void PendingOperation::setFinished()
+{
+ if (mPriv->finished) {
+ if (!mPriv->errorName.isEmpty()) {
+ warning() << this << "trying to finish with success, but already"
+ " failed with" << mPriv->errorName << ":" << mPriv->errorMessage;
+ } else {
+ warning() << this << "trying to finish with success, but already"
+ " succeeded";
+ }
+ return;
+ }
+
+ mPriv->finished = true;
+ Q_ASSERT(isValid());
+ QTimer::singleShot(0, this, SLOT(emitFinished()));
+}
+
+/**
+ * Record that this pending operation has finished with an error, and
+ * emit the finished() signal next time the event loop runs.
+ *
+ * \param name The D-Bus error name, which must be non-empty.
+ * \param message The debugging message.
+ */
+void PendingOperation::setFinishedWithError(const QString &name,
+ const QString &message)
+{
+ if (mPriv->finished) {
+ if (mPriv->errorName.isEmpty()) {
+ warning() << this << "trying to fail with" << name <<
+ "but already failed with" << errorName() << ":" <<
+ errorMessage();
+ } else {
+ warning() << this << "trying to fail with" << name <<
+ "but already succeeded";
+ }
+ return;
+ }
+
+ if (name.isEmpty()) {
+ warning() << this << "should be given a non-empty error name";
+ mPriv->errorName = QLatin1String("org.freedesktop.Telepathy.Qt4.ErrorHandlingError");
+ } else {
+ mPriv->errorName = name;
+ }
+
+ mPriv->errorMessage = message;
+ mPriv->finished = true;
+ Q_ASSERT(isError());
+ QTimer::singleShot(0, this, SLOT(emitFinished()));
+}
+
+/**
+ * Record that this pending operation has finished with an error, and
+ * emit the finished() signal next time the event loop runs.
+ *
+ * \param error The error.
+ * \sa finished()
+ */
+void PendingOperation::setFinishedWithError(const QDBusError &error)
+{
+ setFinishedWithError(error.name(), error.message());
+}
+
+/**
+ * Return whether or not the request completed successfully. If the
+ * request has not yet finished processing (isFinished() returns
+ * \c false), this cannot yet be known, and \c false
+ * will be returned.
+ *
+ * Equivalent to <code>(isFinished() && !isError())</code>.
+ *
+ * \return \c true if the request has finished processing and
+ * has completed successfully, \c false otherwise.
+ */
+bool PendingOperation::isValid() const
+{
+ return (mPriv->finished && mPriv->errorName.isEmpty());
+}
+
+/**
+ * Return whether or not the request has finished processing.
+ *
+ * The signal finished() is emitted when this changes from \c false
+ * to true.
+ *
+ * Equivalent to <code>(isValid() || isError())</code>.
+ *
+ * \return \c true if the request has finished, \c false otherwise.
+ * \sa finished()
+ */
+bool PendingOperation::isFinished() const
+{
+ return mPriv->finished;
+}
+
+/**
+ * Return whether or not the request resulted in an error.
+ *
+ * If the request has not yet finished processing (isFinished() returns
+ * \c false), this cannot yet be known, and \c false
+ * will be returned.
+ *
+ * Equivalent to <code>(isFinished() && !isValid())</code>.
+ *
+ * \return \c true if the request has finished processing and
+ * has resulted in an error, \c false otherwise.
+ */
+bool PendingOperation::isError() const
+{
+ return (mPriv->finished && !mPriv->errorName.isEmpty());
+}
+
+/**
+ * If isError() returns \c true, returns the D-Bus error with which
+ * the operation failed. If the operation succeeded or has not yet
+ * finished, returns an empty string.
+ *
+ * \return A D-Bus error name, or an empty string.
+ */
+QString PendingOperation::errorName() const
+{
+ return mPriv->errorName;
+}
+
+/**
+ * If isError() would return \c true, returns a debugging message associated
+ * with the error, which may be an empty string. Otherwise, return an
+ * empty string.
+ *
+ * \return A debugging message, or an empty string.
+ */
+QString PendingOperation::errorMessage() const
+{
+ return mPriv->errorMessage;
+}
+
+/**
+ * \fn void PendingOperation::finished(Tp::PendingOperation* operation)
+ *
+ * Emitted when the pending operation finishes, i.e. when isFinished()
+ * changes from \c false to \c true.
+ *
+ * \param operation This operation object, from which further information
+ * may be obtained.
+ */
+
+/**
+ * \class PendingSuccess
+ * \ingroup utils
+ * \headerfile TelepathyQt/simple-pending-operations.h <TelepathyQt/PendingSuccess>
+ *
+ * \brief The PendingSuccess class represents PendingOperation that is always
+ * successful.
+ */
+
+/**
+ * \class PendingFailure
+ * \ingroup utils
+ * \headerfile TelepathyQt/simple-pending-operations.h <TelepathyQt/PendingFailure>
+ *
+ * \brief The PendingFailure class represents a PendingOperation that always
+ * fails with the error passed to the constructor.
+ */
+
+/**
+ * \class PendingVoid
+ * \ingroup utils
+ * \headerfile TelepathyQt/simple-pending-operations.h <TelepathyQt/PendingVoid>
+ *
+ * \brief The PendingVoid class is a generic subclass of PendingOperation
+ * representing a pending D-Bus method call that does not return anything
+ * (or returns a result that is not interesting).
+ */
+
+/**
+ * Construct a new PendingVoid object.
+ *
+ * \param object The object on which this pending operation takes place.
+ * \param call A pending call as returned by the auto-generated low level
+ * Telepathy API; if the method returns anything, the return
+ * value(s) will be ignored.
+ */
+PendingVoid::PendingVoid(QDBusPendingCall call, const SharedPtr<RefCounted> &object)
+ : PendingOperation(object)
+{
+ connect(new QDBusPendingCallWatcher(call),
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(watcherFinished(QDBusPendingCallWatcher*)));
+}
+
+void PendingVoid::watcherFinished(QDBusPendingCallWatcher *watcher)
+{
+ if (watcher->isError()) {
+ setFinishedWithError(watcher->error());
+ } else {
+ setFinished();
+ }
+
+ watcher->deleteLater();
+}
+
+struct TP_QT_NO_EXPORT PendingComposite::Private
+{
+ Private(bool failOnFirstError, uint nOperations)
+ : failOnFirstError(failOnFirstError),
+ error(false),
+ nOperations(nOperations),
+ nOperationsFinished(0)
+ {
+ }
+
+ bool failOnFirstError;
+ bool error;
+ QString errorName;
+ QString errorMessage;
+ uint nOperations;
+ uint nOperationsFinished;
+};
+
+/**
+ * \class PendingComposite
+ * \ingroup utils
+ * \headerfile TelepathyQt/simple-pending-operations.h <TelepathyQt/PendingComposite>
+ *
+ * \brief The PendingComposite class is a PendingOperation that can be used
+ * to track multiple pending operations at once.
+ */
+
+PendingComposite::PendingComposite(const QList<PendingOperation*> &operations,
+ const SharedPtr<RefCounted> &object)
+ : PendingOperation(object),
+ mPriv(new Private(true, operations.size()))
+{
+ foreach (PendingOperation *operation, operations) {
+ connect(operation,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onOperationFinished(Tp::PendingOperation*)));
+ }
+}
+
+PendingComposite::PendingComposite(const QList<PendingOperation*> &operations,
+ bool failOnFirstError, const SharedPtr<RefCounted> &object)
+ : PendingOperation(object),
+ mPriv(new Private(failOnFirstError, operations.size()))
+{
+ foreach (PendingOperation *operation, operations) {
+ connect(operation,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onOperationFinished(Tp::PendingOperation*)));
+ }
+}
+
+PendingComposite::~PendingComposite()
+{
+ delete mPriv;
+}
+
+void PendingComposite::onOperationFinished(Tp::PendingOperation *op)
+{
+ if (op->isError()) {
+ if (mPriv->failOnFirstError) {
+ setFinishedWithError(op->errorName(), op->errorMessage());
+ return;
+ } else if (!mPriv->error) {
+ /* only save the first error that will be used on setFinishedWithError when all
+ * pending operations finish */
+ mPriv->error = true;
+ mPriv->errorName = op->errorName();
+ mPriv->errorMessage = op->errorMessage();
+ }
+ }
+
+ if (++mPriv->nOperationsFinished == mPriv->nOperations) {
+ if (!mPriv->error) {
+ setFinished();
+ } else {
+ setFinishedWithError(mPriv->errorName, mPriv->errorMessage);
+ }
+ }
+}
+
+} // Tp
diff --git a/TelepathyQt/pending-operation.h b/TelepathyQt/pending-operation.h
new file mode 100644
index 00000000..55ab454e
--- /dev/null
+++ b/TelepathyQt/pending-operation.h
@@ -0,0 +1,89 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_pending_operation_h_HEADER_GUARD_
+#define _TelepathyQt_pending_operation_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Global>
+#include <TelepathyQt/RefCounted>
+#include <TelepathyQt/SharedPtr>
+
+#include <QObject>
+
+class QDBusError;
+class QDBusPendingCall;
+class QDBusPendingCallWatcher;
+
+namespace Tp
+{
+
+class ReadinessHelper;
+
+class TP_QT_EXPORT PendingOperation : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(PendingOperation)
+
+public:
+ virtual ~PendingOperation();
+
+ TP_QT_DEPRECATED SharedPtr<RefCounted> object() const;
+
+ bool isFinished() const;
+
+ bool isValid() const;
+
+ bool isError() const;
+ QString errorName() const;
+ QString errorMessage() const;
+
+Q_SIGNALS:
+ void finished(Tp::PendingOperation *operation);
+
+protected:
+ PendingOperation(const SharedPtr<RefCounted> &object);
+ TP_QT_NO_EXPORT SharedPtr<RefCounted> _object() const; // TODO: turn this into _object()
+
+protected Q_SLOTS:
+ void setFinished();
+ void setFinishedWithError(const QString &name, const QString &message);
+ void setFinishedWithError(const QDBusError &error);
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void emitFinished();
+
+private:
+ friend class ContactManager;
+ friend class ReadinessHelper;
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/pending-ready.cpp b/TelepathyQt/pending-ready.cpp
new file mode 100644
index 00000000..e8620a43
--- /dev/null
+++ b/TelepathyQt/pending-ready.cpp
@@ -0,0 +1,148 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/PendingReady>
+
+#include "TelepathyQt/_gen/pending-ready.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/DBusProxy>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT PendingReady::Private
+{
+ Private(const DBusProxyPtr &proxy,
+ const Features &requestedFeatures)
+ : proxy(proxy),
+ requestedFeatures(requestedFeatures)
+ {
+ }
+
+ DBusProxyPtr proxy;
+ Features requestedFeatures;
+};
+
+/**
+ * \class PendingReady
+ * \ingroup utils
+ * \headerfile TelepathyQt/pending-ready.h <TelepathyQt/PendingReady>
+ *
+ * \brief The PendingReady class represents the features requested and the reply
+ * to a request for an object to become ready.
+ *
+ * Instances of this class cannot be constructed directly; the only way to get
+ * one is via ReadyObject::becomeReady() or a DBusProxyFactory subclass.
+ *
+ * See \ref async_model
+ */
+
+/**
+ * Construct a new PendingReady object.
+ *
+ * \todo Actually make it do the prepare ops. Currently they aren't taken into account in any way.
+ *
+ * \param object The object that will become ready.
+ * \param requestedFeatures Features to be made ready on the object.
+ */
+PendingReady::PendingReady(const SharedPtr<RefCounted> &object,
+ const Features &requestedFeatures)
+ : PendingOperation(object),
+ mPriv(new Private(DBusProxyPtr(dynamic_cast<DBusProxy*>((DBusProxy*) object.data())),
+ requestedFeatures))
+{
+ // This is a PendingReady created by ReadinessHelper, and will be set ready by it - so should
+ // not do anything ourselves here.
+}
+
+/**
+ * Construct a new PendingReady object.
+ *
+ * \todo Actually make it do the prepare ops. Currently they aren't taken into account in any way.
+ *
+ * \param factory The factory the request was made with.
+ * \param proxy The proxy that will become ready.
+ * \param requestedFeatures Features to be made ready on the object.
+ */
+PendingReady::PendingReady(const SharedPtr<DBusProxyFactory> &factory,
+ const DBusProxyPtr &proxy,
+ const Features &requestedFeatures)
+ : PendingOperation(factory),
+ mPriv(new Private(proxy, requestedFeatures))
+{
+ if (requestedFeatures.isEmpty()) {
+ setFinished();
+ return;
+ }
+
+ connect(proxy->becomeReady(requestedFeatures),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onNestedFinished(Tp::PendingOperation*)));
+}
+
+/**
+ * Class destructor.
+ */
+PendingReady::~PendingReady()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the proxy that should become ready.
+ *
+ * \return A pointer to the DBusProxy object if the operation was
+ * created by a proxy object or a DBusProxyFactory,
+ * otherwise a null DBusProxyPtr.
+ */
+DBusProxyPtr PendingReady::proxy() const
+{
+ return mPriv->proxy;
+}
+
+/**
+ * Return the features that were requested to become ready on the
+ * object.
+ *
+ * \return The requested features as a set of Feature objects.
+ */
+Features PendingReady::requestedFeatures() const
+{
+ return mPriv->requestedFeatures;
+}
+
+void PendingReady::onNestedFinished(Tp::PendingOperation *nested)
+{
+ Q_ASSERT(nested->isFinished());
+
+ if (nested->isValid()) {
+ setFinished();
+ } else {
+ warning() << "Nested PendingReady for" << _object() << "failed with"
+ << nested->errorName() << ":" << nested->errorMessage();
+ setFinishedWithError(nested->errorName(), nested->errorMessage());
+ }
+}
+
+} // Tp
diff --git a/TelepathyQt/pending-ready.h b/TelepathyQt/pending-ready.h
new file mode 100644
index 00000000..7d0627ae
--- /dev/null
+++ b/TelepathyQt/pending-ready.h
@@ -0,0 +1,71 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_pending_ready_h_HEADER_GUARD_
+#define _TelepathyQt_pending_ready_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/DBusProxyFactory>
+#include <TelepathyQt/PendingOperation>
+#include <TelepathyQt/ReadinessHelper>
+#include <TelepathyQt/SharedPtr>
+
+#include <QSet>
+
+namespace Tp
+{
+
+class TP_QT_EXPORT PendingReady: public PendingOperation
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(PendingReady);
+
+public:
+ ~PendingReady();
+
+ DBusProxyPtr proxy() const;
+
+ Features requestedFeatures() const;
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onNestedFinished(Tp::PendingOperation *);
+
+private:
+ friend class Connection;
+ friend class DBusProxyFactory;
+ friend class ReadinessHelper;
+
+ TP_QT_NO_EXPORT PendingReady(const SharedPtr<RefCounted> &object, const Features &requestedFeatures);
+ TP_QT_NO_EXPORT PendingReady(const SharedPtr<DBusProxyFactory> &factory,
+ const DBusProxyPtr &proxy, const Features &requestedFeatures);
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/pending-send-message.cpp b/TelepathyQt/pending-send-message.cpp
new file mode 100644
index 00000000..d339b571
--- /dev/null
+++ b/TelepathyQt/pending-send-message.cpp
@@ -0,0 +1,153 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/PendingSendMessage>
+
+#include "TelepathyQt/_gen/pending-send-message.moc.hpp"
+
+#include <TelepathyQt/ContactMessenger>
+#include <TelepathyQt/Message>
+#include <TelepathyQt/TextChannel>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT PendingSendMessage::Private
+{
+ Private(const Message &message)
+ : message(message)
+ {
+ }
+
+ QString token;
+ Message message;
+};
+
+/**
+ * \class PendingSendMessage
+ * \ingroup clientchannel
+ * \headerfile TelepathyQt/pending-send-message.h <TelepathyQt/PendingSendMessage>
+ *
+ * \brief The PendingSendMessage class represents the parameters of and the
+ * reply to an asynchronous message send request.
+ *
+ * See \ref async_model
+ */
+
+PendingSendMessage::PendingSendMessage(const TextChannelPtr &channel, const Message &message)
+ : PendingOperation(channel),
+ mPriv(new Private(message))
+{
+}
+
+PendingSendMessage::PendingSendMessage(const ContactMessengerPtr &messenger, const Message &message)
+ : PendingOperation(messenger),
+ mPriv(new Private(message))
+{
+}
+
+PendingSendMessage::~PendingSendMessage()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the channel used to send the message if this instance was created using
+ * TextChannel. If it was created using ContactMessenger, return a null TextChannelPtr.
+ *
+ * \return A pointer to the TextChannel object, or a null TextChannelPtr if created using
+ * ContactMessenger.
+ */
+TextChannelPtr PendingSendMessage::channel() const
+{
+ return TextChannelPtr(qobject_cast<TextChannel*>((TextChannel*) _object().data()));
+}
+
+/**
+ * Return the contact messenger used to send the message if this instance was created using
+ * ContactMessenger. If it was created using TextChannel, return a null ContactMessengerPtr.
+ *
+ * \return A pointer to the ContactMessenger object, or a null ContactMessengerPtr if created using
+ * TextChannel.
+ */
+ContactMessengerPtr PendingSendMessage::messenger() const
+{
+ return ContactMessengerPtr(qobject_cast<ContactMessenger*>((ContactMessenger*) _object().data()));
+}
+
+QString PendingSendMessage::sentMessageToken() const
+{
+ return mPriv->token;
+}
+
+Message PendingSendMessage::message() const
+{
+ return mPriv->message;
+}
+
+void PendingSendMessage::onTextSent(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<> reply = *watcher;
+
+ if (reply.isError()) {
+ setFinishedWithError(reply.error());
+ } else {
+ setFinished();
+ }
+ watcher->deleteLater();
+}
+
+void PendingSendMessage::onMessageSent(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QString> reply = *watcher;
+
+ if (reply.isError()) {
+ setFinishedWithError(reply.error());
+ } else {
+ mPriv->token = reply.value();
+ setFinished();
+ }
+ watcher->deleteLater();
+}
+
+void PendingSendMessage::onCDMessageSent(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<QString> reply = *watcher;
+
+ if (reply.isError()) {
+ QDBusError error = reply.error();
+ if (error.name() == TP_QT_DBUS_ERROR_UNKNOWN_METHOD ||
+ error.name() == TP_QT_DBUS_ERROR_UNKNOWN_INTERFACE) {
+ setFinishedWithError(TP_QT_ERROR_NOT_IMPLEMENTED,
+ QLatin1String("Channel Dispatcher implementation (e.g. mission-control), "
+ "does not support interface CD.I.Messages"));
+ } else {
+ setFinishedWithError(error);
+ }
+ } else {
+ mPriv->token = reply.value();
+ setFinished();
+ }
+ watcher->deleteLater();
+}
+
+} // Tp
diff --git a/TelepathyQt/pending-send-message.h b/TelepathyQt/pending-send-message.h
new file mode 100644
index 00000000..fb9a8a3a
--- /dev/null
+++ b/TelepathyQt/pending-send-message.h
@@ -0,0 +1,77 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_pending_send_message_h_HEADER_GUARD_
+#define _TelepathyQt_pending_send_message_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/PendingOperation>
+#include <TelepathyQt/Types>
+
+class QDBusPendingCallWatcher;
+class QString;
+
+namespace Tp
+{
+
+class Message;
+
+class TP_QT_EXPORT PendingSendMessage : public PendingOperation
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(PendingSendMessage)
+
+public:
+ ~PendingSendMessage();
+
+ TextChannelPtr channel() const;
+
+ ContactMessengerPtr messenger() const;
+
+ QString sentMessageToken() const;
+ Message message() const;
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onTextSent(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void onMessageSent(QDBusPendingCallWatcher *watcher);
+ TP_QT_NO_EXPORT void onCDMessageSent(QDBusPendingCallWatcher *watcher);
+
+private:
+ friend class TextChannel;
+ friend class ContactMessenger;
+
+ TP_QT_NO_EXPORT PendingSendMessage(const TextChannelPtr &channel,
+ const Message &message);
+ TP_QT_NO_EXPORT PendingSendMessage(const ContactMessengerPtr &messenger,
+ const Message &message);
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/pending-stream-tube-connection.cpp b/TelepathyQt/pending-stream-tube-connection.cpp
new file mode 100644
index 00000000..5759d923
--- /dev/null
+++ b/TelepathyQt/pending-stream-tube-connection.cpp
@@ -0,0 +1,272 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/PendingStreamTubeConnection>
+
+#include "TelepathyQt/_gen/pending-stream-tube-connection.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/IncomingStreamTubeChannel>
+#include <TelepathyQt/PendingVariant>
+#include <TelepathyQt/Types>
+#include "TelepathyQt/types-internal.h"
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT PendingStreamTubeConnection::Private
+{
+ Private(PendingStreamTubeConnection *parent);
+ ~Private();
+
+ // Public object
+ PendingStreamTubeConnection *parent;
+
+ IncomingStreamTubeChannelPtr tube;
+ SocketAddressType type;
+ QHostAddress hostAddress;
+ quint16 port;
+ QString socketPath;
+ bool requiresCredentials;
+ uchar credentialByte;
+};
+
+PendingStreamTubeConnection::Private::Private(PendingStreamTubeConnection *parent)
+ : parent(parent),
+ requiresCredentials(false),
+ credentialByte(0)
+{
+
+}
+
+PendingStreamTubeConnection::Private::~Private()
+{
+}
+
+/**
+ * \class PendingStreamTubeConnection
+ * \ingroup clientchannel
+ * \headerfile TelepathyQt/incoming-stream-tube-channel.h <TelepathyQt/PendingStreamTubeConnection>
+ *
+ * \brief The PendingStreamTubeConnection class represents an asynchronous
+ * operation for accepting an incoming stream tube.
+ *
+ * See \ref async_model
+ */
+
+PendingStreamTubeConnection::PendingStreamTubeConnection(
+ PendingVariant *acceptOperation,
+ SocketAddressType type,
+ bool requiresCredentials,
+ uchar credentialByte,
+ const IncomingStreamTubeChannelPtr &channel)
+ : PendingOperation(channel),
+ mPriv(new Private(this))
+{
+ mPriv->tube = channel;
+ mPriv->type = type;
+ mPriv->requiresCredentials = requiresCredentials;
+ mPriv->credentialByte = credentialByte;
+
+ /* keep track of channel invalidation */
+ connect(channel.data(),
+ SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
+ SLOT(onChannelInvalidated(Tp::DBusProxy*,QString,QString)));
+
+ debug() << "Calling StreamTube.Accept";
+ if (acceptOperation->isFinished()) {
+ onAcceptFinished(acceptOperation);
+ } else {
+ connect(acceptOperation,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onAcceptFinished(Tp::PendingOperation*)));
+ }
+}
+
+PendingStreamTubeConnection::PendingStreamTubeConnection(
+ const QString& errorName,
+ const QString& errorMessage,
+ const IncomingStreamTubeChannelPtr &channel)
+ : PendingOperation(channel),
+ mPriv(new PendingStreamTubeConnection::Private(this))
+{
+ setFinishedWithError(errorName, errorMessage);
+}
+
+/**
+ * Class destructor.
+ */
+PendingStreamTubeConnection::~PendingStreamTubeConnection()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the type of the opened stream tube socket.
+ *
+ * \return The socket type as #SocketAddressType.
+ * \see localAddress(), ipAddress()
+ */
+SocketAddressType PendingStreamTubeConnection::addressType() const
+{
+ return mPriv->tube->addressType();
+}
+
+/**
+ * Return the local address of the opened stream tube socket.
+ *
+ * This method will return a meaningful value only if the incoming stream tube
+ * was accepted as an Unix socket.
+ *
+ * \return Unix socket address if using an Unix socket,
+ * or an undefined value otherwise.
+ * \see addressType(), ipAddress()
+ */
+QString PendingStreamTubeConnection::localAddress() const
+{
+ return mPriv->tube->localAddress();
+}
+
+/**
+ * Return the IP address/port combination of the opened stream tube socket.
+ *
+ * This method will return a meaningful value only if the incoming stream tube
+ * was accepted as a TCP socket.
+ *
+ * \return Pair of IP address as QHostAddress and port if using a TCP socket,
+ * or an undefined value otherwise.
+ * \see addressType(), localAddress()
+ */
+QPair<QHostAddress, quint16> PendingStreamTubeConnection::ipAddress() const
+{
+ return mPriv->tube->ipAddress();
+}
+
+/**
+ * Return whether sending a credential byte once connecting to the socket is required.
+ *
+ * Note that if this method returns \c true, one should send a SCM_CREDS or SCM_CREDENTIALS
+ * and the credentialByte() once connected. If SCM_CREDS or SCM_CREDENTIALS cannot be sent,
+ * the credentialByte() should still be sent.
+ *
+ * \return \c true if sending credentials is required, \c false otherwise.
+ * \sa credentialByte()
+ */
+bool PendingStreamTubeConnection::requiresCredentials() const
+{
+ return mPriv->requiresCredentials;
+}
+
+/**
+ * Return the credential byte to send once connecting to the socket if requiresCredentials() is \c
+ * true.
+ *
+ * \return The credential byte.
+ * \sa requiresCredentials()
+ */
+uchar PendingStreamTubeConnection::credentialByte() const
+{
+ return mPriv->credentialByte;
+}
+
+void PendingStreamTubeConnection::onChannelInvalidated(DBusProxy *proxy,
+ const QString &errorName, const QString &errorMessage)
+{
+ Q_UNUSED(proxy);
+
+ if (isFinished()) {
+ return;
+ }
+
+ warning().nospace() << "StreamTube.Accept failed because channel was invalidated with " <<
+ errorName << ": " << errorMessage;
+
+ setFinishedWithError(errorName, errorMessage);
+}
+
+void PendingStreamTubeConnection::onAcceptFinished(PendingOperation *op)
+{
+ if (isFinished()) {
+ return;
+ }
+
+ if (op->isError()) {
+ warning().nospace() << "StreamTube.Accept failed with " <<
+ op->errorName() << ": " << op->errorMessage();
+ setFinishedWithError(op->errorName(), op->errorMessage());
+ return;
+ }
+
+ debug() << "StreamTube.Accept returned successfully";
+
+ PendingVariant *pv = qobject_cast<PendingVariant *>(op);
+ // Build the address
+ if (mPriv->type == SocketAddressTypeIPv4) {
+ SocketAddressIPv4 addr = qdbus_cast<SocketAddressIPv4>(pv->result());
+ debug().nospace() << "Got address " << addr.address << ":" << addr.port;
+ mPriv->hostAddress = QHostAddress(addr.address);
+ mPriv->port = addr.port;
+ } else if (mPriv->type == SocketAddressTypeIPv6) {
+ SocketAddressIPv6 addr = qdbus_cast<SocketAddressIPv6>(pv->result());
+ debug().nospace() << "Got address " << addr.address << ":" << addr.port;
+ mPriv->hostAddress = QHostAddress(addr.address);
+ mPriv->port = addr.port;
+ } else {
+ // Unix socket
+ mPriv->socketPath = QLatin1String(qdbus_cast<QByteArray>(pv->result()));
+ debug() << "Got socket " << mPriv->socketPath;
+ }
+
+ // It might have been already opened - check
+ if (mPriv->tube->state() == TubeChannelStateOpen) {
+ onTubeStateChanged(mPriv->tube->state());
+ } else {
+ // Wait until the tube gets opened on the other side
+ connect(mPriv->tube.data(), SIGNAL(stateChanged(Tp::TubeChannelState)),
+ this, SLOT(onTubeStateChanged(Tp::TubeChannelState)));
+ }
+}
+
+void PendingStreamTubeConnection::onTubeStateChanged(TubeChannelState state)
+{
+ debug() << "Tube state changed to " << state;
+ if (state == TubeChannelStateOpen) {
+ // The tube is ready, populate its properties
+ if (mPriv->type == SocketAddressTypeIPv4 || mPriv->type == SocketAddressTypeIPv6) {
+ mPriv->tube->setIpAddress(qMakePair<QHostAddress, quint16>(mPriv->hostAddress,
+ mPriv->port));
+ } else {
+ // Unix socket
+ mPriv->tube->setLocalAddress(mPriv->socketPath);
+ }
+
+ // Mark the operation as finished
+ setFinished();
+ } else if (state != TubeChannelStateLocalPending) {
+ // Something happened
+ setFinishedWithError(QLatin1String("Connection refused"),
+ QLatin1String("The connection to this tube was refused"));
+ }
+}
+
+}
diff --git a/TelepathyQt/pending-stream-tube-connection.h b/TelepathyQt/pending-stream-tube-connection.h
new file mode 100644
index 00000000..2796e906
--- /dev/null
+++ b/TelepathyQt/pending-stream-tube-connection.h
@@ -0,0 +1,81 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_pending_stream_tube_connection_h_HEADER_GUARD_
+#define _TelepathyQt_pending_stream_tube_connection_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/PendingOperation>
+#include <TelepathyQt/Types>
+
+#include <QPair>
+
+class QHostAddress;
+
+namespace Tp
+{
+
+class PendingVariant;
+class IncomingStreamTubeChannel;
+
+class TP_QT_EXPORT PendingStreamTubeConnection : public PendingOperation
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(PendingStreamTubeConnection)
+
+public:
+ virtual ~PendingStreamTubeConnection();
+
+ SocketAddressType addressType() const;
+
+ QPair<QHostAddress, quint16> ipAddress() const;
+ QString localAddress() const;
+
+ bool requiresCredentials() const;
+ uchar credentialByte() const;
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onChannelInvalidated(Tp::DBusProxy *proxy,
+ const QString &errorName, const QString &errorMessage);
+ TP_QT_NO_EXPORT void onAcceptFinished(Tp::PendingOperation *op);
+ TP_QT_NO_EXPORT void onTubeStateChanged(Tp::TubeChannelState state);
+
+private:
+ TP_QT_NO_EXPORT PendingStreamTubeConnection(PendingVariant *acceptOperation,
+ SocketAddressType type, bool requiresCredentials, uchar credentialByte,
+ const IncomingStreamTubeChannelPtr &channel);
+ TP_QT_NO_EXPORT PendingStreamTubeConnection(
+ const QString &errorName, const QString &errorMessage,
+ const IncomingStreamTubeChannelPtr &channel);
+
+ struct Private;
+ friend class IncomingStreamTubeChannel;
+ friend struct Private;
+ Private *mPriv;
+};
+
+}
+
+#endif // TP_PENDING_STREAM_TUBE_CONNECTION_H
diff --git a/TelepathyQt/pending-string-list.cpp b/TelepathyQt/pending-string-list.cpp
new file mode 100644
index 00000000..9776ffc9
--- /dev/null
+++ b/TelepathyQt/pending-string-list.cpp
@@ -0,0 +1,100 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/PendingStringList>
+
+#include "TelepathyQt/_gen/pending-string-list.moc.hpp"
+#include "TelepathyQt/debug-internal.h"
+
+#include <QDBusPendingReply>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT PendingStringList::Private
+{
+ QStringList result;
+};
+
+/**
+ * \class PendingStringList
+ * \ingroup utils
+ * \headerfile TelepathyQt/pending-string-list.h <TelepathyQt/PendingStringList>
+ *
+ * \brief The PendingStringList class is a generic subclass of PendingOperation
+ * representing a pending D-Bus method call that returns a string list.
+ *
+ * See \ref async_model
+ */
+
+PendingStringList::PendingStringList(const SharedPtr<RefCounted> &object)
+ : PendingOperation(object),
+ mPriv(new Private)
+{
+}
+
+PendingStringList::PendingStringList(QDBusPendingCall call, const SharedPtr<RefCounted> &object)
+ : PendingOperation(object),
+ mPriv(new Private)
+{
+ connect(new QDBusPendingCallWatcher(call),
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ this,
+ SLOT(watcherFinished(QDBusPendingCallWatcher*)));
+}
+
+/**
+ * Class destructor.
+ */
+PendingStringList::~PendingStringList()
+{
+ delete mPriv;
+}
+
+QStringList PendingStringList::result() const
+{
+ return mPriv->result;
+}
+
+void PendingStringList::setResult(const QStringList &result)
+{
+ mPriv->result = result;
+}
+
+void PendingStringList::watcherFinished(QDBusPendingCallWatcher* watcher)
+{
+ QDBusPendingReply<QStringList> reply = *watcher;
+
+ if (!reply.isError()) {
+ debug() << "Got reply to PendingStringList call";
+ setResult(reply.value());
+ setFinished();
+ } else {
+ debug().nospace() << "PendingStringList call failed: " <<
+ reply.error().name() << ": " << reply.error().message();
+ setFinishedWithError(reply.error());
+ }
+
+ watcher->deleteLater();
+}
+
+} // Tp
diff --git a/TelepathyQt/pending-string-list.h b/TelepathyQt/pending-string-list.h
new file mode 100644
index 00000000..ddd29871
--- /dev/null
+++ b/TelepathyQt/pending-string-list.h
@@ -0,0 +1,63 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_pending_string_list_h_HEADER_GUARD_
+#define _TelepathyQt_pending_string_list_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/PendingOperation>
+
+#include <QStringList>
+
+namespace Tp
+{
+
+class TP_QT_EXPORT PendingStringList : public PendingOperation
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(PendingStringList);
+
+public:
+ PendingStringList(const SharedPtr<RefCounted> &object);
+ PendingStringList(QDBusPendingCall call, const SharedPtr<RefCounted> &object);
+ ~PendingStringList();
+
+ QStringList result() const;
+
+protected:
+ void setResult(const QStringList &result);
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void watcherFinished(QDBusPendingCallWatcher *watcher);
+
+private:
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/pending-variant-map.cpp b/TelepathyQt/pending-variant-map.cpp
new file mode 100644
index 00000000..effa1467
--- /dev/null
+++ b/TelepathyQt/pending-variant-map.cpp
@@ -0,0 +1,91 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/PendingVariantMap>
+
+#include "TelepathyQt/_gen/pending-variant-map.moc.hpp"
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/Global>
+
+#include <QDBusPendingReply>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT PendingVariantMap::Private
+{
+ QVariantMap result;
+};
+
+/**
+ * \class PendingVariantMap
+ * \ingroup utils
+ * \headerfile TelepathyQt/pending-variant-map.h <TelepathyQt/PendingVariantMap>
+ *
+ * \brief The PendingVariantMap class is a generic subclass of PendingOperation
+ * representing a pending D-Bus method call that returns a variant map.
+ *
+ * See \ref async_model
+ */
+
+PendingVariantMap::PendingVariantMap(QDBusPendingCall call, const SharedPtr<RefCounted> &object)
+ : PendingOperation(object),
+ mPriv(new Private)
+{
+ connect(new QDBusPendingCallWatcher(call),
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ this,
+ SLOT(watcherFinished(QDBusPendingCallWatcher*)));
+}
+
+/**
+ * Class destructor.
+ */
+PendingVariantMap::~PendingVariantMap()
+{
+ delete mPriv;
+}
+
+QVariantMap PendingVariantMap::result() const
+{
+ return mPriv->result;
+}
+
+void PendingVariantMap::watcherFinished(QDBusPendingCallWatcher* watcher)
+{
+ QDBusPendingReply<QVariantMap> reply = *watcher;
+
+ if (!reply.isError()) {
+ debug() << "Got reply to PendingVariantMap call";
+ mPriv->result = reply.value();
+ setFinished();
+ } else {
+ debug().nospace() << "PendingVariantMap call failed: " <<
+ reply.error().name() << ": " << reply.error().message();
+ setFinishedWithError(reply.error());
+ }
+
+ watcher->deleteLater();
+}
+
+} // Tp
diff --git a/TelepathyQt/pending-variant-map.h b/TelepathyQt/pending-variant-map.h
new file mode 100644
index 00000000..035c2a87
--- /dev/null
+++ b/TelepathyQt/pending-variant-map.h
@@ -0,0 +1,60 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009-2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_pending_variant_map_h_HEADER_GUARD_
+#define _TelepathyQt_pending_variant_map_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Global>
+#include <TelepathyQt/PendingOperation>
+
+#include <QVariant>
+
+namespace Tp
+{
+
+class TP_QT_EXPORT PendingVariantMap : public PendingOperation
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(PendingVariantMap);
+
+public:
+ PendingVariantMap(QDBusPendingCall call, const SharedPtr<RefCounted> &object);
+ ~PendingVariantMap();
+
+ QVariantMap result() const;
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void watcherFinished(QDBusPendingCallWatcher*);
+
+private:
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/pending-variant.cpp b/TelepathyQt/pending-variant.cpp
new file mode 100644
index 00000000..f9868fea
--- /dev/null
+++ b/TelepathyQt/pending-variant.cpp
@@ -0,0 +1,91 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/PendingVariant>
+
+#include "TelepathyQt/_gen/pending-variant.moc.hpp"
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/Global>
+
+#include <QDBusPendingReply>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT PendingVariant::Private
+{
+ QVariant result;
+};
+
+/**
+ * \class PendingVariant
+ * \ingroup utils
+ * \headerfile TelepathyQt/pending-variant.h <TelepathyQt/PendingVariant>
+ *
+ * \brief The PendingVariant class is a generic subclass of PendingOperation
+ * representing a pending D-Bus method call that returns a variant.
+ *
+ * See \ref async_model
+ */
+
+PendingVariant::PendingVariant(QDBusPendingCall call, const SharedPtr<RefCounted> &object)
+ : PendingOperation(object),
+ mPriv(new Private)
+{
+ connect(new QDBusPendingCallWatcher(call),
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ this,
+ SLOT(watcherFinished(QDBusPendingCallWatcher*)));
+}
+
+/**
+ * Class destructor.
+ */
+PendingVariant::~PendingVariant()
+{
+ delete mPriv;
+}
+
+QVariant PendingVariant::result() const
+{
+ return mPriv->result;
+}
+
+void PendingVariant::watcherFinished(QDBusPendingCallWatcher* watcher)
+{
+ QDBusPendingReply<QDBusVariant> reply = *watcher;
+
+ if (!reply.isError()) {
+ debug() << "Got reply to PendingVariant call";
+ mPriv->result = reply.value().variant();
+ setFinished();
+ } else {
+ debug().nospace() << "PendingVariant call failed: " <<
+ reply.error().name() << ": " << reply.error().message();
+ setFinishedWithError(reply.error());
+ }
+
+ watcher->deleteLater();
+}
+
+} // Tp
diff --git a/TelepathyQt/pending-variant.h b/TelepathyQt/pending-variant.h
new file mode 100644
index 00000000..0a4ff868
--- /dev/null
+++ b/TelepathyQt/pending-variant.h
@@ -0,0 +1,60 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_pending_variant_h_HEADER_GUARD_
+#define _TelepathyQt_pending_variant_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Global>
+#include <TelepathyQt/PendingOperation>
+
+#include <QVariant>
+
+namespace Tp
+{
+
+class TP_QT_EXPORT PendingVariant : public PendingOperation
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(PendingVariant);
+
+public:
+ PendingVariant(QDBusPendingCall call, const SharedPtr<RefCounted> &object);
+ ~PendingVariant();
+
+ QVariant result() const;
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void watcherFinished(QDBusPendingCallWatcher*);
+
+private:
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/presence.cpp b/TelepathyQt/presence.cpp
new file mode 100644
index 00000000..556e9561
--- /dev/null
+++ b/TelepathyQt/presence.cpp
@@ -0,0 +1,335 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/Presence>
+
+#include "TelepathyQt/debug-internal.h"
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT Presence::Private : public QSharedData
+{
+ Private(const SimplePresence &sp)
+ : sp(sp)
+ {
+ }
+
+ Private(ConnectionPresenceType type, const QString &status, const QString &statusMessage)
+ {
+ sp.type = type;
+ sp.status = status;
+ sp.statusMessage = statusMessage;
+ }
+
+ SimplePresence sp;
+};
+
+/**
+ * \class Presence
+ * \ingroup wrappers
+ * \headerfile TelepathyQt/presence.h <TelepathyQt/Presence>
+ *
+ * \brief The Presence class represents a Telepathy simple presence.
+ */
+
+Presence::Presence()
+{
+}
+
+Presence::Presence(const SimplePresence &sp)
+ : mPriv(new Private(sp))
+{
+}
+
+Presence::Presence(ConnectionPresenceType type, const QString &status, const QString &statusMessage)
+ : mPriv(new Private(type, status, statusMessage))
+{
+}
+
+Presence::Presence(const Presence &other)
+ : mPriv(other.mPriv)
+{
+}
+
+Presence::~Presence()
+{
+}
+
+Presence Presence::available(const QString &statusMessage)
+{
+ return Presence(ConnectionPresenceTypeAvailable, QLatin1String("available"), statusMessage);
+}
+
+Presence Presence::away(const QString &statusMessage)
+{
+ return Presence(ConnectionPresenceTypeAway, QLatin1String("away"), statusMessage);
+}
+
+Presence Presence::brb(const QString &statusMessage)
+{
+ return Presence(ConnectionPresenceTypeAway, QLatin1String("brb"), statusMessage);
+}
+
+Presence Presence::busy(const QString &statusMessage)
+{
+ return Presence(ConnectionPresenceTypeBusy, QLatin1String("busy"), statusMessage);
+}
+
+Presence Presence::xa(const QString &statusMessage)
+{
+ return Presence(ConnectionPresenceTypeExtendedAway, QLatin1String("xa"), statusMessage);
+}
+
+Presence Presence::hidden(const QString &statusMessage)
+{
+ return Presence(ConnectionPresenceTypeHidden, QLatin1String("hidden"), statusMessage);
+}
+
+Presence Presence::offline(const QString &statusMessage)
+{
+ return Presence(ConnectionPresenceTypeOffline, QLatin1String("offline"), statusMessage);
+}
+
+Presence &Presence::operator=(const Presence &other)
+{
+ this->mPriv = other.mPriv;
+ return *this;
+}
+
+bool Presence::operator==(const Presence &other) const
+{
+ if (!isValid() || !other.isValid()) {
+ if (!isValid() && !other.isValid()) {
+ return true;
+ }
+ return false;
+ }
+
+ return mPriv->sp == other.mPriv->sp;
+}
+
+bool Presence::operator!=(const Presence &other) const
+{
+ if (!isValid() || !other.isValid()) {
+ if (!isValid() && !other.isValid()) {
+ return false;
+ }
+ return true;
+ }
+
+ return mPriv->sp != other.mPriv->sp;
+}
+
+ConnectionPresenceType Presence::type() const
+{
+ if (!isValid()) {
+ return ConnectionPresenceTypeUnknown;
+ }
+
+ return (ConnectionPresenceType) mPriv->sp.type;
+}
+
+QString Presence::status() const
+{
+ if (!isValid()) {
+ return QString();
+ }
+
+ return mPriv->sp.status;
+}
+
+QString Presence::statusMessage() const
+{
+ if (!isValid()) {
+ return QString();
+ }
+
+ return mPriv->sp.statusMessage;
+}
+
+void Presence::setStatus(const SimplePresence &value)
+{
+ if (!isValid()) {
+ mPriv = new Private(value);
+ return;
+ }
+
+ mPriv->sp = value;
+}
+
+void Presence::setStatus(ConnectionPresenceType type, const QString &status,
+ const QString &statusMessage)
+{
+ if (!isValid()) {
+ mPriv = new Private(type, status, statusMessage);
+ return;
+ }
+
+ mPriv->sp.type = type;
+ mPriv->sp.status = status;
+ mPriv->sp.statusMessage = statusMessage;
+}
+
+SimplePresence Presence::barePresence() const
+{
+ if (!isValid()) {
+ return SimplePresence();
+ }
+
+ return mPriv->sp;
+}
+
+struct TP_QT_NO_EXPORT PresenceSpec::Private : public QSharedData
+{
+ Private(const QString &status, const SimpleStatusSpec &spec)
+ : status(status),
+ spec(spec)
+ {
+ }
+
+ QString status;
+ SimpleStatusSpec spec;
+};
+
+/**
+ * \class PresenceSpec
+ * \ingroup wrappers
+ * \headerfile TelepathyQt/presence.h <TelepathyQt/PresenceSpec>
+ *
+ * \brief The PresenceSpec class represents a Telepathy presence information
+ * supported by a protocol.
+ */
+
+PresenceSpec::PresenceSpec()
+{
+}
+
+PresenceSpec::PresenceSpec(const QString &status, const SimpleStatusSpec &spec)
+ : mPriv(new Private(status, spec))
+{
+}
+
+PresenceSpec::PresenceSpec(const PresenceSpec &other)
+ : mPriv(other.mPriv)
+{
+}
+
+PresenceSpec::~PresenceSpec()
+{
+}
+
+PresenceSpec &PresenceSpec::operator=(const PresenceSpec &other)
+{
+ this->mPriv = other.mPriv;
+ return *this;
+}
+
+bool PresenceSpec::operator==(const PresenceSpec &other) const
+{
+ if (!isValid() || !other.isValid()) {
+ if (!isValid() && !other.isValid()) {
+ return true;
+ }
+ return false;
+ }
+
+ return (mPriv->status == other.mPriv->status) &&
+ (mPriv->spec == other.mPriv->spec);
+}
+
+bool PresenceSpec::operator!=(const PresenceSpec &other) const
+{
+ if (!isValid() || !other.isValid()) {
+ if (!isValid() && !other.isValid()) {
+ return false;
+ }
+ return true;
+ }
+
+ return (mPriv->status != other.mPriv->status) &&
+ (mPriv->spec != other.mPriv->spec);
+}
+
+bool PresenceSpec::operator<(const PresenceSpec &other) const
+{
+ if (!isValid()) {
+ return false;
+ }
+
+ if (!other.isValid()) {
+ return true;
+ }
+
+ return (mPriv->status < other.mPriv->status);
+}
+
+Presence PresenceSpec::presence(const QString &statusMessage) const
+{
+ if (!isValid()) {
+ return Presence();
+ }
+
+ if (!canHaveStatusMessage() && !statusMessage.isEmpty()) {
+ warning() << "Passing a status message to PresenceSpec with "
+ "canHaveStatusMessage() being false";
+ }
+
+ return Presence((ConnectionPresenceType) mPriv->spec.type, mPriv->status, statusMessage);
+}
+
+bool PresenceSpec::maySetOnSelf() const
+{
+ if (!isValid()) {
+ return false;
+ }
+
+ return mPriv->spec.maySetOnSelf;
+}
+
+bool PresenceSpec::canHaveStatusMessage() const
+{
+ if (!isValid()) {
+ return false;
+ }
+
+ return mPriv->spec.canHaveMessage;
+}
+
+SimpleStatusSpec PresenceSpec::bareSpec() const
+{
+ if (!isValid()) {
+ return SimpleStatusSpec();
+ }
+
+ return mPriv->spec;
+}
+
+/**
+ * \class PresenceSpecList
+ * \ingroup wrappers
+ * \headerfile TelepathyQt/presence.h <TelepathyQt/PresenceSpecList>
+ *
+ * \brief The PresenceSpecList class represents a list of PresenceSpec.
+ */
+
+} // Tp
diff --git a/TelepathyQt/presence.h b/TelepathyQt/presence.h
new file mode 100644
index 00000000..4e5421cd
--- /dev/null
+++ b/TelepathyQt/presence.h
@@ -0,0 +1,135 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_presence_h_HEADER_GUARD_
+#define _TelepathyQt_presence_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+class TP_QT_EXPORT Presence
+{
+public:
+ Presence();
+ Presence(const SimplePresence &sp);
+ Presence(ConnectionPresenceType type, const QString &status, const QString &statusMessage);
+ Presence(const Presence &other);
+ ~Presence();
+
+ static Presence available(const QString &statusMessage = QString());
+ static Presence away(const QString &statusMessage = QString());
+ static Presence brb(const QString &statusMessage = QString());
+ static Presence busy(const QString &statusMessage = QString());
+ static Presence xa(const QString &statusMessage = QString());
+ static Presence hidden(const QString &statusMessage = QString());
+ static Presence offline(const QString &statusMessage = QString());
+
+ bool isValid() const { return mPriv.constData() != 0; }
+
+ Presence &operator=(const Presence &other);
+ bool operator==(const Presence &other) const;
+ bool operator!=(const Presence &other) const;
+
+ ConnectionPresenceType type() const;
+ QString status() const;
+ QString statusMessage() const;
+ void setStatus(const SimplePresence &value);
+ void setStatus(ConnectionPresenceType type, const QString &status,
+ const QString &statusMessage);
+
+ SimplePresence barePresence() const;
+
+private:
+ struct Private;
+ friend struct Private;
+ QSharedDataPointer<Private> mPriv;
+};
+
+class TP_QT_EXPORT PresenceSpec
+{
+public:
+ PresenceSpec();
+ PresenceSpec(const QString &status, const SimpleStatusSpec &spec);
+ PresenceSpec(const PresenceSpec &other);
+ ~PresenceSpec();
+
+ bool isValid() const { return mPriv.constData() != 0; }
+
+ PresenceSpec &operator=(const PresenceSpec &other);
+ bool operator==(const PresenceSpec &other) const;
+ bool operator!=(const PresenceSpec &other) const;
+ bool operator<(const PresenceSpec &other) const;
+
+ Presence presence(const QString &statusMessage = QString()) const;
+ bool maySetOnSelf() const;
+ bool canHaveStatusMessage() const;
+
+ SimpleStatusSpec bareSpec() const;
+
+private:
+ struct Private;
+ friend struct Private;
+ QSharedDataPointer<Private> mPriv;
+};
+
+class TP_QT_EXPORT PresenceSpecList : public QList<PresenceSpec>
+{
+public:
+ PresenceSpecList() { }
+ PresenceSpecList(const SimpleStatusSpecMap &specMap)
+ {
+ SimpleStatusSpecMap::const_iterator i = specMap.constBegin();
+ SimpleStatusSpecMap::const_iterator end = specMap.end();
+ for (; i != end; ++i) {
+ QString status = i.key();
+ SimpleStatusSpec spec = i.value();
+ append(PresenceSpec(status, spec));
+ }
+ }
+ PresenceSpecList(const QList<PresenceSpec> &other)
+ : QList<PresenceSpec>(other)
+ {
+ }
+
+ QMap<QString, PresenceSpec> toMap() const
+ {
+ QMap<QString, PresenceSpec> ret;
+ Q_FOREACH (const PresenceSpec &spec, *this) {
+ ret.insert(spec.presence().status(), spec);
+ }
+ return ret;
+ }
+};
+
+} // Tp
+
+Q_DECLARE_METATYPE(Tp::Presence);
+Q_DECLARE_METATYPE(Tp::PresenceSpec);
+
+#endif
diff --git a/TelepathyQt/profile-manager.cpp b/TelepathyQt/profile-manager.cpp
new file mode 100644
index 00000000..46391b6e
--- /dev/null
+++ b/TelepathyQt/profile-manager.cpp
@@ -0,0 +1,332 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/ProfileManager>
+
+#include "TelepathyQt/_gen/profile-manager.moc.hpp"
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/ConnectionManager>
+#include <TelepathyQt/PendingComposite>
+#include <TelepathyQt/PendingReady>
+#include <TelepathyQt/PendingStringList>
+#include <TelepathyQt/Profile>
+#include <TelepathyQt/ReadinessHelper>
+
+#include <QFile>
+#include <QFileInfo>
+#include <QString>
+#include <QStringList>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT ProfileManager::Private
+{
+ Private(ProfileManager *parent, const QDBusConnection &bus);
+
+ static void introspectMain(Private *self);
+ static void introspectFakeProfiles(Private *self);
+
+ ProfileManager *parent;
+ ReadinessHelper *readinessHelper;
+ QDBusConnection bus;
+ QHash<QString, ProfilePtr> profiles;
+ QList<ConnectionManagerPtr> cms;
+};
+
+ProfileManager::Private::Private(ProfileManager *parent, const QDBusConnection &bus)
+ : parent(parent),
+ readinessHelper(parent->readinessHelper()),
+ bus(bus)
+{
+ ReadinessHelper::Introspectables introspectables;
+
+ ReadinessHelper::Introspectable introspectableCore(
+ QSet<uint>() << 0, // makesSenseForStatuses
+ Features(), // dependsOnFeatures
+ QStringList(), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &Private::introspectMain,
+ this);
+ introspectables[FeatureCore] = introspectableCore;
+
+ ReadinessHelper::Introspectable introspectableFakeProfiles(
+ QSet<uint>() << 0, // makesSenseForStatuses
+ Features() << FeatureCore, // dependsOnFeatures
+ QStringList(), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &Private::introspectFakeProfiles,
+ this);
+ introspectables[FeatureFakeProfiles] = introspectableFakeProfiles;
+
+ readinessHelper->addIntrospectables(introspectables);
+}
+
+void ProfileManager::Private::introspectMain(ProfileManager::Private *self)
+{
+ QStringList searchDirs = Profile::searchDirs();
+
+ foreach (const QString searchDir, searchDirs) {
+ QDir dir(searchDir);
+ dir.setFilter(QDir::Files);
+
+ QFileInfoList list = dir.entryInfoList();
+ for (int i = 0; i < list.size(); ++i) {
+ QFileInfo fi = list.at(i);
+
+ if (fi.completeSuffix() != QLatin1String("profile")) {
+ continue;
+ }
+
+ QString fileName = fi.absoluteFilePath();
+ QString serviceName = fi.baseName();
+
+ if (self->profiles.contains(serviceName)) {
+ debug() << "Profile for service" << serviceName << "already "
+ "exists. Ignoring profile file:" << fileName;
+ continue;
+ }
+
+ ProfilePtr profile = Profile::createForFileName(fileName);
+ if (!profile->isValid()) {
+ continue;
+ }
+
+ if (profile->type() != QLatin1String("IM")) {
+ debug() << "Ignoring profile for service" << serviceName <<
+ ": type != IM. Profile file:" << fileName;
+ continue;
+ }
+
+ debug() << "Found profile for service" << serviceName <<
+ "- profile file:" << fileName;
+ self->profiles.insert(serviceName, profile);
+ }
+ }
+
+ self->readinessHelper->setIntrospectCompleted(FeatureCore, true);
+}
+
+void ProfileManager::Private::introspectFakeProfiles(ProfileManager::Private *self)
+{
+ PendingStringList *pendingCmNames = ConnectionManager::listNames(self->bus);
+ self->parent->connect(pendingCmNames,
+ SIGNAL(finished(Tp::PendingOperation *)),
+ SLOT(onCmNamesRetrieved(Tp::PendingOperation *)));
+}
+
+/**
+ * \class ProfileManager
+ * \headerfile TelepathyQt/profile-manager.h <TelepathyQt/ProfileManager>
+ *
+ * \brief The ProfileManager class provides helper methods to retrieve Profile
+ * objects.
+ */
+
+/**
+ * Feature representing the core that needs to become ready to make the ProfileManager
+ * object usable.
+ *
+ * Note that this feature must be enabled in order to use all ProfileManager methods.
+ *
+ * When calling isReady(), becomeReady(), this feature is implicitly added
+ * to the requested features.
+ */
+const Feature ProfileManager::FeatureCore = Feature(QLatin1String(ProfileManager::staticMetaObject.className()), 0, true);
+
+/**
+ * Enabling this feature will make ProfileManager create fake Profile objects to all protocols
+ * supported on the installed connection managers, even if they don't have .profile files installed
+ * making use of them.
+ *
+ * Fake profiles are identified by Profile::isFake() returning \c true.
+ *
+ * The fake profile will contain the following info:
+ * - Profile::type() will return "IM"
+ * - Profile::provider() will return an empty string
+ * - Profile::serviceName() will return cmName-protocolName
+ * - Profile::name() and Profile::protocolName() will return protocolName
+ * - Profile::iconName() will return "im-protocolName"
+ * - Profile::cmName() will return cmName
+ * - Profile::parameters() will return a list matching CM default parameters for protocol with name
+ * protocolName.
+ * - Profile::presences() will return an empty list and
+ * Profile::allowOtherPresences() will return \c true, meaning that CM
+ * presences should be used
+ * - Profile::unsupportedChannelClassSpecs() will return an empty list
+ *
+ * Where cmName and protocolName are the name of the connection manager and the name of the protocol
+ * for which this fake Profile is created, respectively.
+ */
+const Feature ProfileManager::FeatureFakeProfiles = Feature(QLatin1String(ProfileManager::staticMetaObject.className()), 1);
+
+/**
+ * Create a new ProfileManager object.
+ */
+ProfileManagerPtr ProfileManager::create(const QDBusConnection &bus)
+{
+ return ProfileManagerPtr(new ProfileManager(bus));
+}
+
+/**
+ * Construct a new ProfileManager object.
+ */
+ProfileManager::ProfileManager(const QDBusConnection &bus)
+ : Object(),
+ ReadyObject(this, FeatureCore),
+ mPriv(new Private(this, bus))
+{
+}
+
+/**
+ * Class destructor.
+ */
+ProfileManager::~ProfileManager()
+{
+ delete mPriv;
+}
+
+/**
+ * Return a list of all available profiles.
+ *
+ * \return A list of all available profiles.
+ */
+QList<ProfilePtr> ProfileManager::profiles() const
+{
+ return mPriv->profiles.values();
+}
+
+/**
+ * Return a list of all available profiles for a given connection manager.
+ *
+ * \param cmName Connection manager name.
+ * \return A list of all available profiles for a given connection manager.
+ */
+QList<ProfilePtr> ProfileManager::profilesForCM(const QString &cmName) const
+{
+ QList<ProfilePtr> ret;
+ foreach (const ProfilePtr &profile, mPriv->profiles) {
+ if (profile->cmName() == cmName) {
+ ret << profile;
+ }
+ }
+ return ret;
+}
+
+/**
+ * Return a list of all available profiles for a given \a protocol.
+ *
+ * \param protocolName Protocol name.
+ * \return A list of all available profiles for a given \a protocol.
+ */
+QList<ProfilePtr> ProfileManager::profilesForProtocol(
+ const QString &protocolName) const
+{
+ QList<ProfilePtr> ret;
+ foreach (const ProfilePtr &profile, mPriv->profiles) {
+ if (profile->protocolName() == protocolName) {
+ ret << profile;
+ }
+ }
+ return ret;
+}
+
+/**
+ * Return the profile for a given \a service.
+ *
+ * \param serviceName Service name.
+ * \return The profile for \a service.
+ */
+ProfilePtr ProfileManager::profileForService(const QString &serviceName) const
+{
+ return mPriv->profiles.value(serviceName);
+}
+
+void ProfileManager::onCmNamesRetrieved(Tp::PendingOperation *op)
+{
+ if (op->isError()) {
+ warning().nospace() << "Getting available CMs failed with " <<
+ op->errorName() << ":" << op->errorMessage();
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureFakeProfiles, false,
+ op->errorName(), op->errorMessage());
+ return;
+ }
+
+ PendingStringList *pendingCmNames = qobject_cast<PendingStringList *>(op);
+ QStringList cmNames(pendingCmNames->result());
+ if (cmNames.isEmpty()) {
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureFakeProfiles, true);
+ return;
+ }
+
+ QList<PendingOperation *> ops;
+ foreach (const QString &cmName, cmNames) {
+ ConnectionManagerPtr cm = ConnectionManager::create(mPriv->bus, cmName);
+ mPriv->cms.append(cm);
+ ops.append(cm->becomeReady());
+ }
+
+ PendingComposite *pc = new PendingComposite(ops, false, ProfileManagerPtr(this));
+ connect(pc,
+ SIGNAL(finished(Tp::PendingOperation *)),
+ SLOT(onCMsReady(Tp::PendingOperation *)));
+}
+
+void ProfileManager::onCMsReady(Tp::PendingOperation *op)
+{
+ if (op->isError()) {
+ warning() << "Failed introspecting all CMs, trying to create fake profiles anyway";
+ }
+
+ ProfilePtr profile;
+ foreach (const ConnectionManagerPtr &cm, mPriv->cms) {
+ if (!cm->isReady()) {
+ continue;
+ }
+
+ foreach (const QString &protocolName, cm->supportedProtocols()) {
+ /* check if there is a profile whose service name is protocolName, and if found,
+ * check if the profile is for cm, if not check if there is a profile whose service
+ * name is cm-protocolName, and if not found create one named cm-protocolName. */
+ profile = profileForService(protocolName);
+ if (profile && profile->cmName() == cm->name()) {
+ continue;
+ }
+
+ QString serviceName = QString(QLatin1String("%1-%2")).arg(cm->name()).arg(protocolName);
+ profile = profileForService(serviceName);
+ if (profile) {
+ continue;
+ }
+
+ profile = ProfilePtr(new Profile(
+ serviceName,
+ cm->name(),
+ protocolName,
+ cm->protocol(protocolName)));
+ mPriv->profiles.insert(serviceName, profile);
+ }
+ }
+
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureFakeProfiles, true);
+}
+
+} // Tp
diff --git a/TelepathyQt/profile-manager.h b/TelepathyQt/profile-manager.h
new file mode 100644
index 00000000..c5566339
--- /dev/null
+++ b/TelepathyQt/profile-manager.h
@@ -0,0 +1,75 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_profile_manager_h_HEADER_GUARD_
+#define _TelepathyQt_profile_manager_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Object>
+#include <TelepathyQt/Profile>
+#include <TelepathyQt/ReadyObject>
+#include <TelepathyQt/Types>
+
+#include <QDBusConnection>
+#include <QObject>
+
+namespace Tp
+{
+
+class PendingOperation;
+
+class TP_QT_EXPORT ProfileManager : public Object, public ReadyObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(ProfileManager);
+
+public:
+ static const Feature FeatureCore;
+ static const Feature FeatureFakeProfiles;
+
+ static ProfileManagerPtr create(const QDBusConnection &bus = QDBusConnection::sessionBus());
+
+ ~ProfileManager();
+
+ QList<ProfilePtr> profiles() const;
+ QList<ProfilePtr> profilesForCM(const QString &cmName) const;
+ QList<ProfilePtr> profilesForProtocol(const QString &protocolName) const;
+ ProfilePtr profileForService(const QString &serviceName) const;
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onCmNamesRetrieved(Tp::PendingOperation *op);
+ TP_QT_NO_EXPORT void onCMsReady(Tp::PendingOperation *op);
+
+private:
+ ProfileManager(const QDBusConnection &bus);
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/profile.cpp b/TelepathyQt/profile.cpp
new file mode 100644
index 00000000..ff487b75
--- /dev/null
+++ b/TelepathyQt/profile.cpp
@@ -0,0 +1,1216 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/Profile>
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/ManagerFile>
+#include <TelepathyQt/Utils>
+#include <TelepathyQt/ProtocolInfo>
+#include <TelepathyQt/ProtocolParameter>
+
+#include <QFile>
+#include <QFileInfo>
+#include <QStringList>
+#include <QXmlAttributes>
+#include <QXmlDefaultHandler>
+#include <QXmlInputSource>
+#include <QXmlSimpleReader>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT Profile::Private
+{
+ Private();
+
+ void setServiceName(const QString &serviceName);
+ void setFileName(const QString &fileName);
+
+ void lookupProfile();
+ bool parse(QFile *file);
+ void invalidate();
+
+ struct Data
+ {
+ Data();
+
+ void clear();
+
+ QString type;
+ QString provider;
+ QString name;
+ QString iconName;
+ QString cmName;
+ QString protocolName;
+ Profile::ParameterList parameters;
+ bool allowOtherPresences;
+ Profile::PresenceList presences;
+ RequestableChannelClassSpecList unsupportedChannelClassSpecs;
+ };
+
+ class XmlHandler;
+
+ QString serviceName;
+ bool valid;
+ bool fake;
+ bool allowNonIMType;
+ Data data;
+};
+
+Profile::Private::Data::Data()
+ : allowOtherPresences(false)
+{
+}
+
+void Profile::Private::Data::clear()
+{
+ type = QString();
+ provider = QString();
+ name = QString();
+ iconName = QString();
+ protocolName = QString();
+ parameters = Profile::ParameterList();
+ allowOtherPresences = false;
+ presences = Profile::PresenceList();
+ unsupportedChannelClassSpecs = RequestableChannelClassSpecList();
+}
+
+
+class TP_QT_NO_EXPORT Profile::Private::XmlHandler :
+ public QXmlDefaultHandler
+{
+public:
+ XmlHandler(const QString &serviceName, bool allowNonIMType, Profile::Private::Data *outputData);
+
+ bool startElement(const QString &namespaceURI, const QString &localName,
+ const QString &qName, const QXmlAttributes &attributes);
+ bool endElement(const QString &namespaceURI, const QString &localName,
+ const QString &qName);
+ bool characters(const QString &str);
+ bool fatalError(const QXmlParseException &exception);
+ QString errorString() const;
+
+private:
+ bool attributeValueAsBoolean(const QXmlAttributes &attributes,
+ const QString &qName);
+
+ QString mServiceName;
+ bool allowNonIMType;
+ Profile::Private::Data *mData;
+ QStack<QString> mElements;
+ QString mCurrentText;
+ Profile::Parameter mCurrentParameter;
+ RequestableChannelClass mCurrentCC;
+ QString mCurrentPropertyName;
+ QString mCurrentPropertyType;
+ QString mErrorString;
+ bool mMetServiceTag;
+
+ static const QString xmlNs;
+
+ static const QString elemService;
+ static const QString elemName;
+ static const QString elemParams;
+ static const QString elemParam;
+ static const QString elemPresences;
+ static const QString elemPresence;
+ static const QString elemUnsupportedCCs;
+ static const QString elemCC;
+ static const QString elemProperty;
+
+ static const QString elemAttrId;
+ static const QString elemAttrName;
+ static const QString elemAttrType;
+ static const QString elemAttrProvider;
+ static const QString elemAttrManager;
+ static const QString elemAttrProtocol;
+ static const QString elemAttrIcon;
+ static const QString elemAttrLabel;
+ static const QString elemAttrMandatory;
+ static const QString elemAttrAllowOthers;
+ static const QString elemAttrMessage;
+ static const QString elemAttrDisabled;
+};
+
+const QString Profile::Private::XmlHandler::xmlNs = QLatin1String("http://telepathy.freedesktop.org/wiki/service-profile-v1");
+
+const QString Profile::Private::XmlHandler::elemService = QLatin1String("service");
+const QString Profile::Private::XmlHandler::elemName = QLatin1String("name");
+const QString Profile::Private::XmlHandler::elemParams = QLatin1String("parameters");
+const QString Profile::Private::XmlHandler::elemParam = QLatin1String("parameter");
+const QString Profile::Private::XmlHandler::elemPresences = QLatin1String("presences");
+const QString Profile::Private::XmlHandler::elemPresence = QLatin1String("presence");
+const QString Profile::Private::XmlHandler::elemUnsupportedCCs = QLatin1String("unsupported-channel-classes");
+const QString Profile::Private::XmlHandler::elemCC = QLatin1String("channel-class");
+const QString Profile::Private::XmlHandler::elemProperty = QLatin1String("property");
+
+const QString Profile::Private::XmlHandler::elemAttrId = QLatin1String("id");
+const QString Profile::Private::XmlHandler::elemAttrName = QLatin1String("name");
+const QString Profile::Private::XmlHandler::elemAttrType = QLatin1String("type");
+const QString Profile::Private::XmlHandler::elemAttrProvider = QLatin1String("provider");
+const QString Profile::Private::XmlHandler::elemAttrManager = QLatin1String("manager");
+const QString Profile::Private::XmlHandler::elemAttrProtocol = QLatin1String("protocol");
+const QString Profile::Private::XmlHandler::elemAttrLabel = QLatin1String("label");
+const QString Profile::Private::XmlHandler::elemAttrMandatory = QLatin1String("mandatory");
+const QString Profile::Private::XmlHandler::elemAttrAllowOthers = QLatin1String("allow-others");
+const QString Profile::Private::XmlHandler::elemAttrIcon = QLatin1String("icon");
+const QString Profile::Private::XmlHandler::elemAttrMessage = QLatin1String("message");
+const QString Profile::Private::XmlHandler::elemAttrDisabled = QLatin1String("disabled");
+
+Profile::Private::XmlHandler::XmlHandler(const QString &serviceName,
+ bool allowNonIMType,
+ Profile::Private::Data *outputData)
+ : mServiceName(serviceName),
+ allowNonIMType(allowNonIMType),
+ mData(outputData),
+ mMetServiceTag(false)
+{
+}
+
+bool Profile::Private::XmlHandler::startElement(const QString &namespaceURI,
+ const QString &localName, const QString &qName,
+ const QXmlAttributes &attributes)
+{
+ if (!mMetServiceTag && qName != elemService) {
+ mErrorString = QLatin1String("the file is not a profile file");
+ return false;
+ }
+
+ if (namespaceURI != xmlNs) {
+ // ignore all elements with unknown xmlns
+ debug() << "Ignoring unknown xmlns" << namespaceURI;
+ return true;
+ }
+
+#define CHECK_ELEMENT_IS_CHILD_OF(parentElement) \
+ if (mElements.top() != parentElement) { \
+ mErrorString = QString(QLatin1String("element '%1' is not a " \
+ "child of element '%2'")) \
+ .arg(qName) \
+ .arg(parentElement); \
+ return false; \
+ }
+#define CHECK_ELEMENT_ATTRIBUTES_COUNT(value) \
+ if (attributes.count() != value) { \
+ mErrorString = QString(QLatin1String("element '%1' contains more " \
+ "than %2 attributes")) \
+ .arg(qName) \
+ .arg(value); \
+ return false; \
+ }
+#define CHECK_ELEMENT_HAS_ATTRIBUTE(attribute) \
+ if (attributes.index(attribute) == -1) { \
+ mErrorString = QString(QLatin1String("mandatory attribute '%1' " \
+ "missing on element '%2'")) \
+ .arg(attribute) \
+ .arg(qName); \
+ return false; \
+ }
+#define CHECK_ELEMENT_ATTRIBUTES(allowedAttrs) \
+ for (int i = 0; i < attributes.count(); ++i) { \
+ bool valid = false; \
+ QString attrName = attributes.qName(i); \
+ foreach (const QString &allowedAttr, allowedAttrs) { \
+ if (attrName == allowedAttr) { \
+ valid = true; \
+ break; \
+ } \
+ } \
+ if (!valid) { \
+ mErrorString = QString(QLatin1String("invalid attribute '%1' on " \
+ "element '%2'")) \
+ .arg(attrName) \
+ .arg(qName); \
+ return false; \
+ } \
+ }
+
+ if (qName == elemService) {
+ CHECK_ELEMENT_HAS_ATTRIBUTE(elemAttrId);
+ CHECK_ELEMENT_HAS_ATTRIBUTE(elemAttrType);
+ CHECK_ELEMENT_HAS_ATTRIBUTE(elemAttrManager);
+ CHECK_ELEMENT_HAS_ATTRIBUTE(elemAttrProtocol);
+
+ QStringList allowedAttrs = QStringList() <<
+ elemAttrId << elemAttrType << elemAttrManager <<
+ elemAttrProtocol << elemAttrProvider << elemAttrIcon;
+ CHECK_ELEMENT_ATTRIBUTES(allowedAttrs);
+
+ if (attributes.value(elemAttrId) != mServiceName) {
+ mErrorString = QString(QLatin1String("the '%1' attribute of the "
+ "element '%2' does not match the file name"))
+ .arg(elemAttrId)
+ .arg(elemService);
+ return false;
+ }
+
+ mMetServiceTag = true;
+ mData->type = attributes.value(elemAttrType);
+ if (mData->type != QLatin1String("IM") && !allowNonIMType) {
+ mErrorString = QString(QLatin1String("unknown value of element "
+ "'type': %1"))
+ .arg(mCurrentText);
+ return false;
+ }
+ mData->provider = attributes.value(elemAttrProvider);
+ mData->cmName = attributes.value(elemAttrManager);
+ mData->protocolName = attributes.value(elemAttrProtocol);
+ mData->iconName = attributes.value(elemAttrIcon);
+ } else if (qName == elemParams) {
+ CHECK_ELEMENT_IS_CHILD_OF(elemService);
+ CHECK_ELEMENT_ATTRIBUTES_COUNT(0);
+ } else if (qName == elemParam) {
+ CHECK_ELEMENT_IS_CHILD_OF(elemParams);
+ CHECK_ELEMENT_HAS_ATTRIBUTE(elemAttrName);
+ QStringList allowedAttrs = QStringList() << elemAttrName <<
+ elemAttrType << elemAttrMandatory << elemAttrLabel;
+ CHECK_ELEMENT_ATTRIBUTES(allowedAttrs);
+
+ QString paramType = attributes.value(elemAttrType);
+ if (paramType.isEmpty()) {
+ paramType = QLatin1String("s");
+ }
+ mCurrentParameter.setName(attributes.value(elemAttrName));
+ mCurrentParameter.setDBusSignature(QDBusSignature(paramType));
+ mCurrentParameter.setLabel(attributes.value(elemAttrLabel));
+ mCurrentParameter.setMandatory(attributeValueAsBoolean(attributes,
+ elemAttrMandatory));
+ } else if (qName == elemPresences) {
+ CHECK_ELEMENT_IS_CHILD_OF(elemService);
+ QStringList allowedAttrs = QStringList() << elemAttrAllowOthers;
+ CHECK_ELEMENT_ATTRIBUTES(allowedAttrs);
+
+ mData->allowOtherPresences = attributeValueAsBoolean(attributes,
+ elemAttrAllowOthers);
+ } else if (qName == elemPresence) {
+ CHECK_ELEMENT_IS_CHILD_OF(elemPresences);
+ CHECK_ELEMENT_HAS_ATTRIBUTE(elemAttrId);
+ QStringList allowedAttrs = QStringList() << elemAttrId <<
+ elemAttrLabel << elemAttrIcon << elemAttrMessage <<
+ elemAttrDisabled;
+ CHECK_ELEMENT_ATTRIBUTES(allowedAttrs);
+
+ mData->presences.append(Profile::Presence(
+ attributes.value(elemAttrId),
+ attributes.value(elemAttrLabel),
+ attributes.value(elemAttrIcon),
+ attributes.value(elemAttrMessage),
+ attributeValueAsBoolean(attributes, elemAttrDisabled)));
+ } else if (qName == elemUnsupportedCCs) {
+ CHECK_ELEMENT_IS_CHILD_OF(elemService);
+ CHECK_ELEMENT_ATTRIBUTES_COUNT(0);
+ } else if (qName == elemCC) {
+ CHECK_ELEMENT_IS_CHILD_OF(elemUnsupportedCCs);
+ CHECK_ELEMENT_ATTRIBUTES_COUNT(0);
+ } else if (qName == elemProperty) {
+ CHECK_ELEMENT_IS_CHILD_OF(elemCC);
+ CHECK_ELEMENT_ATTRIBUTES_COUNT(2);
+ CHECK_ELEMENT_HAS_ATTRIBUTE(elemAttrName);
+ CHECK_ELEMENT_HAS_ATTRIBUTE(elemAttrType);
+
+ mCurrentPropertyName = attributes.value(elemAttrName);
+ mCurrentPropertyType = attributes.value(elemAttrType);
+ } else {
+ if (qName != elemName) {
+ Tp::warning() << "Ignoring unknown element" << qName;
+ } else {
+ // check if we are inside <service>
+ CHECK_ELEMENT_IS_CHILD_OF(elemService);
+ // no element here supports attributes
+ CHECK_ELEMENT_ATTRIBUTES_COUNT(0);
+ }
+ }
+
+#undef CHECK_ELEMENT_IS_CHILD_OF
+#undef CHECK_ELEMENT_ATTRIBUTES_COUNT
+#undef CHECK_ELEMENT_HAS_ATTRIBUTE
+#undef CHECK_ELEMENT_ATTRIBUTES
+
+ mElements.push(qName);
+ mCurrentText.clear();
+ return true;
+}
+
+bool Profile::Private::XmlHandler::endElement(const QString &namespaceURI,
+ const QString &localName, const QString &qName)
+{
+ if (namespaceURI != xmlNs) {
+ // ignore all elements with unknown xmlns
+ debug() << "Ignoring unknown xmlns" << namespaceURI;
+ return true;
+ } else if (qName == elemName) {
+ mData->name = mCurrentText;
+ } else if (qName == elemParam) {
+ mCurrentParameter.setValue(ManagerFile::parseValueWithDBusSignature(mCurrentText,
+ mCurrentParameter.dbusSignature().signature()));
+ mData->parameters.append(Profile::Parameter(mCurrentParameter));
+ } else if (qName == elemCC) {
+ mData->unsupportedChannelClassSpecs.append(RequestableChannelClassSpec(mCurrentCC));
+ mCurrentCC.fixedProperties.clear();
+ } else if (qName == elemProperty) {
+ mCurrentCC.fixedProperties[mCurrentPropertyName] =
+ ManagerFile::parseValueWithDBusSignature(mCurrentText,
+ mCurrentPropertyType);
+ }
+
+ mElements.pop();
+ return true;
+}
+
+bool Profile::Private::XmlHandler::characters(const QString &str)
+{
+ mCurrentText += str;
+ return true;
+}
+
+bool Profile::Private::XmlHandler::fatalError(
+ const QXmlParseException &exception)
+{
+ mErrorString = QString(QLatin1String("parse error at line %1, column %2: "
+ "%3"))
+ .arg(exception.lineNumber())
+ .arg(exception.columnNumber())
+ .arg(exception.message());
+ return false;
+}
+
+QString Profile::Private::XmlHandler::errorString() const
+{
+ return mErrorString;
+}
+
+bool Profile::Private::XmlHandler::attributeValueAsBoolean(
+ const QXmlAttributes &attributes, const QString &qName)
+{
+ QString tmpStr = attributes.value(qName);
+ if (tmpStr == QLatin1String("1") ||
+ tmpStr == QLatin1String("true")) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+Profile::Private::Private()
+ : valid(false),
+ fake(false),
+ allowNonIMType(false)
+{
+}
+
+void Profile::Private::setServiceName(const QString &serviceName_)
+{
+ invalidate();
+
+ allowNonIMType = false;
+ serviceName = serviceName_;
+ lookupProfile();
+}
+
+void Profile::Private::setFileName(const QString &fileName)
+{
+ invalidate();
+
+ allowNonIMType = true;
+ QFileInfo fi(fileName);
+ serviceName = fi.baseName();
+
+ debug() << "Loading profile file" << fileName;
+
+ QFile file(fileName);
+ if (!file.exists()) {
+ warning() << QString(QLatin1String("Error parsing profile file %1: file does not exist"))
+ .arg(file.fileName());
+ return;
+ }
+
+ if (!file.open(QFile::ReadOnly)) {
+ warning() << QString(QLatin1String("Error parsing profile file %1: "
+ "cannot open file for readonly access"))
+ .arg(file.fileName());
+ return;
+ }
+
+ if (parse(&file)) {
+ debug() << "Profile file" << fileName << "loaded successfully";
+ }
+}
+
+void Profile::Private::lookupProfile()
+{
+ debug() << "Searching profile for service" << serviceName;
+
+ QStringList searchDirs = Profile::searchDirs();
+ bool found = false;
+ foreach (const QString searchDir, searchDirs) {
+ QString fileName = searchDir + serviceName + QLatin1String(".profile");
+
+ QFile file(fileName);
+ if (!file.exists()) {
+ continue;
+ }
+
+ if (!file.open(QFile::ReadOnly)) {
+ continue;
+ }
+
+ if (parse(&file)) {
+ debug() << "Profile for service" << serviceName << "found:" << fileName;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ debug() << "Cannot find valid profile for service" << serviceName;
+ }
+}
+
+bool Profile::Private::parse(QFile *file)
+{
+ invalidate();
+
+ fake = false;
+ QFileInfo fi(file->fileName());
+ XmlHandler xmlHandler(serviceName, allowNonIMType, &data);
+
+ QXmlSimpleReader xmlReader;
+ xmlReader.setContentHandler(&xmlHandler);
+ xmlReader.setErrorHandler(&xmlHandler);
+
+ QXmlInputSource xmlInputSource(file);
+ if (!xmlReader.parse(xmlInputSource)) {
+ warning() << QString(QLatin1String("Error parsing profile file %1: %2"))
+ .arg(file->fileName())
+ .arg(xmlHandler.errorString());
+ invalidate();
+ return false;
+ }
+
+ valid = true;
+ return true;
+}
+
+void Profile::Private::invalidate()
+{
+ valid = false;
+ data.clear();
+}
+
+/**
+ * \class Profile
+ * \ingroup utils
+ * \headerfile TelepathyQt/profile.h <TelepathyQt/Profile>
+ *
+ * \brief The Profile class provides an easy way to read Telepathy profile
+ * files according to http://telepathy.freedesktop.org/wiki/service-profile-v1.
+ *
+ * Note that profiles with xml element <type> different than "IM" are considered
+ * invalid.
+ */
+
+/**
+ * Create a new Profile object used to read .profiles compliant files.
+ *
+ * \param serviceName The profile service name.
+ * \return A ProfilePtr object pointing to the newly created Profile object.
+ */
+ProfilePtr Profile::createForServiceName(const QString &serviceName)
+{
+ ProfilePtr profile = ProfilePtr(new Profile());
+ profile->setServiceName(serviceName);
+ return profile;
+}
+
+/**
+ * Create a new Profile object used to read .profiles compliant files.
+ *
+ * \param fileName The profile file name.
+ * \return A ProfilePtr object pointing to the newly created Profile object.
+ */
+ProfilePtr Profile::createForFileName(const QString &fileName)
+{
+ ProfilePtr profile = ProfilePtr(new Profile());
+ profile->setFileName(fileName);
+ return profile;
+}
+
+/**
+ * Construct a new Profile object used to read .profiles compliant files.
+ *
+ * \param serviceName The profile service name.
+ */
+Profile::Profile()
+ : mPriv(new Private())
+{
+}
+
+/**
+ * Construct a fake profile using the given \a serviceName, \a cmName,
+ * \a protocolName and \a protocolInfo.
+ *
+ * - isFake() will return \c true
+ * - type() will return "IM"
+ * - provider() will return an empty string
+ * - serviceName() will return \a serviceName
+ * - name() and protocolName() will return \a protocolName
+ * - iconName() will return "im-\a protocolName"
+ * - cmName() will return \a cmName
+ * - parameters() will return a list matching CM default parameters
+ * - presences() will return an empty list and allowOtherPresences will return
+ * \c true, meaning that CM presences should be used
+ * - unsupportedChannelClassSpecs() will return an empty list
+ *
+ * \param serviceName The service name.
+ * \param cmName The connection manager name.
+ * \param protocolName The protocol name.
+ * \param protocolInfo The protocol info for the protocol \a protocolName.
+ */
+Profile::Profile(const QString &serviceName, const QString &cmName,
+ const QString &protocolName, const ProtocolInfo &protocolInfo)
+ : mPriv(new Private())
+{
+ mPriv->serviceName = serviceName;
+
+ mPriv->data.type = QString(QLatin1String("IM"));
+ // provider is empty
+ mPriv->data.name = protocolName;
+ mPriv->data.iconName = QString(QLatin1String("im-%1")).arg(protocolName);
+ mPriv->data.cmName = cmName;
+ mPriv->data.protocolName = protocolName;
+
+ foreach (const ProtocolParameter &protocolParam, protocolInfo.parameters()) {
+ if (!protocolParam.defaultValue().isNull()) {
+ mPriv->data.parameters.append(Profile::Parameter(
+ protocolParam.name(),
+ protocolParam.dbusSignature(),
+ protocolParam.defaultValue(),
+ QString(), // label
+ false)); // mandatory
+ }
+ }
+
+ // parameters will be the same as CM parameters
+ // set allow others to true meaning that the standard CM presences are
+ // supported
+ mPriv->data.allowOtherPresences = true;
+ // presences will be the same as CM presences
+ // unsupported channel classes is empty
+
+ mPriv->valid = true;
+ mPriv->fake = true;
+}
+
+/**
+ * Class destructor.
+ */
+Profile::~Profile()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the unique name of the service to which this profile applies.
+ *
+ * \return The unique name of the service.
+ */
+QString Profile::serviceName() const
+{
+ return mPriv->serviceName;
+}
+
+/**
+ * Return whether this profile is valid.
+ *
+ * \return \c true if valid, otherwise \c false.
+ */
+bool Profile::isValid() const
+{
+ return mPriv->valid;
+}
+
+/**
+ * Return whether this profile is fake.
+ *
+ * Fake profiles are profiles created for services not providing a .profile
+ * file.
+ *
+ * \return \c true if fake, otherwise \c false.
+ */
+bool Profile::isFake() const
+{
+ return mPriv->fake;
+}
+
+/**
+ * Return the type of the service to which this profile applies.
+ *
+ * In general, services of interest of Telepathy should be of type 'IM'.
+ * Other service types exist but are unlikely to affect Telepathy in any way.
+ *
+ * \return The type of the service.
+ */
+QString Profile::type() const
+{
+ return mPriv->data.type;
+}
+
+/**
+ * Return the name of the vendor/organisation/provider who actually runs the
+ * service to which this profile applies.
+ *
+ * \return The provider of the service.
+ */
+QString Profile::provider() const
+{
+ return mPriv->data.provider;
+}
+
+/**
+ * Return the human-readable name for the service to which this profile applies.
+ *
+ * \return The Human-readable name of the service.
+ */
+QString Profile::name() const
+{
+ return mPriv->data.name;
+}
+
+/**
+ * Return the base name of the icon for the service to which this profile
+ * applies.
+ *
+ * \return The base name of the icon for the service.
+ */
+QString Profile::iconName() const
+{
+ return mPriv->data.iconName;
+}
+
+/**
+ * Return the connection manager name for the service to which this profile
+ * applies.
+ *
+ * \return The connection manager name for the service.
+ */
+QString Profile::cmName() const
+{
+ return mPriv->data.cmName;
+}
+
+/**
+ * Return the protocol name for the service to which this profile applies.
+ *
+ * \return The protocol name for the service.
+ */
+QString Profile::protocolName() const
+{
+ return mPriv->data.protocolName;
+}
+
+/**
+ * Return a list of parameters defined for the service to which this profile
+ * applies.
+ *
+ * \return A list of Profile::Parameter.
+ */
+Profile::ParameterList Profile::parameters() const
+{
+ return mPriv->data.parameters;
+}
+
+/**
+ * Return whether this profile defines the parameter named \a name.
+ *
+ * \return \c true if parameter is defined, otherwise \c false.
+ */
+bool Profile::hasParameter(const QString &name) const
+{
+ foreach (const Parameter &parameter, mPriv->data.parameters) {
+ if (parameter.name() == name) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Return the parameter for a given \a name.
+ *
+ * \return A Profile::Parameter.
+ */
+Profile::Parameter Profile::parameter(const QString &name) const
+{
+ foreach (const Parameter &parameter, mPriv->data.parameters) {
+ if (parameter.name() == name) {
+ return parameter;
+ }
+ }
+ return Profile::Parameter();
+}
+
+/**
+ * Return whether the standard CM presences not defined in presences() are
+ * supported.
+ *
+ * \return \c true if standard CM presences are supported, otherwise \c false.
+ */
+bool Profile::allowOtherPresences() const
+{
+ return mPriv->data.allowOtherPresences;
+}
+
+/**
+ * Return a list of presences defined for the service to which this profile
+ * applies.
+ *
+ * \return A list of Profile::Presence.
+ */
+Profile::PresenceList Profile::presences() const
+{
+ return mPriv->data.presences;
+}
+
+/**
+ * Return whether this profile defines the presence with id \a id.
+ *
+ * \return \c true if presence is defined, otherwise \c false.
+ */
+bool Profile::hasPresence(const QString &id) const
+{
+ foreach (const Presence &presence, mPriv->data.presences) {
+ if (presence.id() == id) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Return the presence for a given \a id.
+ *
+ * \return A Profile::Presence.
+ */
+Profile::Presence Profile::presence(const QString &id) const
+{
+ foreach (const Presence &presence, mPriv->data.presences) {
+ if (presence.id() == id) {
+ return presence;
+ }
+ }
+ return Profile::Presence();
+}
+
+/**
+ * A list of channel classes not supported by the service to which this profile
+ * applies.
+ *
+ * \return A list of RequestableChannelClassSpec.
+ */
+RequestableChannelClassSpecList Profile::unsupportedChannelClassSpecs() const
+{
+ return mPriv->data.unsupportedChannelClassSpecs;
+}
+
+void Profile::setServiceName(const QString &serviceName)
+{
+ mPriv->setServiceName(serviceName);
+}
+
+void Profile::setFileName(const QString &fileName)
+{
+ mPriv->setFileName(fileName);
+}
+
+QStringList Profile::searchDirs()
+{
+ QStringList ret;
+
+ QString xdgDataHome = QString::fromLocal8Bit(qgetenv("XDG_DATA_HOME"));
+ if (xdgDataHome.isEmpty()) {
+ ret << QDir::homePath() + QLatin1String("/.local/share/data/telepathy/profiles/");
+ } else {
+ ret << xdgDataHome + QLatin1String("/telepathy/profiles/");
+ }
+
+ QString xdgDataDirsEnv = QString::fromLocal8Bit(qgetenv("XDG_DATA_DIRS"));
+ if (xdgDataDirsEnv.isEmpty()) {
+ ret << QLatin1String("/usr/local/share/telepathy/profiles/");
+ ret << QLatin1String("/usr/share/telepathy/profiles/");
+ } else {
+ QStringList xdgDataDirs = xdgDataDirsEnv.split(QLatin1Char(':'));
+ foreach (const QString xdgDataDir, xdgDataDirs) {
+ ret << xdgDataDir + QLatin1String("/telepathy/profiles/");
+ }
+ }
+
+ return ret;
+}
+
+
+struct TP_QT_NO_EXPORT Profile::Parameter::Private
+{
+ QString name;
+ QDBusSignature dbusSignature;
+ QVariant value;
+ QString label;
+ bool mandatory;
+};
+
+/**
+ * \class Profile::Parameter
+ * \ingroup utils
+ * \headerfile TelepathyQt/profile.h <TelepathyQt/Profile>
+ *
+ * \brief The Profile::Parameter class represents a parameter defined in
+ * .profile files.
+ */
+
+/**
+ * Construct a new Profile::Parameter object.
+ */
+Profile::Parameter::Parameter()
+ : mPriv(new Private)
+{
+ mPriv->mandatory = false;
+}
+
+/**
+ * Construct a new Profile::Parameter object that is a copy of \a other.
+ */
+Profile::Parameter::Parameter(const Parameter &other)
+ : mPriv(new Private)
+{
+ mPriv->name = other.mPriv->name;
+ mPriv->dbusSignature = other.mPriv->dbusSignature;
+ mPriv->value = other.mPriv->value;
+ mPriv->label = other.mPriv->label;
+ mPriv->mandatory = other.mPriv->mandatory;
+}
+
+/**
+ * Construct a new Profile::Parameter object.
+ *
+ * \param name The parameter name.
+ * \param dbusSignature The parameter D-Bus signature.
+ * \param value The parameter value.
+ * \param label The parameter label.
+ * \param mandatory Whether this parameter is mandatory.
+ */
+Profile::Parameter::Parameter(const QString &name,
+ const QDBusSignature &dbusSignature,
+ const QVariant &value,
+ const QString &label,
+ bool mandatory)
+ : mPriv(new Private)
+{
+ mPriv->name = name;
+ mPriv->dbusSignature = dbusSignature;
+ mPriv->value = value;
+ mPriv->label = label;
+ mPriv->mandatory = mandatory;
+}
+
+/**
+ * Class destructor.
+ */
+Profile::Parameter::~Parameter()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the name of this parameter.
+ *
+ * \return The name of this parameter.
+ */
+QString Profile::Parameter::name() const
+{
+ return mPriv->name;
+}
+
+void Profile::Parameter::setName(const QString &name)
+{
+ mPriv->name = name;
+}
+
+/**
+ * Return the D-Bus signature of this parameter.
+ *
+ * \return The D-Bus signature of this parameter.
+ */
+QDBusSignature Profile::Parameter::dbusSignature() const
+{
+ return mPriv->dbusSignature;
+}
+
+void Profile::Parameter::setDBusSignature(const QDBusSignature &dbusSignature)
+{
+ mPriv->dbusSignature = dbusSignature;
+}
+
+/**
+ * Return the QVariant::Type of this parameter, constructed using
+ * dbusSignature().
+ *
+ * \return The QVariant::Type of this parameter.
+ */
+QVariant::Type Profile::Parameter::type() const
+{
+ return ManagerFile::variantTypeFromDBusSignature(mPriv->dbusSignature.signature());
+}
+
+/**
+ * Return the value of this parameter.
+ *
+ * If mandatory() returns \c true, the value must not be modified and should be
+ * used as is when creating accounts for this profile.
+ *
+ * \return The value of this parameter.
+ */
+QVariant Profile::Parameter::value() const
+{
+ return mPriv->value;
+}
+
+void Profile::Parameter::setValue(const QVariant &value)
+{
+ mPriv->value = value;
+}
+
+/**
+ * Return the human-readable label of this parameter.
+ *
+ * \return The human-readable label of this parameter.
+ */
+QString Profile::Parameter::label() const
+{
+ return mPriv->label;
+}
+
+void Profile::Parameter::setLabel(const QString &label)
+{
+ mPriv->label = label;
+}
+
+/**
+ * Return whether this parameter is mandatory, or whether the value returned by
+ * value() should be used as is when creating accounts for this profile.
+ *
+ * \return \c true if mandatory, otherwise \c false.
+ */
+bool Profile::Parameter::isMandatory() const
+{
+ return mPriv->mandatory;
+}
+
+void Profile::Parameter::setMandatory(bool mandatory)
+{
+ mPriv->mandatory = mandatory;
+}
+
+Profile::Parameter &Profile::Parameter::operator=(const Profile::Parameter &other)
+{
+ mPriv->name = other.mPriv->name;
+ mPriv->dbusSignature = other.mPriv->dbusSignature;
+ mPriv->value = other.mPriv->value;
+ mPriv->label = other.mPriv->label;
+ mPriv->mandatory = other.mPriv->mandatory;
+ return *this;
+}
+
+
+struct TP_QT_NO_EXPORT Profile::Presence::Private
+{
+ QString id;
+ QString label;
+ QString iconName;
+ QString message;
+ bool disabled;
+};
+
+/**
+ * \class Profile::Presence
+ * \ingroup utils
+ * \headerfile TelepathyQt/profile.h <TelepathyQt/Profile>
+ *
+ * \brief The Profile::Presence class represents a presence defined in
+ * .profile files.
+ */
+
+/**
+ * Construct a new Profile::Presence object.
+ */
+Profile::Presence::Presence()
+ : mPriv(new Private)
+{
+ mPriv->disabled = false;
+}
+
+/**
+ * Construct a new Profile::Presence object that is a copy of \a other.
+ */
+Profile::Presence::Presence(const Presence &other)
+ : mPriv(new Private)
+{
+ mPriv->id = other.mPriv->id;
+ mPriv->label = other.mPriv->label;
+ mPriv->iconName = other.mPriv->iconName;
+ mPriv->message = other.mPriv->message;
+ mPriv->disabled = other.mPriv->disabled;
+}
+
+/**
+ * Construct a new Profile::Presence object.
+ *
+ * \param id The presence id.
+ * \param label The presence label.
+ * \param iconName The presence icon name.
+ * \param message The presence message.
+ * \param disabled Whether this presence is supported.
+ */
+Profile::Presence::Presence(const QString &id,
+ const QString &label,
+ const QString &iconName,
+ const QString &message,
+ bool disabled)
+ : mPriv(new Private)
+{
+ mPriv->id = id;
+ mPriv->label = label;
+ mPriv->iconName = iconName;
+ mPriv->message = message;
+ mPriv->disabled = disabled;
+}
+
+/**
+ * Class destructor.
+ */
+Profile::Presence::~Presence()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the Telepathy presence id for this presence.
+ *
+ * \return The Telepathy presence id for this presence.
+ */
+QString Profile::Presence::id() const
+{
+ return mPriv->id;
+}
+
+void Profile::Presence::setId(const QString &id)
+{
+ mPriv->id = id;
+}
+
+/**
+ * Return the label that should be used for this presence.
+ *
+ * \return The label for this presence.
+ */
+QString Profile::Presence::label() const
+{
+ return mPriv->label;
+}
+
+void Profile::Presence::setLabel(const QString &label)
+{
+ mPriv->label = label;
+}
+
+/**
+ * Return the icon name of this presence.
+ *
+ * \return The icon name of this presence.
+ */
+QString Profile::Presence::iconName() const
+{
+ return mPriv->iconName;
+}
+
+void Profile::Presence::setIconName(const QString &iconName)
+{
+ mPriv->iconName = iconName;
+}
+
+/**
+ * Return whether user-defined text-message can be attached to this presence.
+ *
+ * \return \c true if user-defined text-message can be attached to this presence, \c false
+ * otherwise.
+ */
+bool Profile::Presence::canHaveStatusMessage() const
+{
+ if (mPriv->message == QLatin1String("1") ||
+ mPriv->message == QLatin1String("true")) {
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * \deprecated Use canHaveStatusMessage() instead.
+ */
+QString Profile::Presence::message() const
+{
+ return mPriv->message;
+}
+
+void Profile::Presence::setMessage(const QString &message)
+{
+ mPriv->message = message;
+}
+
+/**
+ * Return whether this presence is supported for the service to which this
+ * profile applies.
+ *
+ * \return \c true if supported, otherwise \c false.
+ */
+bool Profile::Presence::isDisabled() const
+{
+ return mPriv->disabled;
+}
+
+void Profile::Presence::setDisabled(bool disabled)
+{
+ mPriv->disabled = disabled;
+}
+
+Profile::Presence &Profile::Presence::operator=(const Profile::Presence &other)
+{
+ mPriv->id = other.mPriv->id;
+ mPriv->label = other.mPriv->label;
+ mPriv->iconName = other.mPriv->iconName;
+ mPriv->message = other.mPriv->message;
+ mPriv->disabled = other.mPriv->disabled;
+ return *this;
+}
+
+} // Tp
diff --git a/TelepathyQt/profile.h b/TelepathyQt/profile.h
new file mode 100644
index 00000000..bce78969
--- /dev/null
+++ b/TelepathyQt/profile.h
@@ -0,0 +1,176 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_profile_h_HEADER_GUARD_
+#define _TelepathyQt_profile_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/RequestableChannelClassSpec>
+#include <TelepathyQt/Types>
+
+#include <QDBusSignature>
+#include <QObject>
+#include <QString>
+#include <QVariant>
+
+namespace Tp
+{
+
+class ProtocolInfo;
+
+class TP_QT_EXPORT Profile : public RefCounted
+{
+ Q_DISABLE_COPY(Profile);
+
+public:
+ static ProfilePtr createForServiceName(const QString &serviceName);
+ static ProfilePtr createForFileName(const QString &fileName);
+
+ ~Profile();
+
+ QString serviceName() const;
+
+ bool isValid() const;
+
+ bool isFake() const;
+
+ QString type() const;
+ QString provider() const;
+ QString name() const;
+ QString iconName() const;
+ QString cmName() const;
+ QString protocolName() const;
+
+ class Parameter
+ {
+ public:
+ Parameter();
+ Parameter(const Parameter &other);
+ Parameter(const QString &name,
+ const QDBusSignature &dbusSignature,
+ const QVariant &value,
+ const QString &label,
+ bool mandatory);
+ ~Parameter();
+
+ QString name() const;
+ QDBusSignature dbusSignature() const;
+ QVariant::Type type() const;
+ QVariant value() const;
+ QString label() const;
+
+ bool isMandatory() const;
+
+ // TODO Add matches(Tp::Presence) method
+
+ Parameter &operator=(const Parameter &other);
+
+ private:
+ friend class Profile;
+
+ TP_QT_NO_EXPORT void setName(const QString &name);
+ TP_QT_NO_EXPORT void setDBusSignature(const QDBusSignature &dbusSignature);
+ TP_QT_NO_EXPORT void setValue(const QVariant &value);
+ TP_QT_NO_EXPORT void setLabel(const QString &label);
+ TP_QT_NO_EXPORT void setMandatory(bool mandatory);
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+ };
+ typedef QList<Parameter> ParameterList;
+
+ ParameterList parameters() const;
+ bool hasParameter(const QString &name) const;
+ Parameter parameter(const QString &name) const;
+
+ class Presence
+ {
+ public:
+ Presence();
+ Presence(const Presence &other);
+ Presence(const QString &id,
+ const QString &label,
+ const QString &iconName,
+ const QString &message,
+ bool disabled);
+ ~Presence();
+
+ QString id() const;
+ QString label() const;
+ QString iconName() const;
+ bool canHaveStatusMessage() const;
+ TP_QT_DEPRECATED QString message() const;
+
+ bool isDisabled() const;
+
+ Presence &operator=(const Presence &other);
+
+ private:
+ friend class Profile;
+
+ TP_QT_NO_EXPORT void setId(const QString &id);
+ TP_QT_NO_EXPORT void setLabel(const QString &label);
+ TP_QT_NO_EXPORT void setIconName(const QString &iconName);
+ TP_QT_NO_EXPORT void setMessage(const QString &message);
+ TP_QT_NO_EXPORT void setDisabled(bool disabled);
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+ };
+ typedef QList<Presence> PresenceList;
+
+ bool allowOtherPresences() const;
+ PresenceList presences() const;
+ bool hasPresence(const QString &id) const;
+ Presence presence(const QString &id) const;
+
+ RequestableChannelClassSpecList unsupportedChannelClassSpecs() const;
+
+private:
+ friend class Account;
+ friend class ProfileManager;
+
+ TP_QT_NO_EXPORT Profile();
+ TP_QT_NO_EXPORT Profile(const QString &serviceName, const QString &cmName,
+ const QString &protocolName, const ProtocolInfo &protocolInfo);
+
+ TP_QT_NO_EXPORT void setServiceName(const QString &serviceName);
+ TP_QT_NO_EXPORT void setFileName(const QString &fileName);
+
+ TP_QT_NO_EXPORT static QStringList searchDirs();
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+Q_DECLARE_METATYPE(Tp::Profile::Parameter);
+Q_DECLARE_METATYPE(Tp::Profile::Presence);
+
+#endif
diff --git a/TelepathyQt/properties.cpp b/TelepathyQt/properties.cpp
new file mode 100644
index 00000000..1b61da47
--- /dev/null
+++ b/TelepathyQt/properties.cpp
@@ -0,0 +1,26 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/Properties>
+
+#include "TelepathyQt/_gen/cli-properties-body.hpp"
+#include "TelepathyQt/_gen/cli-properties.moc.hpp"
diff --git a/TelepathyQt/properties.h b/TelepathyQt/properties.h
new file mode 100644
index 00000000..3d23199b
--- /dev/null
+++ b/TelepathyQt/properties.h
@@ -0,0 +1,51 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_properties_h_HEADER_GUARD_
+#define _TelepathyQt_properties_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+/**
+ * \addtogroup clientsideproxies Client-side proxies
+ *
+ * Proxy objects representing remote service objects accessed via D-Bus.
+ *
+ * In addition to providing direct access to methods, signals and properties
+ * exported by the remote objects, some of these proxies offer features like
+ * automatic inspection of remote object capabilities, property tracking,
+ * backwards compatibility helpers for older services and other utilities.
+ */
+
+/**
+ * \defgroup clientprops Telepathy Properties proxy
+ * \ingroup clientsideproxies
+ *
+ * Proxy object representing the Telepathy Properties interface on remote
+ * objects.
+ */
+
+#include <TelepathyQt/_gen/cli-properties.h>
+
+#endif
diff --git a/TelepathyQt/properties.xml b/TelepathyQt/properties.xml
new file mode 100644
index 00000000..2f59b89c
--- /dev/null
+++ b/TelepathyQt/properties.xml
@@ -0,0 +1,9 @@
+<tp:spec
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<tp:title>The Telepathy properties interface</tp:title>
+
+<xi:include href="../spec/Properties_Interface.xml"/>
+
+</tp:spec>
diff --git a/TelepathyQt/protocol-info.cpp b/TelepathyQt/protocol-info.cpp
new file mode 100644
index 00000000..385ea828
--- /dev/null
+++ b/TelepathyQt/protocol-info.cpp
@@ -0,0 +1,374 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/ProtocolInfo>
+
+#include <TelepathyQt/ConnectionCapabilities>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT ProtocolInfo::Private : public QSharedData
+{
+ Private()
+ {
+ }
+
+ Private(const QString &cmName, const QString &name)
+ : cmName(cmName),
+ name(name),
+ iconName(QString(QLatin1String("im-%1")).arg(name))
+ {
+ }
+
+ QString cmName;
+ QString name;
+ ProtocolParameterList params;
+ ConnectionCapabilities caps;
+ QString vcardField;
+ QString englishName;
+ QString iconName;
+ PresenceSpecList statuses;
+ AvatarSpec avatarRequirements;
+};
+
+/**
+ * \class ProtocolInfo
+ * \ingroup clientcm
+ * \headerfile TelepathyQt/protocol-info.h <TelepathyQt/ProtocolInfo>
+ *
+ * \brief The ProtocolInfo class represents a <a
+ * href="http://telepathy.freedesktop.org/spec/Protocol.html">Telepathy Protocol</a>.
+ */
+
+ProtocolInfo::ProtocolInfo()
+{
+}
+
+/**
+ * Construct a new ProtocolInfo object.
+ *
+ * \param cmName Connection manager name.
+ * \param name Protocol name.
+ */
+ProtocolInfo::ProtocolInfo(const QString &cmName, const QString &name)
+ : mPriv(new Private(cmName, name))
+{
+}
+
+ProtocolInfo::ProtocolInfo(const ProtocolInfo &other)
+ : mPriv(other.mPriv)
+{
+}
+
+/**
+ * Class destructor.
+ */
+ProtocolInfo::~ProtocolInfo()
+{
+}
+
+ProtocolInfo &ProtocolInfo::operator=(const ProtocolInfo &other)
+{
+ this->mPriv = other.mPriv;
+ return *this;
+}
+
+/**
+ * Return the short name of the connection manager (e.g. "gabble") for this protocol.
+ *
+ * \return The name of the connection manager for this protocol.
+ */
+QString ProtocolInfo::cmName() const
+{
+ if (!isValid()) {
+ return QString();
+ }
+
+ return mPriv->cmName;
+}
+
+/**
+ * Return the string identifying this protocol as described in the \telepathy_spec
+ * (e.g. "jabber").
+ *
+ * This identifier is not intended to be displayed to users directly; user
+ * interfaces are responsible for mapping them to localized strings.
+ *
+ * \return A string identifying this protocol.
+ */
+QString ProtocolInfo::name() const
+{
+ if (!isValid()) {
+ return QString();
+ }
+
+ return mPriv->name;
+}
+
+/**
+ * Return all supported parameters for this protocol. The parameters' names
+ * may either be the well-known strings specified by the \telepathy_spec
+ * (e.g. "account" and "password"), or implementation-specific strings.
+ *
+ * \return A list of parameters for this protocol.
+ */
+ProtocolParameterList ProtocolInfo::parameters() const
+{
+ if (!isValid()) {
+ return ProtocolParameterList();
+ }
+
+ return mPriv->params;
+}
+
+/**
+ * Return whether a given parameter can be passed to the connection
+ * manager when creating a connection to this protocol.
+ *
+ * \param name The name of a parameter.
+ * \return true if the given parameter exists.
+ */
+bool ProtocolInfo::hasParameter(const QString &name) const
+{
+ if (!isValid()) {
+ return false;
+ }
+
+ foreach (const ProtocolParameter &param, mPriv->params) {
+ if (param.name() == name) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Return whether it might be possible to register new accounts on this
+ * protocol, by setting the special parameter named
+ * <code>register</code> to <code>true</code>.
+ *
+ * \return The same thing as hasParameter("register").
+ * \sa hasParameter()
+ */
+bool ProtocolInfo::canRegister() const
+{
+ if (!isValid()) {
+ return false;
+ }
+
+ return hasParameter(QLatin1String("register"));
+}
+
+/**
+ * Return the capabilities that are expected to be available from a connection
+ * to this protocol, i.e. those for which Connection::createChannel() can
+ * reasonably be expected to succeed.
+ * User interfaces can use this information to show or hide UI components.
+ *
+ * @return An object representing the capabilities expected to be available from
+ * a connection to this protocol.
+ */
+ConnectionCapabilities ProtocolInfo::capabilities() const
+{
+ if (!isValid()) {
+ return ConnectionCapabilities();
+ }
+
+ return mPriv->caps;
+}
+
+/**
+ * Return the name of the most common vCard field used for this protocol's
+ * contact identifiers, normalized to lower case.
+ *
+ * One valid use of this field is to answer the question: given a contact's
+ * vCard containing an X-JABBER field, how can you communicate with the contact?
+ * By iterating through protocols looking for an x-jabber VCardField, one can
+ * build up a list of protocols that handle x-jabber, then offer the user a list
+ * of accounts for those protocols and/or the option to create a new account for
+ * one of those protocols.
+ * It is not necessarily valid to interpret contacts' identifiers as values of
+ * this vCard field. For instance, telepathy-sofiasip supports contacts whose
+ * identifiers are of the form sip:jenny@example.com or tel:8675309, which would
+ * not normally both be represented by any single vCard field.
+ *
+ * \return The most common vCard field used for this protocol's contact
+ * identifiers, or an empty string if there is no such field.
+ */
+QString ProtocolInfo::vcardField() const
+{
+ if (!isValid()) {
+ return QString();
+ }
+
+ return mPriv->vcardField;
+}
+
+/**
+ * Return the English-language name of this protocol, such as "AIM" or "Yahoo!".
+ *
+ * The name can be used as a fallback if an application doesn't have a localized name for this
+ * protocol.
+ *
+ * If the manager file or the CM service doesn't specify the english name, it is inferred from this
+ * protocol name, such that for example "google-talk" becomes "Google Talk", but "local-xmpp"
+ * becomes "Local Xmpp".
+ *
+ * \return An English-language name for this protocol.
+ */
+QString ProtocolInfo::englishName() const
+{
+ if (!isValid()) {
+ return QString();
+ }
+
+ return mPriv->englishName;
+}
+
+/**
+ * Return the name of an icon for this protocol in the system's icon theme, such as "im-msn".
+ *
+ * If the manager file or the CM service doesn't specify the icon name, "im-<protocolname>" is
+ * assumed.
+ *
+ * \return The likely name of an icon for this protocol.
+ */
+QString ProtocolInfo::iconName() const
+{
+ if (!isValid()) {
+ return QString();
+ }
+
+ return mPriv->iconName;
+}
+
+/**
+ * Return a list of PresenceSpec representing the possible presence statuses
+ * from a connection to this protocol.
+ *
+ * \return A list of PresenceSpec representing the possible presence statuses
+ * from a connection to this protocol.
+ */
+PresenceSpecList ProtocolInfo::allowedPresenceStatuses() const
+{
+ if (!isValid()) {
+ return PresenceSpecList();
+ }
+
+ return mPriv->statuses;
+}
+
+/**
+ * Return the requirements (size limits, supported MIME types, etc)
+ * for avatars used on to this protocol.
+ *
+ * \return The requirements for avatars used on this protocol.
+ */
+AvatarSpec ProtocolInfo::avatarRequirements() const
+{
+ if (!isValid()) {
+ return AvatarSpec();
+ }
+
+ return mPriv->avatarRequirements;
+}
+
+void ProtocolInfo::addParameter(const ParamSpec &spec)
+{
+ if (!isValid()) {
+ mPriv = new Private;
+ }
+
+ QVariant defaultValue;
+ if (spec.flags & ConnMgrParamFlagHasDefault) {
+ defaultValue = spec.defaultValue.variant();
+ }
+
+ uint flags = spec.flags;
+ if (spec.name.endsWith(QLatin1String("password"))) {
+ flags |= ConnMgrParamFlagSecret;
+ }
+
+ ProtocolParameter param(spec.name,
+ QDBusSignature(spec.signature),
+ defaultValue,
+ (ConnMgrParamFlag) flags);
+ mPriv->params.append(param);
+}
+
+void ProtocolInfo::setVCardField(const QString &vcardField)
+{
+ if (!isValid()) {
+ mPriv = new Private;
+ }
+
+ mPriv->vcardField = vcardField;
+}
+
+void ProtocolInfo::setEnglishName(const QString &englishName)
+{
+ if (!isValid()) {
+ mPriv = new Private;
+ }
+
+ mPriv->englishName = englishName;
+}
+
+void ProtocolInfo::setIconName(const QString &iconName)
+{
+ if (!isValid()) {
+ mPriv = new Private;
+ }
+
+ mPriv->iconName = iconName;
+}
+
+void ProtocolInfo::setRequestableChannelClasses(
+ const RequestableChannelClassList &caps)
+{
+ if (!isValid()) {
+ mPriv = new Private;
+ }
+
+ mPriv->caps.updateRequestableChannelClasses(caps);
+}
+
+void ProtocolInfo::setAllowedPresenceStatuses(const PresenceSpecList &statuses)
+{
+ if (!isValid()) {
+ mPriv = new Private;
+ }
+
+ mPriv->statuses = statuses;
+}
+
+void ProtocolInfo::setAvatarRequirements(const AvatarSpec &avatarRequirements)
+{
+ if (!isValid()) {
+ mPriv = new Private;
+ }
+
+ mPriv->avatarRequirements = avatarRequirements;
+}
+
+} // Tp
diff --git a/TelepathyQt/protocol-info.h b/TelepathyQt/protocol-info.h
new file mode 100644
index 00000000..36d27ca9
--- /dev/null
+++ b/TelepathyQt/protocol-info.h
@@ -0,0 +1,102 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_protocol_info_h_HEADER_GUARD_
+#define _TelepathyQt_protocol_info_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/AvatarSpec>
+#include <TelepathyQt/Global>
+#include <TelepathyQt/PresenceSpec>
+#include <TelepathyQt/ProtocolParameter>
+#include <TelepathyQt/Types>
+
+#include <QSharedDataPointer>
+#include <QString>
+#include <QList>
+
+namespace Tp
+{
+
+class ConnectionCapabilities;
+
+class TP_QT_EXPORT ProtocolInfo
+{
+public:
+ ProtocolInfo();
+ ProtocolInfo(const ProtocolInfo &other);
+ ~ProtocolInfo();
+
+ bool isValid() const { return mPriv.constData() != 0; }
+
+ ProtocolInfo &operator=(const ProtocolInfo &other);
+
+ QString cmName() const;
+
+ QString name() const;
+
+ ProtocolParameterList parameters() const;
+ bool hasParameter(const QString &name) const;
+
+ bool canRegister() const;
+
+ ConnectionCapabilities capabilities() const;
+
+ QString vcardField() const;
+
+ QString englishName() const;
+
+ QString iconName() const;
+
+ PresenceSpecList allowedPresenceStatuses() const;
+
+ AvatarSpec avatarRequirements() const;
+
+private:
+ friend class ConnectionManager;
+
+ TP_QT_NO_EXPORT ProtocolInfo(const QString &cmName, const QString &name);
+
+ TP_QT_NO_EXPORT void addParameter(const ParamSpec &spec);
+ TP_QT_NO_EXPORT void setVCardField(const QString &vcardField);
+ TP_QT_NO_EXPORT void setEnglishName(const QString &englishName);
+ TP_QT_NO_EXPORT void setIconName(const QString &iconName);
+ TP_QT_NO_EXPORT void setRequestableChannelClasses(const RequestableChannelClassList &caps);
+ TP_QT_NO_EXPORT void setAllowedPresenceStatuses(const PresenceSpecList &statuses);
+ TP_QT_NO_EXPORT void setAvatarRequirements(const AvatarSpec &avatarRequirements);
+
+ struct Private;
+ friend struct Private;
+ QSharedDataPointer<Private> mPriv;
+};
+
+typedef QList<ProtocolInfo> ProtocolInfoList;
+
+} // Tp
+
+Q_DECLARE_METATYPE(Tp::ProtocolInfo);
+Q_DECLARE_METATYPE(Tp::ProtocolInfoList);
+
+#endif
diff --git a/TelepathyQt/protocol-parameter.cpp b/TelepathyQt/protocol-parameter.cpp
new file mode 100644
index 00000000..3f7f3476
--- /dev/null
+++ b/TelepathyQt/protocol-parameter.cpp
@@ -0,0 +1,176 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/ProtocolParameter>
+
+#include <TelepathyQt/ManagerFile>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT ProtocolParameter::Private : public QSharedData
+{
+ Private(const QString &name, const QDBusSignature &dbusSignature, QVariant::Type type,
+ const QVariant &defaultValue, ConnMgrParamFlag flags)
+ : name(name), dbusSignature(dbusSignature), type(type), defaultValue(defaultValue),
+ flags(flags) {}
+
+ QString name;
+ QDBusSignature dbusSignature;
+ QVariant::Type type;
+ QVariant defaultValue;
+ ConnMgrParamFlag flags;
+};
+
+/**
+ * \class ProtocolParameter
+ * \ingroup clientcm
+ * \headerfile TelepathyQt/protocol-parameter.h <TelepathyQt/ProtocolParameter>
+ *
+ * \brief The ProtocolParameter class represents a Telepathy protocol parameter.
+ */
+
+
+ProtocolParameter::ProtocolParameter()
+{
+}
+
+ProtocolParameter::ProtocolParameter(const QString &name,
+ const QDBusSignature &dbusSignature,
+ QVariant defaultValue,
+ ConnMgrParamFlag flags)
+ : mPriv(new Private(name, dbusSignature,
+ ManagerFile::variantTypeFromDBusSignature(dbusSignature.signature()), defaultValue,
+ flags))
+{
+}
+
+ProtocolParameter::ProtocolParameter(const ProtocolParameter &other)
+ : mPriv(other.mPriv)
+{
+}
+
+ProtocolParameter::~ProtocolParameter()
+{
+}
+
+ProtocolParameter &ProtocolParameter::operator=(const ProtocolParameter &other)
+{
+ this->mPriv = other.mPriv;
+ return *this;
+}
+
+bool ProtocolParameter::operator==(const ProtocolParameter &other) const
+{
+ if (!isValid() || !other.isValid()) {
+ if (!isValid() && !other.isValid()) {
+ return true;
+ }
+ return false;
+ }
+
+ return (mPriv->name == other.name());
+}
+
+bool ProtocolParameter::operator==(const QString &name) const
+{
+ if (!isValid()) {
+ return false;
+ }
+
+ return (mPriv->name == name);
+}
+
+bool ProtocolParameter::operator<(const Tp::ProtocolParameter& other) const
+{
+ return mPriv->name < other.name();
+}
+
+QString ProtocolParameter::name() const
+{
+ if (!isValid()) {
+ return QString();
+ }
+
+ return mPriv->name;
+}
+
+QDBusSignature ProtocolParameter::dbusSignature() const
+{
+ if (!isValid()) {
+ return QDBusSignature();
+ }
+
+ return mPriv->dbusSignature;
+}
+
+QVariant::Type ProtocolParameter::type() const
+{
+ if (!isValid()) {
+ return QVariant::Invalid;
+ }
+
+ return mPriv->type;
+}
+
+QVariant ProtocolParameter::defaultValue() const
+{
+ if (!isValid()) {
+ return QVariant();
+ }
+
+ return mPriv->defaultValue;
+}
+
+bool ProtocolParameter::isRequired() const
+{
+ if (!isValid()) {
+ return false;
+ }
+
+ return mPriv->flags & ConnMgrParamFlagRequired;
+}
+
+bool ProtocolParameter::isSecret() const
+{
+ if (!isValid()) {
+ return false;
+ }
+
+ return mPriv->flags & ConnMgrParamFlagSecret;
+}
+
+bool ProtocolParameter::isRequiredForRegistration() const
+{
+ if (!isValid()) {
+ return false;
+ }
+
+ return mPriv->flags & ConnMgrParamFlagRegister;
+}
+
+uint qHash(const ProtocolParameter& parameter)
+{
+ return qHash(parameter.name());
+}
+
+} // Tp
diff --git a/TelepathyQt/protocol-parameter.h b/TelepathyQt/protocol-parameter.h
new file mode 100644
index 00000000..0b017edc
--- /dev/null
+++ b/TelepathyQt/protocol-parameter.h
@@ -0,0 +1,87 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_protocol_parameter_h_HEADER_GUARD_
+#define _TelepathyQt_protocol_parameter_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/Global>
+
+#include <QDBusSignature>
+#include <QSharedDataPointer>
+#include <QString>
+#include <QVariant>
+
+namespace Tp
+{
+
+class TP_QT_EXPORT ProtocolParameter
+{
+public:
+ ProtocolParameter();
+ ProtocolParameter(const ProtocolParameter &other);
+ ~ProtocolParameter();
+
+ bool isValid() const { return mPriv.constData() != 0; }
+
+ ProtocolParameter &operator=(const ProtocolParameter &other);
+ bool operator==(const ProtocolParameter &other) const;
+ bool operator==(const QString &name) const;
+ bool operator<(const ProtocolParameter &other) const;
+
+ QString name() const;
+ QDBusSignature dbusSignature() const;
+ QVariant::Type type() const;
+ QVariant defaultValue() const;
+
+ bool isRequired() const;
+ bool isSecret() const;
+ bool isRequiredForRegistration() const;
+
+private:
+ friend class ConnectionManager;
+ friend class ProtocolInfo;
+
+ TP_QT_NO_EXPORT ProtocolParameter(const QString &name,
+ const QDBusSignature &dbusSignature,
+ QVariant defaultValue,
+ ConnMgrParamFlag flags);
+
+ struct Private;
+ friend struct Private;
+ QSharedDataPointer<Private> mPriv;
+};
+
+typedef QList<ProtocolParameter> ProtocolParameterList;
+
+uint qHash(const ProtocolParameter &parameter);
+
+} // Tp
+
+Q_DECLARE_METATYPE(Tp::ProtocolParameter);
+Q_DECLARE_METATYPE(Tp::ProtocolParameterList);
+
+#endif
diff --git a/TelepathyQt/readiness-helper.cpp b/TelepathyQt/readiness-helper.cpp
new file mode 100644
index 00000000..1bc810ab
--- /dev/null
+++ b/TelepathyQt/readiness-helper.cpp
@@ -0,0 +1,684 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/ReadinessHelper>
+
+#include "TelepathyQt/_gen/readiness-helper.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/DBusProxy>
+#include <TelepathyQt/PendingReady>
+#include <TelepathyQt/RefCounted>
+#include <TelepathyQt/SharedPtr>
+
+#include <QDBusError>
+#include <QSharedData>
+#include <QTimer>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT ReadinessHelper::Introspectable::Private : public QSharedData
+{
+ Private(const QSet<uint> &makesSenseForStatuses,
+ const Features &dependsOnFeatures,
+ const QStringList &dependsOnInterfaces,
+ IntrospectFunc introspectFunc,
+ void *introspectFuncData,
+ bool critical)
+ : makesSenseForStatuses(makesSenseForStatuses),
+ dependsOnFeatures(dependsOnFeatures),
+ dependsOnInterfaces(dependsOnInterfaces),
+ introspectFunc(introspectFunc),
+ introspectFuncData(introspectFuncData),
+ critical(critical) {}
+
+ QSet<uint> makesSenseForStatuses;
+ Features dependsOnFeatures;
+ QStringList dependsOnInterfaces;
+ IntrospectFunc introspectFunc;
+ void *introspectFuncData;
+ bool critical;
+};
+
+ReadinessHelper::Introspectable::Introspectable()
+ : mPriv(new Private(QSet<uint>(), Features(), QStringList(), 0, 0, false))
+{
+}
+
+ReadinessHelper::Introspectable::Introspectable(const QSet<uint> &makesSenseForStatuses,
+ const Features &dependsOnFeatures, const QStringList &dependsOnInterfaces,
+ IntrospectFunc introspectFunc, void *introspectFuncData, bool critical)
+ : mPriv(new Private(makesSenseForStatuses, dependsOnFeatures, dependsOnInterfaces,
+ introspectFunc, introspectFuncData, critical))
+{
+}
+
+ReadinessHelper::Introspectable::Introspectable(const Introspectable &other)
+ : mPriv(other.mPriv)
+{
+}
+
+ReadinessHelper::Introspectable::~Introspectable()
+{
+}
+
+ReadinessHelper::Introspectable &ReadinessHelper::Introspectable::operator=(
+ const Introspectable &other)
+{
+ mPriv = other.mPriv;
+ return *this;
+}
+
+struct TP_QT_NO_EXPORT ReadinessHelper::Private
+{
+ Private(ReadinessHelper *parent,
+ RefCounted *object,
+ uint currentStatus,
+ const Introspectables &introspectables);
+ Private(ReadinessHelper *parent,
+ DBusProxy *proxy,
+ uint currentStatus,
+ const Introspectables &introspectables);
+ ~Private();
+
+ void setCurrentStatus(uint newStatus);
+ void setIntrospectCompleted(const Feature &feature, bool success,
+ const QString &errorName = QString(),
+ const QString &errorMessage = QString());
+ void iterateIntrospection();
+ Features depsFor(const Feature &feature); // Recursive dependencies for a feature
+
+ void abortOperations(const QString &errorName, const QString &errorMessage);
+
+ ReadinessHelper *parent;
+ RefCounted *object;
+ DBusProxy *proxy;
+ uint currentStatus;
+ QStringList interfaces;
+ Introspectables introspectables;
+ QSet<uint> supportedStatuses;
+ Features supportedFeatures;
+ Features satisfiedFeatures;
+ Features requestedFeatures;
+ Features missingFeatures;
+ Features pendingFeatures;
+ Features inFlightFeatures;
+ QHash<Feature, QPair<QString, QString> > missingFeaturesErrors;
+ QList<PendingReady *> pendingOperations;
+
+ bool pendingStatusChange;
+ uint pendingStatus;
+};
+
+ReadinessHelper::Private::Private(
+ ReadinessHelper *parent,
+ RefCounted *object,
+ uint currentStatus,
+ const Introspectables &introspectables)
+ : parent(parent),
+ object(object),
+ proxy(0),
+ currentStatus(currentStatus),
+ introspectables(introspectables),
+ pendingStatusChange(false),
+ pendingStatus(-1)
+{
+ for (Introspectables::const_iterator i = introspectables.constBegin();
+ i != introspectables.constEnd(); ++i) {
+ Feature feature = i.key();
+ Introspectable introspectable = i.value();
+ Q_ASSERT(introspectable.mPriv->introspectFunc != 0);
+ supportedStatuses += introspectable.mPriv->makesSenseForStatuses;
+ supportedFeatures += feature;
+ }
+}
+
+ReadinessHelper::Private::Private(
+ ReadinessHelper *parent,
+ DBusProxy *proxy,
+ uint currentStatus,
+ const Introspectables &introspectables)
+ : parent(parent),
+ object(proxy),
+ proxy(proxy),
+ currentStatus(currentStatus),
+ introspectables(introspectables),
+ pendingStatusChange(false),
+ pendingStatus(-1)
+{
+ Q_ASSERT(proxy != 0);
+
+ for (Introspectables::const_iterator i = introspectables.constBegin();
+ i != introspectables.constEnd(); ++i) {
+ Feature feature = i.key();
+ Introspectable introspectable = i.value();
+ Q_ASSERT(introspectable.mPriv->introspectFunc != 0);
+ supportedStatuses += introspectable.mPriv->makesSenseForStatuses;
+ supportedFeatures += feature;
+ }
+}
+
+ReadinessHelper::Private::~Private()
+{
+ const static QString messageDestroyed(QLatin1String("Destroyed"));
+
+ abortOperations(TP_QT_ERROR_CANCELLED, messageDestroyed);
+}
+
+void ReadinessHelper::Private::setCurrentStatus(uint newStatus)
+{
+ if (currentStatus == newStatus) {
+ return;
+ }
+
+ if (inFlightFeatures.isEmpty()) {
+ currentStatus = newStatus;
+ satisfiedFeatures.clear();
+ missingFeatures.clear();
+
+ // Make all features that were requested for the new status pending again
+ pendingFeatures = requestedFeatures;
+
+ // becomeReady ensures that the recursive dependencies of the requested features are already
+ // in the requested set, so we don't have to re-add them here
+
+ if (supportedStatuses.contains(currentStatus)) {
+ QTimer::singleShot(0, parent, SLOT(iterateIntrospection()));
+ } else {
+ emit parent->statusReady(currentStatus);
+ }
+ } else {
+ debug() << "status changed while introspection process was running";
+ pendingStatusChange = true;
+ pendingStatus = newStatus;
+ }
+}
+
+void ReadinessHelper::Private::setIntrospectCompleted(const Feature &feature,
+ bool success, const QString &errorName, const QString &errorMessage)
+{
+ debug() << "ReadinessHelper::setIntrospectCompleted: feature:" << feature <<
+ "- success:" << success;
+ if (pendingStatusChange) {
+ debug() << "ReadinessHelper::setIntrospectCompleted called while there is "
+ "a pending status change - ignoring";
+
+ inFlightFeatures.remove(feature);
+
+ // ignore all introspection completed as the state changed
+ if (!inFlightFeatures.isEmpty()) {
+ return;
+ }
+ pendingStatusChange = false;
+ setCurrentStatus(pendingStatus);
+ return;
+ }
+
+ Q_ASSERT(pendingFeatures.contains(feature));
+ Q_ASSERT(inFlightFeatures.contains(feature));
+
+ if (success) {
+ satisfiedFeatures.insert(feature);
+ }
+ else {
+ missingFeatures.insert(feature);
+ missingFeaturesErrors.insert(feature,
+ QPair<QString, QString>(errorName, errorMessage));
+ if (errorName.isEmpty()) {
+ warning() << "ReadinessHelper::setIntrospectCompleted: Feature" <<
+ feature << "introspection failed but no error message was given";
+ }
+ }
+
+ pendingFeatures.remove(feature);
+ inFlightFeatures.remove(feature);
+
+ QTimer::singleShot(0, parent, SLOT(iterateIntrospection()));
+}
+
+void ReadinessHelper::Private::iterateIntrospection()
+{
+ if (proxy && !proxy->isValid()) {
+ debug() << "ReadinessHelper: not iterating as the proxy is invalidated";
+ return;
+ }
+
+ // When there's a pending status change, we MUST NOT
+ // - finish PendingReadys (as they'd not be finished in the new status)
+ // - claim a status as being ready (because the new one isn't)
+ // and SHOULD NOT
+ // - fire new introspection jobs (as that would just delay the pending status change even more)
+ // and NEED NOT
+ // - flag features as missing (as the completed features will be cleared anyway when starting
+ // introspection for the new status)
+ //
+ // So we can safely skip the rest of this function here.
+ if (pendingStatusChange) {
+ debug() << "ReadinessHelper: not iterating as a status change is pending";
+ return;
+ }
+
+ // Flag the currently pending reverse dependencies of any previously discovered missing features
+ // as missing
+ foreach (const Feature &feature, pendingFeatures) {
+ if (!depsFor(feature).intersect(missingFeatures).isEmpty()) {
+ missingFeatures.insert(feature);
+ missingFeaturesErrors.insert(feature,
+ QPair<QString, QString>(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Feature depends on other features that are not available")));
+ }
+ }
+
+ const Features completedFeatures = satisfiedFeatures + missingFeatures;
+
+ // check if any pending operations for becomeReady should finish now
+ // based on their requested features having nothing more than what
+ // satisfiedFeatures + missingFeatures has
+ QString errorName;
+ QString errorMessage;
+ foreach (PendingReady *operation, pendingOperations) {
+ if ((operation->requestedFeatures() - completedFeatures).isEmpty()) {
+ if (parent->isReady(operation->requestedFeatures(), &errorName, &errorMessage)) {
+ operation->setFinished();
+ } else {
+ operation->setFinishedWithError(errorName, errorMessage);
+ }
+
+ // Remove the operation from tracking, so we don't double-finish it
+ //
+ // Qt foreach makes a copy of the container, which will be detached at this point, so
+ // this is perfectly safe
+ pendingOperations.removeOne(operation);
+ }
+ }
+
+ if ((requestedFeatures - completedFeatures).isEmpty()) {
+ // Otherwise, we'd emit statusReady with currentStatus although we are supposed to be
+ // introspecting the pendingStatus and only when that is complete, emit statusReady
+ Q_ASSERT(!pendingStatusChange);
+
+ // all requested features satisfied or missing
+ emit parent->statusReady(currentStatus);
+ return;
+ }
+
+ // update pendingFeatures with the difference of requested and
+ // satisfied + missing
+ pendingFeatures -= completedFeatures;
+
+ // find out which features don't have dependencies that are still pending
+ Features readyToIntrospect;
+ foreach (const Feature &feature, pendingFeatures) {
+ // missing doesn't have to be considered here anymore
+ if ((introspectables[feature].mPriv->dependsOnFeatures - satisfiedFeatures).isEmpty()) {
+ readyToIntrospect.insert(feature);
+ }
+ }
+
+ // now readyToIntrospect should contain all the features which have
+ // all their feature dependencies satisfied
+ foreach (const Feature &feature, readyToIntrospect) {
+ if (inFlightFeatures.contains(feature)) {
+ continue;
+ }
+
+ inFlightFeatures.insert(feature);
+
+ Introspectable introspectable = introspectables[feature];
+
+ if (!introspectable.mPriv->makesSenseForStatuses.contains(currentStatus)) {
+ // No-op satisfy features for which nothing has to be done in
+ // the current state
+ setIntrospectCompleted(feature, true);
+ return; // will be called with a single-shot soon again
+ }
+
+ foreach (const QString &interface, introspectable.mPriv->dependsOnInterfaces) {
+ if (!interfaces.contains(interface)) {
+ // If a feature is ready to introspect and depends on a interface
+ // that is not present the feature can't possibly be satisfied
+ debug() << "feature" << feature << "depends on interfaces" <<
+ introspectable.mPriv->dependsOnInterfaces << ", but interface" << interface <<
+ "is not present";
+ setIntrospectCompleted(feature, false,
+ QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Feature depend on interfaces that are not available"));
+ return; // will be called with a single-shot soon again
+ }
+ }
+
+ // yes, with the dependency info, we can even parallelize
+ // introspection of several features at once, reducing total round trip
+ // time considerably with many independent features!
+ (*(introspectable.mPriv->introspectFunc))(introspectable.mPriv->introspectFuncData);
+ }
+}
+
+Features ReadinessHelper::Private::depsFor(const Feature &feature)
+{
+ Features deps;
+
+ foreach (Feature dep, introspectables[feature].mPriv->dependsOnFeatures) {
+ deps += dep;
+ deps += depsFor(dep);
+ }
+
+ return deps;
+}
+
+void ReadinessHelper::Private::abortOperations(const QString &errorName,
+ const QString &errorMessage)
+{
+ foreach (PendingReady *operation, pendingOperations) {
+ operation->setFinishedWithError(errorName, errorMessage);
+ }
+ pendingOperations.clear();
+}
+
+/**
+ * \class ReadinessHelper
+ * \ingroup utils
+ * \headerfile TelepathyQt/readiness-helper.h <TelepathyQt/ReadinessHelper>
+ *
+ * \brief The ReadinessHelper class is a helper class used by the introspection
+ * process.
+ */
+
+/**
+ * \class ReadinessHelper::Introspectable
+ * \ingroup utils
+ * \headerfile TelepathyQt/readiness-helper.h <TelepathyQt/ReadinessHelper>
+ *
+ * \brief The ReadinessHelper::Introspectable class represents a introspectable
+ * used by ReadinessHelper.
+ */
+
+ReadinessHelper::ReadinessHelper(RefCounted *object,
+ uint currentStatus,
+ const Introspectables &introspectables,
+ QObject *parent)
+ : QObject(parent),
+ mPriv(new Private(this, object, currentStatus, introspectables))
+{
+}
+
+ReadinessHelper::ReadinessHelper(DBusProxy *proxy,
+ uint currentStatus,
+ const Introspectables &introspectables,
+ QObject *parent)
+ : QObject(parent),
+ mPriv(new Private(this, proxy, currentStatus, introspectables))
+{
+}
+
+ReadinessHelper::~ReadinessHelper()
+{
+ delete mPriv;
+}
+
+void ReadinessHelper::addIntrospectables(const Introspectables &introspectables)
+{
+ // QMap::unite will create multiple items if the key is already in the map
+ // so let's make sure we don't duplicate keys
+ for (Introspectables::const_iterator i = introspectables.constBegin();
+ i != introspectables.constEnd(); ++i) {
+ Feature feature = i.key();
+ if (mPriv->introspectables.contains(feature)) {
+ warning() << "ReadinessHelper::addIntrospectables: trying to add an "
+ "introspectable for feature" << feature << "but introspectable "
+ "for this feature already exists";
+ } else {
+ Introspectable introspectable = i.value();
+ mPriv->introspectables.insert(feature, introspectable);
+ mPriv->supportedStatuses += introspectable.mPriv->makesSenseForStatuses;
+ mPriv->supportedFeatures += feature;
+ }
+ }
+
+ debug() << "ReadinessHelper: new supportedStatuses =" << mPriv->supportedStatuses;
+ debug() << "ReadinessHelper: new supportedFeatures =" << mPriv->supportedFeatures;
+}
+
+uint ReadinessHelper::currentStatus() const
+{
+ return mPriv->currentStatus;
+}
+
+void ReadinessHelper::setCurrentStatus(uint currentStatus)
+{
+ mPriv->setCurrentStatus(currentStatus);
+}
+
+/**
+ * Force the current internal status to \a currentStatus.
+ *
+ * Note that this method will not start a new introspection or restart the
+ * current one in case one is running.
+ *
+ * This is useful for example when the status is unknown initially but it will
+ * become known in the first introspection run and there is no need to re-run
+ * the introspection.
+ *
+ * \param currentStatus The status to set.
+ */
+void ReadinessHelper::forceCurrentStatus(uint currentStatus)
+{
+ mPriv->currentStatus = currentStatus;
+}
+
+QStringList ReadinessHelper::interfaces() const
+{
+ return mPriv->interfaces;
+}
+
+void ReadinessHelper::setInterfaces(const QStringList &interfaces)
+{
+ mPriv->interfaces = interfaces;
+}
+
+Features ReadinessHelper::requestedFeatures() const
+{
+ return mPriv->requestedFeatures;
+}
+
+Features ReadinessHelper::actualFeatures() const
+{
+ return mPriv->satisfiedFeatures;
+}
+
+Features ReadinessHelper::missingFeatures() const
+{
+ return mPriv->missingFeatures;
+}
+
+bool ReadinessHelper::isReady(const Feature &feature,
+ QString *errorName, QString *errorMessage) const
+{
+ if (mPriv->proxy && !mPriv->proxy->isValid()) {
+ if (errorName) {
+ *errorName = mPriv->proxy->invalidationReason();
+ }
+ if (errorMessage) {
+ *errorMessage = mPriv->proxy->invalidationMessage();
+ }
+ return false;
+ }
+
+ if (!mPriv->supportedFeatures.contains(feature)) {
+ if (errorName) {
+ *errorName = QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT);
+ }
+ if (errorMessage) {
+ *errorMessage = QLatin1String("Unsupported feature");
+ }
+ return false;
+ }
+
+ bool ret = true;
+
+ if (feature.isCritical()) {
+ if (!mPriv->satisfiedFeatures.contains(feature)) {
+ ret = false;
+ }
+ } else {
+ if (!mPriv->satisfiedFeatures.contains(feature) &&
+ !mPriv->missingFeatures.contains(feature)) {
+ ret = false;
+ }
+ }
+
+ if (!ret) {
+ QPair<QString, QString> error = mPriv->missingFeaturesErrors[feature];
+ if (errorName) {
+ *errorName = error.first;
+ }
+ if (errorMessage) {
+ *errorMessage = error.second;
+ }
+ }
+
+ return ret;
+}
+
+bool ReadinessHelper::isReady(const Features &features, QString *errorName, QString *errorMessage) const
+{
+ if (mPriv->proxy && !mPriv->proxy->isValid()) {
+ if (errorName) {
+ *errorName = mPriv->proxy->invalidationReason();
+ }
+ if (errorMessage) {
+ *errorMessage = mPriv->proxy->invalidationMessage();
+ }
+ return false;
+ }
+
+ Q_ASSERT(!features.isEmpty());
+
+ foreach (const Feature &feature, features) {
+ if (!isReady(feature, errorName, errorMessage)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+PendingReady *ReadinessHelper::becomeReady(const Features &requestedFeatures)
+{
+ Q_ASSERT(!requestedFeatures.isEmpty());
+
+ if (mPriv->proxy) {
+ connect(mPriv->proxy,
+ SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
+ this,
+ SLOT(onProxyInvalidated(Tp::DBusProxy*,QString,QString)),
+ Qt::UniqueConnection);
+
+ if (!mPriv->proxy->isValid()) {
+ PendingReady *operation = new PendingReady(SharedPtr<RefCounted>(mPriv->object),
+ requestedFeatures);
+ operation->setFinishedWithError(mPriv->proxy->invalidationReason(),
+ mPriv->proxy->invalidationMessage());
+ return operation;
+ }
+ }
+
+ Features supportedFeatures = mPriv->supportedFeatures;
+ if (supportedFeatures.intersect(requestedFeatures) != requestedFeatures) {
+ warning() << "ReadinessHelper::becomeReady called with invalid features: requestedFeatures =" <<
+ requestedFeatures << "- supportedFeatures =" << mPriv->supportedFeatures;
+ PendingReady *operation = new PendingReady(SharedPtr<RefCounted>(mPriv->object),
+ requestedFeatures);
+ operation->setFinishedWithError(
+ QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT),
+ QLatin1String("Requested features contains unsupported feature"));
+ return operation;
+ }
+
+ if (mPriv->proxy && !mPriv->proxy->isValid()) {
+ PendingReady *operation = new PendingReady(SharedPtr<RefCounted>(mPriv->object),
+ requestedFeatures);
+ operation->setFinishedWithError(mPriv->proxy->invalidationReason(),
+ mPriv->proxy->invalidationMessage());
+ return operation;
+ }
+
+ PendingReady *operation;
+ foreach (operation, mPriv->pendingOperations) {
+ if (operation->requestedFeatures() == requestedFeatures) {
+ return operation;
+ }
+ }
+
+ // Insert the dependencies of the requested features too
+ Features requestedWithDeps = requestedFeatures;
+ foreach (const Feature &feature, requestedFeatures) {
+ requestedWithDeps.unite(mPriv->depsFor(feature));
+ }
+
+ mPriv->requestedFeatures += requestedWithDeps;
+ mPriv->pendingFeatures += requestedWithDeps; // will be updated in iterateIntrospection
+
+ operation = new PendingReady(SharedPtr<RefCounted>(mPriv->object), requestedFeatures);
+ mPriv->pendingOperations.append(operation);
+ // Only we finish these PendingReadys, so we don't need destroyed or finished handling for them
+ // - we already know when that happens, as we caused it!
+
+ QTimer::singleShot(0, this, SLOT(iterateIntrospection()));
+
+ return operation;
+}
+
+void ReadinessHelper::setIntrospectCompleted(const Feature &feature, bool success,
+ const QString &errorName, const QString &errorMessage)
+{
+ if (mPriv->proxy && !mPriv->proxy->isValid()) {
+ // proxy became invalid, ignore here
+ return;
+ }
+ mPriv->setIntrospectCompleted(feature, success, errorName, errorMessage);
+}
+
+void ReadinessHelper::setIntrospectCompleted(const Feature &feature, bool success,
+ const QDBusError &error)
+{
+ setIntrospectCompleted(feature, success, error.name(), error.message());
+}
+
+void ReadinessHelper::iterateIntrospection()
+{
+ mPriv->iterateIntrospection();
+}
+
+void ReadinessHelper::onProxyInvalidated(DBusProxy *proxy,
+ const QString &errorName, const QString &errorMessage)
+{
+ // clear satisfied and missing features as we have public methods to get them
+ mPriv->satisfiedFeatures.clear();
+ mPriv->missingFeatures.clear();
+
+ mPriv->abortOperations(errorName, errorMessage);
+}
+
+} // Tp
diff --git a/TelepathyQt/readiness-helper.h b/TelepathyQt/readiness-helper.h
new file mode 100644
index 00000000..d570d172
--- /dev/null
+++ b/TelepathyQt/readiness-helper.h
@@ -0,0 +1,130 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_readiness_helper_h_HEADER_GUARD_
+#define _TelepathyQt_readiness_helper_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Feature>
+
+#include <QMap>
+#include <QSet>
+#include <QSharedDataPointer>
+#include <QStringList>
+
+class QDBusError;
+
+namespace Tp
+{
+
+class DBusProxy;
+class PendingOperation;
+class PendingReady;
+class RefCounted;
+
+class TP_QT_EXPORT ReadinessHelper : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(ReadinessHelper)
+
+public:
+ typedef void (*IntrospectFunc)(void *data);
+
+ struct Introspectable {
+ public:
+ Introspectable();
+ Introspectable(const QSet<uint> &makesSenseForStatuses,
+ const Features &dependsOnFeatures,
+ const QStringList &dependsOnInterfaces,
+ IntrospectFunc introspectFunc,
+ void *introspectFuncData,
+ bool critical = false);
+ Introspectable(const Introspectable &other);
+ ~Introspectable();
+
+ Introspectable &operator=(const Introspectable &other);
+
+ private:
+ friend class ReadinessHelper;
+
+ struct Private;
+ friend struct Private;
+ QSharedDataPointer<Private> mPriv;
+ };
+ typedef QMap<Feature, Introspectable> Introspectables;
+
+ ReadinessHelper(RefCounted *object,
+ uint currentStatus = 0,
+ const Introspectables &introspectables = Introspectables(),
+ QObject *parent = 0);
+ ReadinessHelper(DBusProxy *proxy,
+ uint currentStatus = 0,
+ const Introspectables &introspectables = Introspectables(),
+ QObject *parent = 0);
+ ~ReadinessHelper();
+
+ void addIntrospectables(const Introspectables &introspectables);
+
+ uint currentStatus() const;
+ void setCurrentStatus(uint currentStatus);
+ void forceCurrentStatus(uint currentStatus);
+
+ QStringList interfaces() const;
+ void setInterfaces(const QStringList &interfaces);
+
+ Features requestedFeatures() const;
+ Features actualFeatures() const;
+ Features missingFeatures() const;
+
+ bool isReady(const Feature &feature,
+ QString *errorName = 0, QString *errorMessage = 0) const;
+ bool isReady(const Features &features,
+ QString *errorName = 0, QString *errorMessage = 0) const;
+ PendingReady *becomeReady(const Features &requestedFeatures);
+
+ void setIntrospectCompleted(const Feature &feature, bool success,
+ const QString &errorName = QString(),
+ const QString &errorMessage = QString());
+ void setIntrospectCompleted(const Feature &feature, bool success,
+ const QDBusError &error);
+
+Q_SIGNALS:
+ void statusReady(uint status);
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void iterateIntrospection();
+
+ TP_QT_NO_EXPORT void onProxyInvalidated(Tp::DBusProxy *proxy,
+ const QString &errorName, const QString &errorMessage);
+
+private:
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/ready-object.cpp b/TelepathyQt/ready-object.cpp
new file mode 100644
index 00000000..4c7037ff
--- /dev/null
+++ b/TelepathyQt/ready-object.cpp
@@ -0,0 +1,161 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/ReadyObject>
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/PendingReady>
+#include <TelepathyQt/ReadinessHelper>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT ReadyObject::Private
+{
+ Private(ReadyObject *parent, RefCounted *object, Feature featureCore);
+ Private(ReadyObject *parent, DBusProxy *proxy, Feature featureCore);
+ ~Private();
+
+ ReadyObject *parent;
+ const Features coreFeatures;
+ ReadinessHelper *readinessHelper;
+};
+
+ReadyObject::Private::Private(ReadyObject *parent, RefCounted *object,
+ Feature featureCore)
+ : parent(parent),
+ coreFeatures(Features() << featureCore),
+ readinessHelper(new ReadinessHelper(object))
+{
+}
+
+ReadyObject::Private::Private(ReadyObject *parent, DBusProxy *proxy,
+ Feature featureCore)
+ : parent(parent),
+ coreFeatures(Features() << featureCore),
+ readinessHelper(new ReadinessHelper(proxy))
+{
+}
+
+ReadyObject::Private::~Private()
+{
+ delete readinessHelper;
+}
+
+/**
+ * \class ReadyObject
+ * \ingroup clientreadiness
+ * \headerfile TelepathyQt/ready-object.h> <TelepathyQt/ReadyObject>
+ */
+
+/**
+ * Construct a new ReadyObject object.
+ *
+ * \param object The RefCounted the object refers to.
+ * \param featureCore The core feature of the object.
+ */
+ReadyObject::ReadyObject(RefCounted *object, const Feature &featureCore)
+ : mPriv(new Private(this, object, featureCore))
+{
+}
+
+/**
+ * Construct a new ReadyObject object.
+ *
+ * \param proxy The DBusProxy the object refers to.
+ * \param featureCore The core feature of the object.
+ */
+ReadyObject::ReadyObject(DBusProxy *proxy, const Feature &featureCore)
+ : mPriv(new Private(this, proxy, featureCore))
+{
+}
+
+/**
+ * Class destructor.
+ */
+ReadyObject::~ReadyObject()
+{
+ delete mPriv;
+}
+
+/**
+ * Return whether this object has finished its initial setup.
+ *
+ * This is mostly useful as a sanity check, in code that shouldn't be run
+ * until the object is ready. To wait for the object to be ready, call
+ * becomeReady() and connect to the finished signal on the result.
+ *
+ * \param features The features which should be tested
+ * \return \c true if the object has finished its initial setup for basic
+ * functionality plus the given features
+ */
+bool ReadyObject::isReady(const Features &features) const
+{
+ if (features.isEmpty()) {
+ return mPriv->readinessHelper->isReady(mPriv->coreFeatures);
+ }
+ return mPriv->readinessHelper->isReady(features);
+}
+
+/**
+ * Return a pending operation which will succeed when this object finishes
+ * its initial setup, or will fail if a fatal error occurs during this
+ * initial setup.
+ *
+ * If an empty set is used FeatureCore will be considered as the requested
+ * feature.
+ *
+ * \param requestedFeatures The features which should be enabled
+ * \return A PendingReady object which will emit finished
+ * when this object has finished or failed initial setup for basic
+ * functionality plus the given features
+ */
+PendingReady *ReadyObject::becomeReady(const Features &requestedFeatures)
+{
+ if (requestedFeatures.isEmpty()) {
+ return mPriv->readinessHelper->becomeReady(mPriv->coreFeatures);
+ }
+ return mPriv->readinessHelper->becomeReady(requestedFeatures);
+}
+
+Features ReadyObject::requestedFeatures() const
+{
+ return mPriv->readinessHelper->requestedFeatures();
+}
+
+Features ReadyObject::actualFeatures() const
+{
+ return mPriv->readinessHelper->actualFeatures();
+}
+
+Features ReadyObject::missingFeatures() const
+{
+ return mPriv->readinessHelper->missingFeatures();
+}
+
+ReadinessHelper *ReadyObject::readinessHelper() const
+{
+ return mPriv->readinessHelper;
+}
+
+} // Tp
diff --git a/TelepathyQt/ready-object.h b/TelepathyQt/ready-object.h
new file mode 100644
index 00000000..d963f9e8
--- /dev/null
+++ b/TelepathyQt/ready-object.h
@@ -0,0 +1,69 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_ready_object_h_HEADER_GUARD_
+#define _TelepathyQt_ready_object_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Feature>
+
+#include <QObject>
+
+namespace Tp
+{
+
+class DBusProxy;
+class PendingReady;
+class ReadinessHelper;
+class RefCounted;
+
+class TP_QT_EXPORT ReadyObject
+{
+ Q_DISABLE_COPY(ReadyObject)
+
+public:
+ ReadyObject(RefCounted *object, const Feature &featureCore);
+ ReadyObject(DBusProxy *proxy, const Feature &featureCore);
+ virtual ~ReadyObject();
+
+ virtual bool isReady(const Features &features = Features()) const;
+ virtual PendingReady *becomeReady(const Features &requestedFeatures = Features());
+
+ virtual Features requestedFeatures() const;
+ virtual Features actualFeatures() const;
+ virtual Features missingFeatures() const;
+
+protected:
+ ReadinessHelper *readinessHelper() const;
+
+private:
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/referenced-handles.cpp b/TelepathyQt/referenced-handles.cpp
new file mode 100644
index 00000000..475e5c2e
--- /dev/null
+++ b/TelepathyQt/referenced-handles.cpp
@@ -0,0 +1,333 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/ReferencedHandles>
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/Connection>
+
+#include <QPointer>
+#include <QSharedData>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT ReferencedHandles::Private : public QSharedData
+{
+ QWeakPointer<Connection> connection;
+ HandleType handleType;
+ UIntList handles;
+
+ Private()
+ {
+ handleType = HandleTypeNone;
+ }
+
+ Private(const ConnectionPtr &conn, HandleType handleType,
+ const UIntList &handles)
+ : connection(conn.data()), handleType(handleType), handles(handles)
+ {
+ Q_ASSERT(!conn.isNull());
+ Q_ASSERT(handleType != 0);
+
+ foreach (uint handle, handles) {
+ conn->refHandle(handleType, handle);
+ }
+ }
+
+ Private(const Private &a)
+ : QSharedData(a),
+ connection(a.connection),
+ handleType(a.handleType),
+ handles(a.handles)
+ {
+ if (!handles.isEmpty()) {
+ ConnectionPtr conn(connection);
+ if (!conn) {
+ debug() << " Destroyed after Connection, so the Connection "
+ "has already released the handles";
+ return;
+ }
+
+ for (const_iterator i = handles.constBegin(); i != handles.constEnd(); ++i) {
+ conn->refHandle(handleType, *i);
+ }
+ }
+ }
+
+ ~Private()
+ {
+ if (!handles.isEmpty()) {
+ ConnectionPtr conn(connection);
+ if (!conn) {
+ debug() << " Destroyed after Connection, so the Connection "
+ "has already released the handles";
+ return;
+ }
+
+ for (const_iterator i = handles.constBegin(); i != handles.constEnd(); ++i) {
+ conn->unrefHandle(handleType, *i);
+ }
+ }
+ }
+
+private:
+ void operator=(const Private&);
+};
+
+/**
+ * \class ReferencedHandles
+ * \ingroup clientconn
+ * \headerfile TelepathyQt/referenced-handles.h <TelepathyQt/ReferencedHandles>
+ *
+ * \brief Helper container for safe management of handle lifetimes. Every handle
+ * in a ReferencedHandles container is guaranteed to be valid (and stay valid,
+ * as long it's in at least one ReferencedHandles container).
+ *
+ * The class offers a QList-style API. However, from the mutable operations,
+ * only the operations for which the validity guarantees can be preserved are
+ * provided. This means no functions which can add an arbitrary handle to the
+ * container are included - the only way to add handles to the container is to
+ * reference them using Connection::referenceHandles() and appending the
+ * resulting ReferenceHandles instance.
+ *
+ * ReferencedHandles is a implicitly shared class.
+ */
+
+ReferencedHandles::ReferencedHandles()
+ : mPriv(new Private)
+{
+}
+
+ReferencedHandles::ReferencedHandles(const ReferencedHandles &other)
+ : mPriv(other.mPriv)
+{
+}
+
+ReferencedHandles::~ReferencedHandles()
+{
+}
+
+ConnectionPtr ReferencedHandles::connection() const
+{
+ return ConnectionPtr(mPriv->connection);
+}
+
+HandleType ReferencedHandles::handleType() const
+{
+ return mPriv->handleType;
+}
+
+uint ReferencedHandles::at(int i) const
+{
+ return mPriv->handles[i];
+}
+
+uint ReferencedHandles::value(int i, uint defaultValue) const
+{
+ return mPriv->handles.value(i, defaultValue);
+}
+
+ReferencedHandles::const_iterator ReferencedHandles::begin() const
+{
+ return mPriv->handles.begin();
+}
+
+ReferencedHandles::const_iterator ReferencedHandles::end() const
+{
+ return mPriv->handles.end();
+}
+
+bool ReferencedHandles::contains(uint handle) const
+{
+ return mPriv->handles.contains(handle);
+}
+
+int ReferencedHandles::count(uint handle) const
+{
+ return mPriv->handles.count(handle);
+}
+
+int ReferencedHandles::indexOf(uint handle, int from) const
+{
+ return mPriv->handles.indexOf(handle, from);
+}
+
+bool ReferencedHandles::isEmpty() const
+{
+ return mPriv->handles.isEmpty();
+}
+
+int ReferencedHandles::lastIndexOf(uint handle, int from) const
+{
+ return mPriv->handles.lastIndexOf(handle, from);
+}
+
+ReferencedHandles ReferencedHandles::mid(int pos, int length) const
+{
+ return ReferencedHandles(connection(), handleType(),
+ mPriv->handles.mid(pos, length));
+}
+
+int ReferencedHandles::size() const
+{
+ return mPriv->handles.size();
+}
+
+void ReferencedHandles::clear()
+{
+ if (!mPriv->handles.empty()) {
+ ConnectionPtr conn(mPriv->connection);
+ if (conn) {
+ foreach (uint handle, mPriv->handles) {
+ conn->unrefHandle(handleType(), handle);
+ }
+ } else {
+ warning() << "Connection already destroyed in "
+ "ReferencedHandles::clear() so can't unref!";
+ }
+ }
+
+ mPriv->handles.clear();
+}
+
+void ReferencedHandles::move(int from, int to)
+{
+ mPriv->handles.move(from, to);
+}
+
+int ReferencedHandles::removeAll(uint handle)
+{
+ int count = mPriv->handles.removeAll(handle);
+
+ if (count > 0) {
+ ConnectionPtr conn(mPriv->connection);
+ if (conn) {
+ for (int i = 0; i < count; ++i) {
+ conn->unrefHandle(handleType(), handle);
+ }
+ } else {
+ warning() << "Connection already destroyed in "
+ "ReferencedHandles::removeAll() with handle ==" <<
+ handle << "so can't unref!";
+ }
+ }
+
+ return count;
+}
+
+void ReferencedHandles::removeAt(int i)
+{
+ ConnectionPtr conn(mPriv->connection);
+ if (conn) {
+ conn->unrefHandle(handleType(), at(i));
+ } else {
+ warning() << "Connection already destroyed in "
+ "ReferencedHandles::removeAt() with i ==" <<
+ i << "so can't unref!";
+ }
+
+ mPriv->handles.removeAt(i);
+}
+
+bool ReferencedHandles::removeOne(uint handle)
+{
+ bool wasThere = mPriv->handles.removeOne(handle);
+
+ if (wasThere) {
+ ConnectionPtr conn(mPriv->connection);
+ if (conn) {
+ conn->unrefHandle(handleType(), handle);
+ } else {
+ warning() << "Connection already destroyed in "
+ "ReferencedHandles::removeOne() with handle ==" <<
+ handle << "so can't unref!";
+ }
+ }
+
+ return wasThere;
+}
+
+void ReferencedHandles::swap(int i, int j)
+{
+ mPriv->handles.swap(i, j);
+}
+
+uint ReferencedHandles::takeAt(int i)
+{
+ ConnectionPtr conn(mPriv->connection);
+ if (conn) {
+ conn->unrefHandle(handleType(), at(i));
+ } else {
+ warning() << "Connection already destroyed in "
+ "ReferencedHandles::takeAt() with i ==" <<
+ i << "so can't unref!";
+ }
+
+ return mPriv->handles.takeAt(i);
+}
+
+ReferencedHandles ReferencedHandles::operator+(const ReferencedHandles &another) const
+{
+ if (connection() != another.connection() ||
+ handleType() != another.handleType()) {
+ warning() << "Tried to concatenate ReferencedHandles instances "
+ "with different connection and/or handle type";
+ return *this;
+ }
+
+ return ReferencedHandles(connection(), handleType(),
+ mPriv->handles + another.mPriv->handles);
+}
+
+ReferencedHandles &ReferencedHandles::operator=(
+ const ReferencedHandles &another)
+{
+ mPriv = another.mPriv;
+ return *this;
+}
+
+bool ReferencedHandles::operator==(const ReferencedHandles &another) const
+{
+ return connection() == another.connection() &&
+ handleType() == another.handleType() &&
+ mPriv->handles == another.mPriv->handles;
+}
+
+bool ReferencedHandles::operator==(const UIntList &list) const
+{
+ return mPriv->handles == list;
+}
+
+UIntList ReferencedHandles::toList() const
+{
+ return mPriv->handles;
+}
+
+ReferencedHandles::ReferencedHandles(const ConnectionPtr &connection,
+ HandleType handleType, const UIntList &handles)
+ : mPriv(new Private(connection, handleType, handles))
+{
+}
+
+} // Tp
diff --git a/TelepathyQt/referenced-handles.h b/TelepathyQt/referenced-handles.h
new file mode 100644
index 00000000..71dd0e2f
--- /dev/null
+++ b/TelepathyQt/referenced-handles.h
@@ -0,0 +1,264 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_referenced_handles_h_HEADER_GUARD_
+#define _TelepathyQt_referenced_handles_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/Types>
+
+#ifndef QT_NO_STL
+# include <list>
+#endif
+
+#include <QList>
+#include <QSet>
+#include <QSharedDataPointer>
+#include <QVector>
+
+namespace Tp
+{
+
+class Connection;
+
+class TP_QT_EXPORT ReferencedHandles
+{
+public:
+ typedef UIntList::const_iterator const_iterator;
+ typedef UIntList::ConstIterator ConstIterator;
+ typedef UIntList::const_pointer const_pointer;
+ typedef UIntList::const_reference const_reference;
+ typedef UIntList::difference_type difference_type;
+ typedef UIntList::pointer pointer;
+ typedef UIntList::reference reference;
+ typedef UIntList::size_type size_type;
+ typedef UIntList::value_type value_type;
+
+ ReferencedHandles();
+ ReferencedHandles(const ReferencedHandles &other);
+ ~ReferencedHandles();
+
+ ConnectionPtr connection() const;
+ HandleType handleType() const;
+
+ uint at(int i) const;
+
+ inline uint back() const
+ {
+ return last();
+ }
+
+ inline uint first() const
+ {
+ return at(0);
+ }
+
+ inline uint front() const
+ {
+ return first();
+ }
+
+ inline uint last() const
+ {
+ return at(size() - 1);
+ }
+
+ uint value(int i, uint defaultValue = 0) const;
+
+ const_iterator begin() const;
+
+ inline const_iterator constBegin() const
+ {
+ return begin();
+ }
+
+ inline const_iterator constEnd() const
+ {
+ return end();
+ }
+
+ const_iterator end() const;
+
+ bool contains(uint handle) const;
+
+ int count(uint handle) const;
+
+ inline int count() const
+ {
+ return size();
+ }
+
+ inline bool empty() const
+ {
+ return isEmpty();
+ }
+
+ inline bool endsWith(uint handle) const
+ {
+ return !isEmpty() && last() == handle;
+ }
+
+ int indexOf(uint handle, int from = 0) const;
+
+ bool isEmpty() const;
+
+ int lastIndexOf(uint handle, int from = -1) const;
+
+ inline int length() const
+ {
+ return count();
+ }
+
+ ReferencedHandles mid(int pos, int length = -1) const;
+
+ int size() const;
+
+ inline bool startsWith(uint handle) const
+ {
+ return !isEmpty() && first() == handle;
+ }
+
+ inline void append(const ReferencedHandles& another)
+ {
+ *this = *this + another;
+ }
+
+ void clear();
+ void move(int from, int to);
+
+ inline void pop_back()
+ {
+ return removeLast();
+ }
+
+ inline void pop_front()
+ {
+ return removeFirst();
+ }
+
+ int removeAll(uint handle);
+
+ void removeAt(int i);
+
+ inline void removeFirst()
+ {
+ return removeAt(0);
+ }
+
+ inline void removeLast()
+ {
+ return removeAt(size() - 1);
+ }
+
+ bool removeOne(uint handle);
+
+ void swap(int i, int j);
+
+ uint takeAt(int i);
+
+ inline uint takeFirst()
+ {
+ return takeAt(0);
+ }
+
+ inline uint takeLast()
+ {
+ return takeAt(size() - 1);
+ }
+
+ bool operator!=(const ReferencedHandles& another) const
+ {
+ return !(*this == another);
+ }
+
+ bool operator!=(const UIntList& another) const
+ {
+ return !(*this == another);
+ }
+
+ ReferencedHandles operator+(const ReferencedHandles& another) const;
+
+ inline ReferencedHandles& operator+=(const ReferencedHandles& another)
+ {
+ return *this = (*this + another);
+ }
+
+ ReferencedHandles& operator<<(const ReferencedHandles& another)
+ {
+ return *this += another;
+ }
+
+ ReferencedHandles& operator=(const ReferencedHandles& another);
+
+ bool operator==(const ReferencedHandles& another) const;
+ bool operator==(const UIntList& list) const;
+
+ inline uint operator[](int i) const
+ {
+ return at(i);
+ }
+
+ UIntList toList() const;
+
+ inline QSet<uint> toSet() const
+ {
+ return toList().toSet();
+ }
+
+#ifndef QT_NO_STL
+ inline std::list<uint> toStdList() const
+ {
+ return toList().toStdList();
+ }
+#endif
+
+ inline QVector<uint> toVector() const
+ {
+ return toList().toVector();
+ }
+
+private:
+ // For access to the "prime" constructor
+ friend class ContactManager;
+ friend class PendingContactAttributes;
+ friend class PendingContacts;
+ friend class PendingHandles;
+
+ TP_QT_NO_EXPORT ReferencedHandles(const ConnectionPtr &connection,
+ HandleType handleType, const UIntList& handles);
+
+ struct Private;
+ friend struct Private;
+ QSharedDataPointer<Private> mPriv;
+};
+
+typedef QListIterator<uint> ReferencedHandlesIterator;
+
+} // Tp
+
+Q_DECLARE_METATYPE(Tp::ReferencedHandles);
+
+#endif
diff --git a/TelepathyQt/request-temporary-handler-internal.cpp b/TelepathyQt/request-temporary-handler-internal.cpp
new file mode 100644
index 00000000..14b47923
--- /dev/null
+++ b/TelepathyQt/request-temporary-handler-internal.cpp
@@ -0,0 +1,135 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 "TelepathyQt/request-temporary-handler-internal.h"
+
+#include "TelepathyQt/_gen/request-temporary-handler-internal.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/ChannelClassSpecList>
+
+namespace Tp
+{
+
+SharedPtr<RequestTemporaryHandler> RequestTemporaryHandler::create(const AccountPtr &account)
+{
+ return SharedPtr<RequestTemporaryHandler>(new RequestTemporaryHandler(account));
+}
+
+RequestTemporaryHandler::RequestTemporaryHandler(const AccountPtr &account)
+ : AbstractClient(),
+ QObject(),
+ AbstractClientHandler(ChannelClassSpecList(), AbstractClientHandler::Capabilities(), false),
+ mAccount(account),
+ mQueueChannelReceived(true),
+ dbusHandlerInvoked(false)
+{
+}
+
+RequestTemporaryHandler::~RequestTemporaryHandler()
+{
+}
+
+void RequestTemporaryHandler::handleChannels(
+ const MethodInvocationContextPtr<> &context,
+ const AccountPtr &account,
+ const ConnectionPtr &connection,
+ const QList<ChannelPtr> &channels,
+ const QList<ChannelRequestPtr> &requestsSatisfied,
+ const QDateTime &userActionTime,
+ const HandlerInfo &handlerInfo)
+{
+ Q_ASSERT(dbusHandlerInvoked);
+
+ QString errorMessage;
+
+ ChannelPtr oldChannel = channel();
+ if (channels.size() != 1 || requestsSatisfied.size() != 1) {
+ errorMessage = QLatin1String("Only one channel and one channel request should be given "
+ "to HandleChannels");
+ } else if (account != mAccount) {
+ errorMessage = QLatin1String("Account received is not the same as the account which made "
+ "the request");
+ } else if (oldChannel && oldChannel != channels.first()) {
+ errorMessage = QLatin1String("Received a channel that is not the same as the first "
+ "one received");
+ }
+
+ if (!errorMessage.isEmpty()) {
+ warning() << "Handling channel failed with" << TP_QT_ERROR_SERVICE_CONFUSED << ":" <<
+ errorMessage;
+
+ // Only emit error if we didn't receive any channel yet.
+ if (!oldChannel) {
+ emit error(TP_QT_ERROR_SERVICE_CONFUSED, errorMessage);
+ }
+ context->setFinishedWithError(TP_QT_ERROR_SERVICE_CONFUSED, errorMessage);
+ return;
+ }
+
+ ChannelRequestPtr channelRequest = requestsSatisfied.first();
+
+ if (!oldChannel) {
+ mChannel = QWeakPointer<Channel>(channels.first().data());
+ emit channelReceived(channel(), userActionTime, channelRequest->hints());
+ } else {
+ if (mQueueChannelReceived) {
+ mChannelReceivedQueue.enqueue(qMakePair(userActionTime, channelRequest->hints()));
+ } else {
+ emit channelReceived(oldChannel, userActionTime, channelRequest->hints());
+ }
+ }
+
+ context->setFinished();
+}
+
+void RequestTemporaryHandler::setQueueChannelReceived(bool queue)
+{
+ mQueueChannelReceived = queue;
+ if (!queue) {
+ processChannelReceivedQueue();
+ }
+}
+
+void RequestTemporaryHandler::setDBusHandlerInvoked()
+{
+ dbusHandlerInvoked = true;
+}
+
+void RequestTemporaryHandler::setDBusHandlerErrored(const QString &errorName, const QString &errorMessage)
+{
+ Q_ASSERT(dbusHandlerInvoked);
+ if (!channel()) {
+ emit error(errorName, errorMessage);
+ }
+}
+
+void RequestTemporaryHandler::processChannelReceivedQueue()
+{
+ while (!mChannelReceivedQueue.isEmpty()) {
+ QPair<QDateTime, ChannelRequestHints> info = mChannelReceivedQueue.dequeue();
+ emit channelReceived(channel(), info.first, info.second);
+ }
+}
+
+} // Tp
diff --git a/TelepathyQt/request-temporary-handler-internal.h b/TelepathyQt/request-temporary-handler-internal.h
new file mode 100644
index 00000000..43eb87d3
--- /dev/null
+++ b/TelepathyQt/request-temporary-handler-internal.h
@@ -0,0 +1,91 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_request_temporary_handler_internal_h_HEADER_GUARD_
+#define _TelepathyQt_request_temporary_handler_internal_h_HEADER_GUARD_
+
+#include <QWeakPointer>
+
+#include <TelepathyQt/AbstractClientHandler>
+#include <TelepathyQt/Account>
+#include <TelepathyQt/Channel>
+
+namespace Tp
+{
+
+class TP_QT_NO_EXPORT RequestTemporaryHandler : public QObject, public AbstractClientHandler
+{
+ Q_OBJECT
+
+public:
+ static SharedPtr<RequestTemporaryHandler> create(const AccountPtr &account);
+
+ ~RequestTemporaryHandler();
+
+ AccountPtr account() const { return mAccount; }
+ ChannelPtr channel() const { return ChannelPtr(mChannel); }
+
+ /**
+ * Handlers we request ourselves never go through the approvers but this
+ * handler shouldn't get any channels we didn't request - hence let's make
+ * this always false to leave slightly less room for the CD to get confused and
+ * give some channel we didn't request to us, without even asking an approver
+ * first. Though if the CD isn't confused it shouldn't really matter - our filter
+ * is empty anyway.
+ */
+ bool bypassApproval() const { return false; }
+
+ void handleChannels(const MethodInvocationContextPtr<> &context,
+ const AccountPtr &account,
+ const ConnectionPtr &connection,
+ const QList<ChannelPtr> &channels,
+ const QList<ChannelRequestPtr> &requestsSatisfied,
+ const QDateTime &userActionTime,
+ const HandlerInfo &handlerInfo);
+
+ void setQueueChannelReceived(bool queue);
+
+ void setDBusHandlerInvoked();
+ void setDBusHandlerErrored(const QString &errorName, const QString &errorMessage);
+
+ bool isDBusHandlerInvoked() const { return dbusHandlerInvoked; }
+
+Q_SIGNALS:
+ void error(const QString &errorName, const QString &errorMessage);
+ void channelReceived(const Tp::ChannelPtr &channel, const QDateTime &userActionTime,
+ const Tp::ChannelRequestHints &requestHints);
+
+private:
+ RequestTemporaryHandler(const AccountPtr &account);
+
+ void processChannelReceivedQueue();
+
+ AccountPtr mAccount;
+ QWeakPointer<Channel> mChannel;
+ bool mQueueChannelReceived;
+ QQueue<QPair<QDateTime, ChannelRequestHints> > mChannelReceivedQueue;
+ bool dbusHandlerInvoked;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/requestable-channel-class-spec.cpp b/TelepathyQt/requestable-channel-class-spec.cpp
new file mode 100644
index 00000000..bf8bbc4e
--- /dev/null
+++ b/TelepathyQt/requestable-channel-class-spec.cpp
@@ -0,0 +1,494 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/RequestableChannelClassSpec>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT RequestableChannelClassSpec::Private : public QSharedData
+{
+ Private(const RequestableChannelClass &rcc)
+ : rcc(rcc) {}
+
+ RequestableChannelClass rcc;
+};
+
+/**
+ * \class RequestableChannelClassSpec
+ * \ingroup wrappers
+ * \headerfile TelepathyQt/requestable-channel-class-spec.h <TelepathyQt/RequestableChannelClassSpec>
+ *
+ * \brief The RequestableChannelClassSpec class represents a Telepathy
+ * requestable channel class.
+ */
+
+RequestableChannelClassSpec::RequestableChannelClassSpec(const RequestableChannelClass &rcc)
+ : mPriv(new Private(rcc))
+{
+}
+
+RequestableChannelClassSpec::RequestableChannelClassSpec()
+{
+}
+
+RequestableChannelClassSpec::RequestableChannelClassSpec(const RequestableChannelClassSpec &other)
+ : mPriv(other.mPriv)
+{
+}
+
+RequestableChannelClassSpec::~RequestableChannelClassSpec()
+{
+}
+
+RequestableChannelClassSpec RequestableChannelClassSpec::textChat()
+{
+ static RequestableChannelClassSpec spec;
+
+ if (!spec.isValid()) {
+ RequestableChannelClass rcc;
+ rcc.fixedProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType"),
+ TP_QT_IFACE_CHANNEL_TYPE_TEXT);
+ rcc.fixedProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandleType"),
+ (uint) HandleTypeContact);
+ spec = RequestableChannelClassSpec(rcc);
+ }
+
+ return spec;
+}
+
+RequestableChannelClassSpec RequestableChannelClassSpec::textChatroom()
+{
+ static RequestableChannelClassSpec spec;
+
+ if (!spec.isValid()) {
+ RequestableChannelClass rcc;
+ rcc.fixedProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType"),
+ TP_QT_IFACE_CHANNEL_TYPE_TEXT);
+ rcc.fixedProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandleType"),
+ (uint) HandleTypeRoom);
+ spec = RequestableChannelClassSpec(rcc);
+ }
+
+ return spec;
+}
+
+RequestableChannelClassSpec RequestableChannelClassSpec::streamedMediaCall()
+{
+ static RequestableChannelClassSpec spec;
+
+ if (!spec.isValid()) {
+ RequestableChannelClass rcc;
+ rcc.fixedProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType"),
+ TP_QT_IFACE_CHANNEL_TYPE_STREAMED_MEDIA);
+ rcc.fixedProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandleType"),
+ (uint) HandleTypeContact);
+ spec = RequestableChannelClassSpec(rcc);
+ }
+
+ return spec;
+}
+
+RequestableChannelClassSpec RequestableChannelClassSpec::streamedMediaAudioCall()
+{
+ static RequestableChannelClassSpec spec;
+
+ if (!spec.isValid()) {
+ RequestableChannelClass rcc;
+ rcc.fixedProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType"),
+ TP_QT_IFACE_CHANNEL_TYPE_STREAMED_MEDIA);
+ rcc.fixedProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandleType"),
+ (uint) HandleTypeContact);
+ rcc.allowedProperties.append(
+ TP_QT_IFACE_CHANNEL_TYPE_STREAMED_MEDIA + QLatin1String(".InitialAudio"));
+ spec = RequestableChannelClassSpec(rcc);
+ }
+
+ return spec;
+}
+
+RequestableChannelClassSpec RequestableChannelClassSpec::streamedMediaVideoCall()
+{
+ static RequestableChannelClassSpec spec;
+
+ if (!spec.isValid()) {
+ RequestableChannelClass rcc;
+ rcc.fixedProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType"),
+ TP_QT_IFACE_CHANNEL_TYPE_STREAMED_MEDIA);
+ rcc.fixedProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandleType"),
+ (uint) HandleTypeContact);
+ rcc.allowedProperties.append(
+ TP_QT_IFACE_CHANNEL_TYPE_STREAMED_MEDIA + QLatin1String(".InitialVideo"));
+ spec = RequestableChannelClassSpec(rcc);
+ }
+
+ return spec;
+}
+
+RequestableChannelClassSpec RequestableChannelClassSpec::streamedMediaVideoCallWithAudio()
+{
+ static RequestableChannelClassSpec spec;
+
+ if (!spec.isValid()) {
+ RequestableChannelClass rcc;
+ rcc.fixedProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType"),
+ TP_QT_IFACE_CHANNEL_TYPE_STREAMED_MEDIA);
+ rcc.fixedProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandleType"),
+ (uint) HandleTypeContact);
+ rcc.allowedProperties.append(
+ TP_QT_IFACE_CHANNEL_TYPE_STREAMED_MEDIA + QLatin1String(".InitialAudio"));
+ rcc.allowedProperties.append(
+ TP_QT_IFACE_CHANNEL_TYPE_STREAMED_MEDIA + QLatin1String(".InitialVideo"));
+ spec = RequestableChannelClassSpec(rcc);
+ }
+
+ return spec;
+}
+
+RequestableChannelClassSpec RequestableChannelClassSpec::fileTransfer()
+{
+ static RequestableChannelClassSpec spec;
+
+ if (!spec.isValid()) {
+ RequestableChannelClass rcc;
+ rcc.fixedProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType"),
+ TP_QT_IFACE_CHANNEL_TYPE_FILE_TRANSFER);
+ rcc.fixedProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandleType"),
+ (uint) HandleTypeContact);
+ spec = RequestableChannelClassSpec(rcc);
+ }
+
+ return spec;
+}
+
+RequestableChannelClassSpec RequestableChannelClassSpec::conferenceTextChat()
+{
+ static RequestableChannelClassSpec spec;
+
+ if (!spec.isValid()) {
+ RequestableChannelClass rcc;
+ rcc.fixedProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType"),
+ TP_QT_IFACE_CHANNEL_TYPE_TEXT);
+ rcc.allowedProperties.append(
+ TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE + QLatin1String(".InitialChannels"));
+ spec = RequestableChannelClassSpec(rcc);
+ }
+
+ return spec;
+}
+
+RequestableChannelClassSpec RequestableChannelClassSpec::conferenceTextChatWithInvitees()
+{
+ static RequestableChannelClassSpec spec;
+
+ if (!spec.isValid()) {
+ RequestableChannelClass rcc;
+ rcc.fixedProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType"),
+ TP_QT_IFACE_CHANNEL_TYPE_TEXT);
+ rcc.allowedProperties.append(
+ TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE + QLatin1String(".InitialChannels"));
+ rcc.allowedProperties.append(
+ TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE + QLatin1String(".InitialInviteeHandles"));
+ spec = RequestableChannelClassSpec(rcc);
+ }
+
+ return spec;
+}
+
+RequestableChannelClassSpec RequestableChannelClassSpec::conferenceTextChatroom()
+{
+ static RequestableChannelClassSpec spec;
+
+ if (!spec.isValid()) {
+ RequestableChannelClass rcc;
+ rcc.fixedProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType"),
+ TP_QT_IFACE_CHANNEL_TYPE_TEXT);
+ rcc.fixedProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandleType"),
+ (uint) HandleTypeRoom);
+
+ rcc.allowedProperties.append(
+ TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE + QLatin1String(".InitialChannels"));
+ spec = RequestableChannelClassSpec(rcc);
+ }
+
+ return spec;
+}
+
+RequestableChannelClassSpec RequestableChannelClassSpec::conferenceTextChatroomWithInvitees()
+{
+ static RequestableChannelClassSpec spec;
+
+ if (!spec.isValid()) {
+ RequestableChannelClass rcc;
+ rcc.fixedProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType"),
+ TP_QT_IFACE_CHANNEL_TYPE_TEXT);
+ rcc.fixedProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandleType"),
+ (uint) HandleTypeRoom);
+
+ rcc.allowedProperties.append(
+ TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE + QLatin1String(".InitialChannels"));
+ rcc.allowedProperties.append(
+ TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE + QLatin1String(".InitialInviteeHandles"));
+ spec = RequestableChannelClassSpec(rcc);
+ }
+
+ return spec;
+}
+
+RequestableChannelClassSpec RequestableChannelClassSpec::conferenceStreamedMediaCall()
+{
+ static RequestableChannelClassSpec spec;
+
+ if (!spec.isValid()) {
+ RequestableChannelClass rcc;
+ rcc.fixedProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType"),
+ TP_QT_IFACE_CHANNEL_TYPE_STREAMED_MEDIA);
+ rcc.allowedProperties.append(
+ TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE + QLatin1String(".InitialChannels"));
+ spec = RequestableChannelClassSpec(rcc);
+ }
+
+ return spec;
+}
+
+RequestableChannelClassSpec RequestableChannelClassSpec::conferenceStreamedMediaCallWithInvitees()
+{
+ static RequestableChannelClassSpec spec;
+
+ if (!spec.isValid()) {
+ RequestableChannelClass rcc;
+ rcc.fixedProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType"),
+ TP_QT_IFACE_CHANNEL_TYPE_STREAMED_MEDIA);
+ rcc.allowedProperties.append(
+ TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE + QLatin1String(".InitialChannels"));
+ rcc.allowedProperties.append(
+ TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE + QLatin1String(".InitialInviteeHandles"));
+ spec = RequestableChannelClassSpec(rcc);
+ }
+
+ return spec;
+}
+
+RequestableChannelClassSpec RequestableChannelClassSpec::contactSearch()
+{
+ static RequestableChannelClassSpec spec;
+
+ if (!spec.isValid()) {
+ RequestableChannelClass rcc;
+ rcc.fixedProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType"),
+ TP_QT_IFACE_CHANNEL_TYPE_CONTACT_SEARCH);
+ spec = RequestableChannelClassSpec(rcc);
+ }
+
+ return spec;
+}
+
+RequestableChannelClassSpec RequestableChannelClassSpec::contactSearchWithSpecificServer()
+{
+ static RequestableChannelClassSpec spec;
+
+ if (!spec.isValid()) {
+ RequestableChannelClass rcc;
+ rcc.fixedProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType"),
+ TP_QT_IFACE_CHANNEL_TYPE_CONTACT_SEARCH);
+ rcc.allowedProperties.append(
+ TP_QT_IFACE_CHANNEL_TYPE_CONTACT_SEARCH + QLatin1String(".Server"));
+ spec = RequestableChannelClassSpec(rcc);
+ }
+
+ return spec;
+}
+
+RequestableChannelClassSpec RequestableChannelClassSpec::contactSearchWithLimit()
+{
+ static RequestableChannelClassSpec spec;
+
+ if (!spec.isValid()) {
+ RequestableChannelClass rcc;
+ rcc.fixedProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType"),
+ TP_QT_IFACE_CHANNEL_TYPE_CONTACT_SEARCH);
+ rcc.allowedProperties.append(
+ TP_QT_IFACE_CHANNEL_TYPE_CONTACT_SEARCH + QLatin1String(".Limit"));
+ spec = RequestableChannelClassSpec(rcc);
+ }
+
+ return spec;
+}
+
+RequestableChannelClassSpec RequestableChannelClassSpec::contactSearchWithSpecificServerAndLimit()
+{
+ static RequestableChannelClassSpec spec;
+
+ if (!spec.isValid()) {
+ RequestableChannelClass rcc;
+ rcc.fixedProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType"),
+ TP_QT_IFACE_CHANNEL_TYPE_CONTACT_SEARCH);
+ rcc.allowedProperties.append(
+ TP_QT_IFACE_CHANNEL_TYPE_CONTACT_SEARCH + QLatin1String(".Server"));
+ rcc.allowedProperties.append(
+ TP_QT_IFACE_CHANNEL_TYPE_CONTACT_SEARCH + QLatin1String(".Limit"));
+ spec = RequestableChannelClassSpec(rcc);
+ }
+
+ return spec;
+}
+
+RequestableChannelClassSpec RequestableChannelClassSpec::streamTube(const QString &service)
+{
+ static RequestableChannelClassSpec spec;
+
+ if (!spec.isValid()) {
+ RequestableChannelClass rcc;
+ rcc.fixedProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType"),
+ TP_QT_IFACE_CHANNEL_TYPE_STREAM_TUBE);
+ rcc.fixedProperties.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandleType"),
+ (uint) HandleTypeContact);
+ spec = RequestableChannelClassSpec(rcc);
+ }
+
+ if (service.isEmpty()) {
+ return spec;
+ }
+
+ RequestableChannelClass rcc = spec.bareClass();
+ rcc.fixedProperties.insert(TP_QT_IFACE_CHANNEL_TYPE_STREAM_TUBE + QLatin1String(".Service"),
+ service);
+ return RequestableChannelClassSpec(rcc);
+}
+
+RequestableChannelClassSpec &RequestableChannelClassSpec::operator=(const RequestableChannelClassSpec &other)
+{
+ this->mPriv = other.mPriv;
+ return *this;
+}
+
+bool RequestableChannelClassSpec::operator==(const RequestableChannelClassSpec &other) const
+{
+ if (!isValid() || !other.isValid()) {
+ if (!isValid() && !other.isValid()) {
+ return true;
+ }
+ return false;
+ }
+
+ return mPriv->rcc == other.mPriv->rcc;
+}
+
+bool RequestableChannelClassSpec::supports(const RequestableChannelClassSpec &other) const
+{
+ if (!isValid()) {
+ return false;
+ }
+
+ if (mPriv->rcc.fixedProperties == other.fixedProperties()) {
+ foreach (const QString &prop, other.allowedProperties()) {
+ if (!mPriv->rcc.allowedProperties.contains(prop)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+QString RequestableChannelClassSpec::channelType() const
+{
+ if (!isValid()) {
+ return QString();
+ }
+ return qdbus_cast<QString>(mPriv->rcc.fixedProperties.value(
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType")));
+}
+
+bool RequestableChannelClassSpec::hasTargetHandleType() const
+{
+ if (!isValid()) {
+ return false;
+ }
+ return mPriv->rcc.fixedProperties.contains(
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType"));
+}
+
+HandleType RequestableChannelClassSpec::targetHandleType() const
+{
+ if (!hasTargetHandleType()) {
+ return (HandleType) -1;
+ }
+ return (HandleType) qdbus_cast<uint>(mPriv->rcc.fixedProperties.value(
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType")));
+}
+
+bool RequestableChannelClassSpec::hasFixedProperty(const QString &name) const
+{
+ if (!isValid()) {
+ return false;
+ }
+ return mPriv->rcc.fixedProperties.contains(name);
+}
+
+QVariant RequestableChannelClassSpec::fixedProperty(const QString &name) const
+{
+ if (!isValid()) {
+ return QVariant();
+ }
+ return mPriv->rcc.fixedProperties.value(name);
+}
+
+QVariantMap RequestableChannelClassSpec::fixedProperties() const
+{
+ if (!isValid()) {
+ return QVariantMap();
+ }
+ return mPriv->rcc.fixedProperties;
+}
+
+bool RequestableChannelClassSpec::allowsProperty(const QString &name) const
+{
+ if (!isValid()) {
+ return false;
+ }
+ return mPriv->rcc.allowedProperties.contains(name);
+}
+
+QStringList RequestableChannelClassSpec::allowedProperties() const
+{
+ if (!isValid()) {
+ return QStringList();
+ }
+ return mPriv->rcc.allowedProperties;
+}
+
+RequestableChannelClass RequestableChannelClassSpec::bareClass() const
+{
+ return isValid() ? mPriv->rcc : RequestableChannelClass();
+}
+
+/**
+ * \class RequestableChannelClassSpecList
+ * \ingroup wrappers
+ * \headerfile TelepathyQt/requestable-channel-class-spec.h <TelepathyQt/RequestableChannelClassSpecList>
+ *
+ * \brief The RequestableChannelClassSpecList class represents a list of
+ * RequestableChannelClassSpec.
+ */
+
+} // Tp
diff --git a/TelepathyQt/requestable-channel-class-spec.h b/TelepathyQt/requestable-channel-class-spec.h
new file mode 100644
index 00000000..752f2e4c
--- /dev/null
+++ b/TelepathyQt/requestable-channel-class-spec.h
@@ -0,0 +1,134 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_requestable_channel_class_spec_h_HEADER_GUARD_
+#define _TelepathyQt_requestable_channel_class_spec_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+class TP_QT_EXPORT RequestableChannelClassSpec
+{
+public:
+ RequestableChannelClassSpec();
+ RequestableChannelClassSpec(const RequestableChannelClass &rcc);
+ RequestableChannelClassSpec(const RequestableChannelClassSpec &other);
+ ~RequestableChannelClassSpec();
+
+ static RequestableChannelClassSpec textChat();
+ static RequestableChannelClassSpec textChatroom();
+
+ static RequestableChannelClassSpec streamedMediaCall();
+ static RequestableChannelClassSpec streamedMediaAudioCall();
+ static RequestableChannelClassSpec streamedMediaVideoCall();
+ static RequestableChannelClassSpec streamedMediaVideoCallWithAudio();
+
+ static RequestableChannelClassSpec fileTransfer();
+
+ static RequestableChannelClassSpec conferenceTextChat();
+ static RequestableChannelClassSpec conferenceTextChatWithInvitees();
+ static RequestableChannelClassSpec conferenceTextChatroom();
+ static RequestableChannelClassSpec conferenceTextChatroomWithInvitees();
+ static RequestableChannelClassSpec conferenceStreamedMediaCall();
+ static RequestableChannelClassSpec conferenceStreamedMediaCallWithInvitees();
+
+ static RequestableChannelClassSpec contactSearch();
+ static RequestableChannelClassSpec contactSearchWithSpecificServer();
+ static RequestableChannelClassSpec contactSearchWithLimit();
+ static RequestableChannelClassSpec contactSearchWithSpecificServerAndLimit();
+
+ static RequestableChannelClassSpec streamTube(const QString &service = QString());
+
+ bool isValid() const { return mPriv.constData() != 0; }
+
+ RequestableChannelClassSpec &operator=(const RequestableChannelClassSpec &other);
+ bool operator==(const RequestableChannelClassSpec &other) const;
+
+ bool supports(const RequestableChannelClassSpec &spec) const;
+
+ QString channelType() const;
+
+ bool hasTargetHandleType() const;
+ HandleType targetHandleType() const;
+
+ bool hasFixedProperty(const QString &name) const;
+ QVariant fixedProperty(const QString &name) const;
+ QVariantMap fixedProperties() const;
+
+ bool allowsProperty(const QString &name) const;
+ QStringList allowedProperties() const;
+
+ RequestableChannelClass bareClass() const;
+
+private:
+ struct Private;
+ friend struct Private;
+ QSharedDataPointer<Private> mPriv;
+};
+
+class TP_QT_EXPORT RequestableChannelClassSpecList :
+ public QList<RequestableChannelClassSpec>
+{
+public:
+ RequestableChannelClassSpecList() { }
+ RequestableChannelClassSpecList(const RequestableChannelClass &rcc)
+ {
+ append(RequestableChannelClassSpec(rcc));
+ }
+ RequestableChannelClassSpecList(const RequestableChannelClassList &rccs)
+ {
+ Q_FOREACH (const RequestableChannelClass &rcc, rccs) {
+ append(RequestableChannelClassSpec(rcc));
+ }
+ }
+ RequestableChannelClassSpecList(const RequestableChannelClassSpec &rccSpec)
+ {
+ append(rccSpec);
+ }
+ RequestableChannelClassSpecList(const QList<RequestableChannelClassSpec> &other)
+ : QList<RequestableChannelClassSpec>(other)
+ {
+ }
+
+ RequestableChannelClassList bareClasses() const
+ {
+ RequestableChannelClassList list;
+ Q_FOREACH (const RequestableChannelClassSpec &rccSpec, *this) {
+ list.append(rccSpec.bareClass());
+ }
+ return list;
+ }
+};
+
+} // Tp
+
+Q_DECLARE_METATYPE(Tp::RequestableChannelClassSpec);
+Q_DECLARE_METATYPE(Tp::RequestableChannelClassSpecList);
+
+#endif
diff --git a/TelepathyQt/room-list-channel.cpp b/TelepathyQt/room-list-channel.cpp
new file mode 100644
index 00000000..9c7aec55
--- /dev/null
+++ b/TelepathyQt/room-list-channel.cpp
@@ -0,0 +1,106 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/RoomListChannel>
+
+#include "TelepathyQt/_gen/room-list-channel.moc.hpp"
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/Connection>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT RoomListChannel::Private
+{
+ inline Private();
+ inline ~Private();
+};
+
+RoomListChannel::Private::Private()
+{
+}
+
+RoomListChannel::Private::~Private()
+{
+}
+
+/**
+ * \class RoomListChannel
+ * \ingroup clientchannel
+ * \headerfile TelepathyQt/room-list-channel.h <TelepathyQt/RoomListChannel>
+ *
+ * \brief The RoomListChannel class represents a Telepathy Channel of type RoomList.
+ *
+ * Note that this subclass of Channel will eventually provide a high-level API for the
+ * RoomList interface. Until then, it's just a Channel.
+ *
+ * For more details, please refer to \telepathy_spec.
+ *
+ * See \ref async_model, \ref shared_ptr
+ */
+
+/**
+ * Create a new RoomListChannel object.
+ *
+ * \param connection Connection owning this channel, and specifying the
+ * service.
+ * \param objectPath The channel object path.
+ * \param immutableProperties The channel immutable properties.
+ * \return A RoomListChannelPtr object pointing to the newly created
+ * RoomListChannel object.
+ */
+RoomListChannelPtr RoomListChannel::create(const ConnectionPtr &connection,
+ const QString &objectPath, const QVariantMap &immutableProperties)
+{
+ return RoomListChannelPtr(new RoomListChannel(connection, objectPath,
+ immutableProperties));
+}
+
+/**
+ * Construct a new RoomListChannel object.
+ *
+ * \param connection Connection owning this channel, and specifying the
+ * service.
+ * \param objectPath The channel object path.
+ * \param immutableProperties The channel immutable properties.
+ * \param coreFeature The core feature of the channel type, if any. The corresponding introspectable should
+ * should depend on Channel::FeatureCore.
+ */
+RoomListChannel::RoomListChannel(const ConnectionPtr &connection,
+ const QString &objectPath,
+ const QVariantMap &immutableProperties,
+ const Feature &coreFeature)
+ : Channel(connection, objectPath, immutableProperties, coreFeature),
+ mPriv(new Private())
+{
+}
+
+/**
+ * Class destructor.
+ */
+RoomListChannel::~RoomListChannel()
+{
+ delete mPriv;
+}
+
+} // Tp
diff --git a/TelepathyQt/room-list-channel.h b/TelepathyQt/room-list-channel.h
new file mode 100644
index 00000000..1c9a47a6
--- /dev/null
+++ b/TelepathyQt/room-list-channel.h
@@ -0,0 +1,59 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_room_list_channel_h_HEADER_GUARD_
+#define _TelepathyQt_room_list_channel_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Channel>
+
+namespace Tp
+{
+
+class TP_QT_EXPORT RoomListChannel : public Channel
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(RoomListChannel)
+
+public:
+ static RoomListChannelPtr create(const ConnectionPtr &connection,
+ const QString &objectPath, const QVariantMap &immutableProperties);
+
+ virtual ~RoomListChannel();
+
+protected:
+ RoomListChannel(const ConnectionPtr &connection, const QString &objectPath,
+ const QVariantMap &immutableProperties,
+ const Feature &coreFeature = Channel::FeatureCore);
+
+private:
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/shared-ptr.dox b/TelepathyQt/shared-ptr.dox
new file mode 100644
index 00000000..ce21dc63
--- /dev/null
+++ b/TelepathyQt/shared-ptr.dox
@@ -0,0 +1,111 @@
+/*
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2011 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * \page shared_ptr Shared Pointer Usage
+ *
+ * \section shared_ptr_overview Overview
+ *
+ * The Qt parent/child object model does not fit well with Telepathy-Qt4 object
+ * model, where in some places we either don't know the object parent or we
+ * can't use a parent, as the object can stay alive without it.
+ *
+ * To avoid memory leaks, caused by objects that got instantiated and don't have
+ * any parent, we decided to make some of our objects reference counted, by
+ * making them inherit SharedData.
+ *
+ * Making the object reference counted, does not guarantee that it will get
+ * deleted when nobody is referencing it.
+ *
+ * When instantiating new classes that inherits SharedData the reference count
+ * is 0, this is referred to as the floating state. Again this may lead to
+ * memory leaks, caused by objects in the floating state that never get deleted.
+ *
+ * So the solution is to put the object in a SharedPtr as soon as possible,
+ * letting the SharedPtr manage the object lifetime.
+ *
+ * The pattern used is that classes inherit SharedData and are used
+ * together with SharedPtr. When the reference count hits 0, the object
+ * is deleted.
+ *
+ * In order to assure that the object is put in a SharedPtr as soon as possible,
+ * our objects inheriting SharedData will have the constructor either private
+ * or protected, in case we want to support custom classes, and will have a
+ * public static create method that will return a SharedPtr pointing to the
+ * object instance.
+ *
+ * Note that when developing custom classes, this pattern should be followed,
+ * to avoid objects in floating state, avoiding memory leaks.
+ */
+
+/**
+ * \class Tp::RefCounted
+ * \ingroup utils
+ * \headerfile TelepathyQt/shared-ptr.h <TelepathyQt/RefCounted>
+ *
+ * \brief The RefCounted class is a base class for shared objects used by
+ * SharedPtr.
+ *
+ * See \ref shared_ptr
+ */
+
+/**
+ * \class Tp::SharedPtr
+ * \ingroup utils
+ * \headerfile TelepathyQt/shared-ptr.h <TelepathyQt/SharedPtr>
+ *
+ * \brief The SharedPtr class is a pointer to an explicitly shared object.
+ *
+ * See \ref shared_ptr
+ */
+
+/**
+ * \fn static SharedPtr<T> Tp::SharedPtr<T>::dynamicCast(const SharedPtr<X> &)
+ *
+ * Casts the pointer given by src to a pointer pointing to an object of type T. The cast will
+ * succeed if the C++ runtime type identification mechanism considers the type T to be the actual
+ * runtime type of the object pointed to by src or one of its (possibly indirect) parent classes.
+ * Otherwise, a null pointer is returned.
+ *
+ * Note that this also allows down-casting a baseclass pointer to a subclass pointer.
+ *
+ * This cast method should not be used for QObject-derived classes, as Qt provides a more portable
+ * and efficient type identification mechanism, which is used by qObjectCast().
+ *
+ * This cast method requires the C++ dynamic runtime type identification facility to be enabled
+ * (which might be disabled by eg. the -fno-rtti flag of the GNU G++ compiler).
+ */
+
+/**
+ * \fn static SharedPtr<T> Tp::SharedPtr<T>::qObjectCast(const SharedPtr<X> &)
+ *
+ * Casts the pointer given by src to a pointer pointing to an object of type T. The cast will
+ * succeed if the Qt runtime type identification mechanism considers the type T to be the actual
+ * runtime type of the object pointed to by src or one of its (possibly indirect) parent classes.
+ * Otherwise, a null pointer is returned.
+ *
+ * Note that this also allows down-casting a baseclass pointer to a subclass pointer.
+ *
+ * This cast method MUST not be used for classes not derived from QObject. However, dynamicCast()
+ * provides the same semantics for all classes, provided the C++ runtime type identification
+ * facility is enabled. This method, on the other hand, doesn't require the standard C++ facility
+ * and is probably also faster for the types it can be used with.
+ */
diff --git a/TelepathyQt/shared-ptr.h b/TelepathyQt/shared-ptr.h
new file mode 100644
index 00000000..69119980
--- /dev/null
+++ b/TelepathyQt/shared-ptr.h
@@ -0,0 +1,152 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_shared_ptr_h_HEADER_GUARD_
+#define _TelepathyQt_shared_ptr_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Global>
+
+#include <QHash>
+#include <QWeakPointer>
+
+namespace Tp
+{
+
+class RefCounted;
+template <class T> class SharedPtr;
+
+class TP_QT_EXPORT RefCounted
+{
+ Q_DISABLE_COPY(RefCounted)
+
+public:
+ inline RefCounted() : strongref(0) { }
+ inline virtual ~RefCounted() { }
+
+ inline void ref() const { strongref.ref(); }
+ inline bool deref() const { return strongref.deref(); }
+
+ mutable QAtomicInt strongref;
+};
+
+template <class T>
+class SharedPtr
+{
+ typedef bool (SharedPtr<T>::*UnspecifiedBoolType)() const;
+
+public:
+ inline SharedPtr() : d(0) { }
+ explicit inline SharedPtr(T *d) : d(d) { if (d) { d->ref(); } }
+ template <typename Subclass>
+ inline SharedPtr(const SharedPtr<Subclass> &o) : d(o.data()) { if (d) { d->ref(); } }
+ inline SharedPtr(const SharedPtr<T> &o) : d(o.d) { if (d) { d->ref(); } }
+ explicit inline SharedPtr(const QWeakPointer<T> &o)
+ {
+ if (o.data() && o.data()->strongref > 0) {
+ d = static_cast<T*>(o.data());
+ d->ref();
+ } else {
+ d = 0;
+ }
+ }
+ inline ~SharedPtr()
+ {
+ if (d && !d->deref()) {
+ T *saved = d;
+ d = 0;
+ delete saved;
+ }
+ }
+
+ inline void reset()
+ {
+ SharedPtr<T>().swap(*this);
+ }
+
+ inline T *data() const { return d; }
+ inline const T *constData() const { return d; }
+ inline T *operator->() { return d; }
+ inline T *operator->() const { return d; }
+
+ inline bool isNull() const { return !d; }
+ inline bool operator!() const { return isNull(); }
+ operator UnspecifiedBoolType() const { return !isNull() ? &SharedPtr<T>::operator! : 0; }
+
+ inline bool operator==(const SharedPtr<T> &o) const { return d == o.d; }
+ inline bool operator!=(const SharedPtr<T> &o) const { return d != o.d; }
+ inline bool operator==(const T *ptr) const { return d == ptr; }
+ inline bool operator!=(const T *ptr) const { return d != ptr; }
+
+ inline SharedPtr<T> &operator=(const SharedPtr<T> &o)
+ {
+ SharedPtr<T>(o).swap(*this);
+ return *this;
+ }
+
+ inline void swap(SharedPtr<T> &o)
+ {
+ T *tmp = d;
+ d = o.d;
+ o.d = tmp;
+ }
+
+ template <class X>
+ static inline SharedPtr<T> staticCast(const SharedPtr<X> &src)
+ {
+ return SharedPtr<T>(static_cast<T*>(src.data()));
+ }
+
+ template <class X>
+ static inline SharedPtr<T> dynamicCast(const SharedPtr<X> &src)
+ {
+ return SharedPtr<T>(dynamic_cast<T*>(src.data()));
+ }
+
+ template <class X>
+ static inline SharedPtr<T> constCast(const SharedPtr<X> &src)
+ {
+ return SharedPtr<T>(const_cast<T*>(src.data()));
+ }
+
+ template <class X>
+ static inline SharedPtr<T> qObjectCast(const SharedPtr<X> &src)
+ {
+ return SharedPtr<T>(qobject_cast<T*>(src.data()));
+ }
+
+private:
+ T *d;
+};
+
+template<typename T>
+inline uint qHash(const SharedPtr<T> &ptr)
+{
+ return QT_PREPEND_NAMESPACE(qHash<T>(ptr.data()));
+}
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/simple-call-observer.cpp b/TelepathyQt/simple-call-observer.cpp
new file mode 100644
index 00000000..a62b8a40
--- /dev/null
+++ b/TelepathyQt/simple-call-observer.cpp
@@ -0,0 +1,298 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/SimpleCallObserver>
+
+#include "TelepathyQt/_gen/simple-call-observer.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/Account>
+#include <TelepathyQt/ChannelClassSpec>
+#include <TelepathyQt/ChannelClassSpecList>
+#include <TelepathyQt/Connection>
+#include <TelepathyQt/Contact>
+#include <TelepathyQt/ContactManager>
+#include <TelepathyQt/PendingContacts>
+#include <TelepathyQt/PendingComposite>
+#include <TelepathyQt/PendingReady>
+#include <TelepathyQt/PendingSuccess>
+#include <TelepathyQt/SimpleObserver>
+#include <TelepathyQt/StreamedMediaChannel>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT SimpleCallObserver::Private
+{
+ Private(SimpleCallObserver *parent, const AccountPtr &account,
+ const QString &contactIdentifier, bool requiresNormalization,
+ CallDirection direction);
+
+ SimpleCallObserver *parent;
+ AccountPtr account;
+ QString contactIdentifier;
+ CallDirection direction;
+ SimpleObserverPtr observer;
+};
+
+SimpleCallObserver::Private::Private(SimpleCallObserver *parent,
+ const AccountPtr &account,
+ const QString &contactIdentifier, bool requiresNormalization,
+ CallDirection direction)
+ : parent(parent),
+ account(account),
+ contactIdentifier(contactIdentifier),
+ direction(direction)
+{
+ debug() << "Creating a new SimpleCallObserver";
+ ChannelClassSpec channelFilter = ChannelClassSpec::streamedMediaCall();
+ if (direction == CallDirectionIncoming) {
+ channelFilter.setRequested(false);
+ } else if (direction == CallDirectionOutgoing) {
+ channelFilter.setRequested(true);
+ }
+
+ observer = SimpleObserver::create(account, ChannelClassSpecList() << channelFilter,
+ contactIdentifier, requiresNormalization, QList<ChannelClassFeatures>());
+
+ parent->connect(observer.data(),
+ SIGNAL(newChannels(QList<Tp::ChannelPtr>)),
+ SLOT(onNewChannels(QList<Tp::ChannelPtr>)));
+ parent->connect(observer.data(),
+ SIGNAL(channelInvalidated(Tp::ChannelPtr,QString,QString)),
+ SLOT(onChannelInvalidated(Tp::ChannelPtr,QString,QString)));
+}
+
+/**
+ * \class SimpleCallObserver
+ * \ingroup utils
+ * \headerfile TelepathyQt/simple-call-observer.h <TelepathyQt/SimpleCallObserver>
+ *
+ * \brief The SimpleCallObserver class provides an easy way to track calls
+ * in an account and can be optionally filtered by a contact and/or
+ * call direction.
+ */
+
+/**
+ * Create a new SimpleCallObserver object.
+ *
+ * Events will be signalled for all calls in \a account that respect \a direction.
+ *
+ * \param account The account used to listen to events.
+ * \param direction The direction of the calls used to filter events.
+ * \return An SimpleCallObserverPtr object pointing to the newly created
+ * SimpleCallObserver object.
+ */
+SimpleCallObserverPtr SimpleCallObserver::create(const AccountPtr &account,
+ CallDirection direction)
+{
+ return create(account, QString(), false, direction);
+}
+
+/**
+ * Create a new SimpleCallObserver object.
+ *
+ * Events will be signalled for all calls in \a account established with \a contact and
+ * that respect \a direction.
+ *
+ * \param account The account used to listen to events.
+ * \param contact The contact used to filter events.
+ * \param direction The direction of the calls used to filter events.
+ * \return An SimpleCallObserverPtr object pointing to the newly created
+ * SimpleCallObserver object.
+ */
+SimpleCallObserverPtr SimpleCallObserver::create(const AccountPtr &account,
+ const ContactPtr &contact,
+ CallDirection direction)
+{
+ if (contact) {
+ return create(account, contact->id(), false, direction);
+ }
+ return create(account, QString(), false, direction);
+}
+
+/**
+ * Create a new SimpleCallObserver object.
+ *
+ * Events will be signalled for all calls in \a account established with a contact identified by \a
+ * contactIdentifier and that respect \a direction.
+ *
+ * \param account The account used to listen to events.
+ * \param contactIdentifier The identifier of the contact used to filter events.
+ * \param direction The direction of the calls used to filter events.
+ * \return An SimpleCallObserverPtr object pointing to the newly created
+ * SimpleCallObserver object.
+ */
+SimpleCallObserverPtr SimpleCallObserver::create(const AccountPtr &account,
+ const QString &contactIdentifier,
+ CallDirection direction)
+{
+ return create(account, contactIdentifier, true, direction);
+}
+
+SimpleCallObserverPtr SimpleCallObserver::create(const AccountPtr &account,
+ const QString &contactIdentifier, bool requiresNormalization,
+ CallDirection direction)
+{
+ return SimpleCallObserverPtr(
+ new SimpleCallObserver(account, contactIdentifier,
+ requiresNormalization, direction));
+}
+
+/**
+ * Construct a new SimpleCallObserver object.
+ *
+ * \param account The account used to listen to events.
+ * \param contactIdentifier The identifier of the contact used to filter events.
+ * \param requiresNormalization Whether \a contactIdentifier needs to be
+ * normalized.
+ * \param direction The direction of the calls used to filter events.
+ * \return An SimpleCallObserverPtr object pointing to the newly created
+ * SimpleCallObserver object.
+ */
+SimpleCallObserver::SimpleCallObserver(const AccountPtr &account,
+ const QString &contactIdentifier, bool requiresNormalization,
+ CallDirection direction)
+ : mPriv(new Private(this, account, contactIdentifier, requiresNormalization, direction))
+{
+}
+
+/**
+ * Class destructor.
+ */
+SimpleCallObserver::~SimpleCallObserver()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the account used to listen to events.
+ *
+ * \return A pointer to the Account object.
+ */
+AccountPtr SimpleCallObserver::account() const
+{
+ return mPriv->account;
+}
+
+/**
+ * Return the identifier of the contact used to filter events, or an empty string if none was
+ * provided at construction.
+ *
+ * \return The identifier of the contact.
+ */
+QString SimpleCallObserver::contactIdentifier() const
+{
+ return mPriv->contactIdentifier;
+}
+
+/**
+ * Return the direction of the calls used to filter events.
+ *
+ * \return The direction of the calls as SimpleCallObserver::CallDirection.
+ */
+SimpleCallObserver::CallDirection SimpleCallObserver::direction() const
+{
+ return mPriv->direction;
+}
+
+/**
+ * Return the list of streamed media calls currently being observed.
+ *
+ * \return A list of pointers to StreamedMediaChannel objects.
+ */
+QList<StreamedMediaChannelPtr> SimpleCallObserver::streamedMediaCalls() const
+{
+ QList<StreamedMediaChannelPtr> ret;
+ foreach (const ChannelPtr &channel, mPriv->observer->channels()) {
+ StreamedMediaChannelPtr smChannel = StreamedMediaChannelPtr::qObjectCast(channel);
+ if (smChannel) {
+ ret << smChannel;
+ }
+ }
+ return ret;
+}
+
+void SimpleCallObserver::onNewChannels(const QList<ChannelPtr> &channels)
+{
+ foreach (const ChannelPtr &channel, channels) {
+ StreamedMediaChannelPtr smChannel = StreamedMediaChannelPtr::qObjectCast(channel);
+ if (!smChannel) {
+ if (channel->channelType() != TP_QT_IFACE_CHANNEL_TYPE_STREAMED_MEDIA) {
+ warning() << "Channel received to observe is not of type StreamedMedia, service "
+ "confused. Ignoring channel";
+ } else {
+ warning() << "Channel received to observe is not a subclass of "
+ "StreamedMediaChannel. ChannelFactory set on this observer's account must "
+ "construct StreamedMediaChannel subclasses for channels of type StreamedMedia. "
+ "Ignoring channel";
+ }
+ continue;
+ }
+
+ emit streamedMediaCallStarted(StreamedMediaChannelPtr::qObjectCast(channel));
+ }
+}
+
+void SimpleCallObserver::onChannelInvalidated(const ChannelPtr &channel,
+ const QString &errorName, const QString &errorMessage)
+{
+ StreamedMediaChannelPtr smChannel = StreamedMediaChannelPtr::qObjectCast(channel);
+ if (!smChannel) {
+ if (channel->channelType() != TP_QT_IFACE_CHANNEL_TYPE_STREAMED_MEDIA) {
+ warning() << "Channel received by this observer is not of type StreamedMedia, service "
+ "confused. Ignoring channel";
+ } else {
+ warning() << "Channel received by this observer is not a subclass of "
+ "StreamedMediaChannel. ChannelFactory set on this observer's account must "
+ "construct StreamedMediaChannel subclasses for channels of type StreamedMedia. "
+ "Ignoring channel";
+ }
+ return;
+ }
+ emit streamedMediaCallEnded(smChannel, errorName, errorMessage);
+}
+
+/**
+ * \fn void SimpleCallObserver::streamedMediaCallStarted(const Tp::StreamedMediaChannelPtr &channel)
+ *
+ * Emitted whenever a streamed media call that matches this observer's criteria is
+ * started.
+ *
+ * \param channel The channel representing the streamed media call that started.
+ */
+
+/**
+ * \fn void SimpleCallObserver::streamedMediaCallEnded(const Tp::StreamedMediaChannelPtr &channel,
+ * const QString &errorName, const QString &errorMessage)
+ *
+ * Emitted whenever a streamed media call that matches this observer's criteria has
+ * ended.
+ *
+ * \param channel The channel representing the streamed media call that ended.
+ * \param errorName A D-Bus error name (a string in a subset
+ * of ASCII, prefixed with a reversed domain name).
+ * \param errorMessage A debugging message associated with the error.
+ */
+
+} // Tp
diff --git a/TelepathyQt/simple-call-observer.h b/TelepathyQt/simple-call-observer.h
new file mode 100644
index 00000000..988d092a
--- /dev/null
+++ b/TelepathyQt/simple-call-observer.h
@@ -0,0 +1,95 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_simple_call_observer_h_HEADER_GUARD_
+#define _TelepathyQt_simple_call_observer_h_HEADER_GUARD_
+
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/Types>
+
+#include <QObject>
+
+namespace Tp
+{
+
+class PendingOperation;
+
+class TP_QT_EXPORT SimpleCallObserver : public QObject,
+ public RefCounted
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(SimpleCallObserver)
+ Q_FLAGS(CallDirection CallDirections)
+
+public:
+ enum CallDirection {
+ CallDirectionIncoming = 0x01,
+ CallDirectionOutgoing = 0x02,
+ CallDirectionBoth = CallDirectionIncoming | CallDirectionOutgoing
+ };
+ Q_DECLARE_FLAGS(CallDirections, CallDirection)
+
+ static SimpleCallObserverPtr create(const AccountPtr &account,
+ CallDirection direction = CallDirectionBoth);
+ static SimpleCallObserverPtr create(const AccountPtr &account,
+ const ContactPtr &contact,
+ CallDirection direction = CallDirectionBoth);
+ static SimpleCallObserverPtr create(const AccountPtr &account,
+ const QString &contactIdentifier,
+ CallDirection direction = CallDirectionBoth);
+
+ virtual ~SimpleCallObserver();
+
+ AccountPtr account() const;
+ QString contactIdentifier() const;
+ CallDirection direction() const;
+
+ QList<StreamedMediaChannelPtr> streamedMediaCalls() const;
+
+Q_SIGNALS:
+ void streamedMediaCallStarted(const Tp::StreamedMediaChannelPtr &channel);
+ void streamedMediaCallEnded(const Tp::StreamedMediaChannelPtr &channel,
+ const QString &errorName, const QString &errorMessage);
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onNewChannels(const QList<Tp::ChannelPtr> &channels);
+ TP_QT_NO_EXPORT void onChannelInvalidated(const Tp::ChannelPtr &channel,
+ const QString &errorName, const QString &errorMessage);
+
+private:
+ TP_QT_NO_EXPORT static SimpleCallObserverPtr create(
+ const AccountPtr &account,
+ const QString &contactIdentifier, bool requiresNormalization,
+ CallDirection direction);
+
+ TP_QT_NO_EXPORT SimpleCallObserver(const AccountPtr &account,
+ const QString &contactIdentifier, bool requiresNormalization,
+ CallDirection direction);
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/simple-observer-internal.h b/TelepathyQt/simple-observer-internal.h
new file mode 100644
index 00000000..78900cf0
--- /dev/null
+++ b/TelepathyQt/simple-observer-internal.h
@@ -0,0 +1,261 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/Account>
+#include <TelepathyQt/AccountFactory>
+#include <TelepathyQt/Channel>
+#include <TelepathyQt/ChannelClassFeatures>
+#include <TelepathyQt/ChannelClassSpec>
+#include <TelepathyQt/ChannelClassSpecList>
+#include <TelepathyQt/ClientRegistrar>
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT SimpleObserver::Private
+{
+ Private(SimpleObserver *parent,
+ const AccountPtr &account,
+ const ChannelClassSpecList &channelFilter,
+ const QString &contactIdentifier,
+ bool requiresNormalization,
+ const QList<ChannelClassFeatures> &extraChannelFeatures);
+
+ bool filterChannel(const AccountPtr &channelAccount, const ChannelPtr &channel);
+ void insertChannels(const AccountPtr &channelsAccount, const QList<ChannelPtr> &channels);
+ void removeChannel(const AccountPtr &channelAccount, const ChannelPtr &channel,
+ const QString &errorName, const QString &errorMessage);
+
+ void processChannelsQueue();
+ void processNewChannelsQueue();
+ void processChannelsInvalidationQueue();
+
+ class FakeAccountFactory;
+ class Observer;
+ class ChannelWrapper;
+ struct NewChannelsInfo;
+ struct ChannelInvalidationInfo;
+
+ SimpleObserver *parent;
+ AccountPtr account;
+ ChannelClassSpecList channelFilter;
+ QString contactIdentifier;
+ QString normalizedContactIdentifier;
+ QList<ChannelClassFeatures> extraChannelFeatures;
+ ClientRegistrarPtr cr;
+ SharedPtr<Observer> observer;
+ QSet<ChannelPtr> channels;
+ QQueue<void (SimpleObserver::Private::*)()> channelsQueue;
+ QQueue<ChannelInvalidationInfo> channelsInvalidationQueue;
+ QQueue<NewChannelsInfo> newChannelsQueue;
+ static QHash<QPair<QString, QSet<ChannelClassSpec> >, QWeakPointer<Observer> > observers;
+ static uint numObservers;
+};
+
+class TP_QT_NO_EXPORT SimpleObserver::Private::FakeAccountFactory :
+ public AccountFactory
+{
+ Q_OBJECT
+
+public:
+ static SharedPtr<FakeAccountFactory> create(const QDBusConnection &bus)
+ {
+ return SharedPtr<FakeAccountFactory>(new FakeAccountFactory(bus));
+ }
+
+ ~FakeAccountFactory() { }
+
+private:
+ friend class Observer;
+
+ FakeAccountFactory(const QDBusConnection &bus)
+ : AccountFactory(bus, Features())
+ {
+ }
+
+ AccountPtr construct(const QString &busName, const QString &objectPath,
+ const ConnectionFactoryConstPtr &connFactory,
+ const ChannelFactoryConstPtr &chanFactory,
+ const ContactFactoryConstPtr &contactFactory) const
+ {
+ if (mAccounts.contains(objectPath)) {
+ return mAccounts.value(objectPath);
+ }
+ return AccountFactory::construct(busName, objectPath, connFactory,
+ chanFactory, contactFactory);
+ }
+
+ QHash<QString, AccountPtr> accounts() const { return mAccounts; }
+ void registerAccount(const AccountPtr &account)
+ {
+ mAccounts.insert(account->objectPath(), account);
+ }
+
+ QHash<QString, AccountPtr> mAccounts;
+};
+
+class TP_QT_NO_EXPORT SimpleObserver::Private::Observer : public QObject,
+ public AbstractClientObserver
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(Observer)
+
+public:
+ struct ContextInfo
+ {
+ ContextInfo() {}
+ ContextInfo(const MethodInvocationContextPtr<> &context,
+ const AccountPtr &account,
+ const QList<ChannelPtr> &channels)
+ : context(context),
+ account(account),
+ channels(channels)
+ {
+ }
+
+ MethodInvocationContextPtr<> context;
+ AccountPtr account;
+ QList<ChannelPtr> channels;
+ };
+
+ Observer(const QWeakPointer<ClientRegistrar> &cr,
+ const SharedPtr<FakeAccountFactory> &fakeAccountFactory,
+ const ChannelClassSpecList &channelFilter,
+ const QString &observerName);
+ ~Observer();
+
+ QWeakPointer<ClientRegistrar> clientRegistrar() const { return mCr; }
+ SharedPtr<FakeAccountFactory> fakeAccountFactory() const { return mFakeAccountFactory; }
+
+ QString observerName() const { return mObserverName; }
+
+ QSet<ChannelClassFeatures> extraChannelFeatures() const { return mExtraChannelFeatures; }
+ void registerExtraChannelFeatures(const QList<ChannelClassFeatures> &features)
+ {
+ mExtraChannelFeatures.unite(features.toSet());
+ }
+
+ QSet<AccountPtr> accounts() const { return mAccounts; }
+ void registerAccount(const AccountPtr &account)
+ {
+ mAccounts.insert(account);
+ mFakeAccountFactory->registerAccount(account);
+ }
+
+ QHash<ChannelPtr, ChannelWrapper*> channels() const { return mChannels; }
+
+ void observeChannels(
+ const MethodInvocationContextPtr<> &context,
+ const AccountPtr &account,
+ const ConnectionPtr &connection,
+ const QList<ChannelPtr> &channels,
+ const ChannelDispatchOperationPtr &dispatchOperation,
+ const QList<ChannelRequestPtr> &requestsSatisfied,
+ const ObserverInfo &observerInfo);
+
+Q_SIGNALS:
+ void newChannels(const Tp::AccountPtr &channelsAccount, const QList<Tp::ChannelPtr> &channels);
+ void channelInvalidated(const Tp::AccountPtr &channelAccount, const Tp::ChannelPtr &channel,
+ const QString &errorName, const QString &errorMessage);
+
+private Q_SLOTS:
+ void onChannelInvalidated(const Tp::AccountPtr &channelAccount, const Tp::ChannelPtr &channel,
+ const QString &errorName, const QString &errorMessage);
+ void onChannelsReady(Tp::PendingOperation *op);
+
+private:
+ Features featuresFor(const ChannelClassSpec &channelClass) const;
+
+ QWeakPointer<ClientRegistrar> mCr;
+ SharedPtr<FakeAccountFactory> mFakeAccountFactory;
+ QString mObserverName;
+ QSet<ChannelClassFeatures> mExtraChannelFeatures;
+ QSet<AccountPtr> mAccounts;
+ QHash<ChannelPtr, ChannelWrapper*> mChannels;
+ QHash<ChannelPtr, ChannelWrapper*> mIncompleteChannels;
+ QHash<PendingOperation*, ContextInfo*> mObserveChannelsInfo;
+};
+
+class TP_QT_NO_EXPORT SimpleObserver::Private::ChannelWrapper :
+ public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(ChannelWrapper)
+
+public:
+ ChannelWrapper(const AccountPtr &channelAccount, const ChannelPtr &channel,
+ const Features &extraChannelFeatures, QObject *parent);
+ ~ChannelWrapper() { }
+
+ AccountPtr channelAccount() const { return mChannelAccount; }
+ ChannelPtr channel() const { return mChannel; }
+ Features extraChannelFeatures() const { return mExtraChannelFeatures; }
+
+ PendingOperation *becomeReady();
+
+Q_SIGNALS:
+ void channelInvalidated(const Tp::AccountPtr &channelAccount, const Tp::ChannelPtr &channel,
+ const QString &errorName, const QString &errorMessage);
+
+private Q_SLOTS:
+ void onChannelInvalidated(Tp::DBusProxy *proxy, const QString &errorName,
+ const QString &errorMessage);
+
+private:
+ AccountPtr mChannelAccount;
+ ChannelPtr mChannel;
+ Features mExtraChannelFeatures;
+};
+
+struct TP_QT_NO_EXPORT SimpleObserver::Private::NewChannelsInfo
+{
+ NewChannelsInfo();
+ NewChannelsInfo(const AccountPtr &channelsAccount, const QList<ChannelPtr> &channels)
+ : channelsAccount(channelsAccount),
+ channels(channels)
+ {
+ }
+
+ AccountPtr channelsAccount;
+ QList<ChannelPtr> channels;
+};
+
+struct TP_QT_NO_EXPORT SimpleObserver::Private::ChannelInvalidationInfo
+{
+ ChannelInvalidationInfo();
+ ChannelInvalidationInfo(const AccountPtr &channelAccount, const ChannelPtr &channel,
+ const QString &errorName, const QString &errorMessage)
+ : channelAccount(channelAccount),
+ channel(channel),
+ errorName(errorName),
+ errorMessage(errorMessage)
+ {
+ }
+
+ AccountPtr channelAccount;
+ ChannelPtr channel;
+ QString errorName;
+ QString errorMessage;
+};
+
+} // Tp
diff --git a/TelepathyQt/simple-observer.cpp b/TelepathyQt/simple-observer.cpp
new file mode 100644
index 00000000..44bfff19
--- /dev/null
+++ b/TelepathyQt/simple-observer.cpp
@@ -0,0 +1,643 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/SimpleObserver>
+#include "TelepathyQt/simple-observer-internal.h"
+
+#include "TelepathyQt/_gen/simple-observer.moc.hpp"
+#include "TelepathyQt/_gen/simple-observer-internal.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/ChannelClassSpec>
+#include <TelepathyQt/ChannelClassSpecList>
+#include <TelepathyQt/Connection>
+#include <TelepathyQt/Contact>
+#include <TelepathyQt/ContactManager>
+#include <TelepathyQt/PendingContacts>
+#include <TelepathyQt/PendingComposite>
+#include <TelepathyQt/PendingReady>
+#include <TelepathyQt/PendingSuccess>
+
+namespace Tp
+{
+
+QHash<QPair<QString, QSet<ChannelClassSpec> >, QWeakPointer<SimpleObserver::Private::Observer> > SimpleObserver::Private::observers;
+uint SimpleObserver::Private::numObservers = 0;
+
+SimpleObserver::Private::Private(SimpleObserver *parent,
+ const AccountPtr &account,
+ const ChannelClassSpecList &channelFilter,
+ const QString &contactIdentifier,
+ bool requiresNormalization,
+ const QList<ChannelClassFeatures> &extraChannelFeatures)
+ : parent(parent),
+ account(account),
+ channelFilter(channelFilter),
+ contactIdentifier(contactIdentifier),
+ extraChannelFeatures(extraChannelFeatures)
+{
+ QSet<ChannelClassSpec> normalizedChannelFilter = channelFilter.toSet();
+ QPair<QString, QSet<ChannelClassSpec> > observerUniqueId(
+ account->dbusConnection().baseService(), normalizedChannelFilter);
+ observer = SharedPtr<Observer>(observers.value(observerUniqueId));
+ if (!observer) {
+ SharedPtr<FakeAccountFactory> fakeAccountFactory = FakeAccountFactory::create(
+ account->dbusConnection());
+
+ cr = ClientRegistrar::create(
+ AccountFactoryPtr::qObjectCast(fakeAccountFactory),
+ account->connectionFactory(),
+ account->channelFactory(),
+ account->contactFactory());
+
+ QString observerName = QString(QLatin1String("TpQt4SO_%1_%2"))
+ .arg(account->dbusConnection().baseService()
+ .replace(QLatin1String(":"), QLatin1String("_"))
+ .replace(QLatin1String("."), QLatin1String("_")))
+ .arg(numObservers++);
+ observer = SharedPtr<Observer>(new Observer(cr.data(), fakeAccountFactory,
+ normalizedChannelFilter.toList(), observerName));
+ if (!cr->registerClient(observer, observerName, false)) {
+ warning() << "Unable to register observer" << observerName;
+ observer.reset();
+ cr.reset();
+ return;
+ }
+
+ debug() << "Observer" << observerName << "registered";
+ observers.insert(observerUniqueId, observer.data());
+ } else {
+ debug() << "Observer" << observer->observerName() <<
+ "already registered and matches filter, using it";
+ cr = ClientRegistrarPtr(observer->clientRegistrar());
+ }
+
+ observer->registerExtraChannelFeatures(extraChannelFeatures);
+ observer->registerAccount(account);
+
+ if (contactIdentifier.isEmpty() || !requiresNormalization) {
+ normalizedContactIdentifier = contactIdentifier;
+ } else {
+ parent->connect(account.data(),
+ SIGNAL(connectionChanged(Tp::ConnectionPtr)),
+ SLOT(onAccountConnectionChanged(Tp::ConnectionPtr)));
+ }
+
+ parent->connect(observer.data(),
+ SIGNAL(newChannels(Tp::AccountPtr,QList<Tp::ChannelPtr>)),
+ SLOT(onNewChannels(Tp::AccountPtr,QList<Tp::ChannelPtr>)));
+ parent->connect(observer.data(),
+ SIGNAL(channelInvalidated(Tp::AccountPtr,Tp::ChannelPtr,QString,QString)),
+ SLOT(onChannelInvalidated(Tp::AccountPtr,Tp::ChannelPtr,QString,QString)));
+}
+
+bool SimpleObserver::Private::filterChannel(const AccountPtr &channelAccount,
+ const ChannelPtr &channel)
+{
+ if (channelAccount != account) {
+ return false;
+ }
+
+ if (contactIdentifier.isEmpty()) {
+ return true;
+ }
+
+ QString targetId = channel->immutableProperties().value(
+ TP_QT_IFACE_CHANNEL + QLatin1String(".TargetID")).toString();
+ if (targetId != normalizedContactIdentifier) {
+ // we didn't filter per contact, let's filter here
+ return false;
+ }
+ return true;
+}
+
+void SimpleObserver::Private::insertChannels(const AccountPtr &channelsAccount,
+ const QList<ChannelPtr> &newChannels)
+{
+ QSet<ChannelPtr> match;
+ foreach (const ChannelPtr &channel, newChannels) {
+ if (!channels.contains(channel) && filterChannel(channelsAccount, channel)) {
+ match.insert(channel);
+ }
+ }
+
+ if (match.isEmpty()) {
+ return;
+ }
+
+ channels.unite(match);
+ emit parent->newChannels(match.toList());
+}
+
+void SimpleObserver::Private::removeChannel(const AccountPtr &channelAccount,
+ const ChannelPtr &channel,
+ const QString &errorName, const QString &errorMessage)
+{
+ if (!channels.contains(channel) || !filterChannel(channelAccount, channel)) {
+ return;
+ }
+
+ channels.remove(channel);
+ emit parent->channelInvalidated(channel, errorName, errorMessage);
+}
+
+void SimpleObserver::Private::processChannelsQueue()
+{
+ if (channelsQueue.isEmpty()) {
+ return;
+ }
+
+ while (!channelsQueue.isEmpty()) {
+ (this->*(channelsQueue.dequeue()))();
+ }
+}
+
+void SimpleObserver::Private::processNewChannelsQueue()
+{
+ NewChannelsInfo info = newChannelsQueue.dequeue();
+ insertChannels(info.channelsAccount, info.channels);
+}
+
+void SimpleObserver::Private::processChannelsInvalidationQueue()
+{
+ ChannelInvalidationInfo info = channelsInvalidationQueue.dequeue();
+ removeChannel(info.channelAccount, info.channel, info.errorName, info.errorMessage);
+}
+
+SimpleObserver::Private::Observer::Observer(const QWeakPointer<ClientRegistrar> &cr,
+ const SharedPtr<FakeAccountFactory> &fakeAccountFactory,
+ const ChannelClassSpecList &channelFilter,
+ const QString &observerName)
+ : AbstractClient(),
+ QObject(),
+ AbstractClientObserver(channelFilter, true),
+ mCr(cr),
+ mFakeAccountFactory(fakeAccountFactory),
+ mObserverName(observerName)
+{
+}
+
+SimpleObserver::Private::Observer::~Observer()
+{
+ // no need to delete channel wrappers here as they have 'this' as parent
+
+ // no need to delete context infos here as this observer will never be deleted before all
+ // PendingComposites finish (hold reference to this), which will properly delete them
+
+ // no need to unregister this observer here as the client registrar destructor will
+ // unregister it
+}
+
+void SimpleObserver::Private::Observer::observeChannels(
+ const MethodInvocationContextPtr<> &context,
+ const AccountPtr &account,
+ const ConnectionPtr &connection,
+ const QList<ChannelPtr> &channels,
+ const ChannelDispatchOperationPtr &dispatchOperation,
+ const QList<ChannelRequestPtr> &requestsSatisfied,
+ const ObserverInfo &observerInfo)
+{
+ if (!mAccounts.contains(account)) {
+ context->setFinished();
+ return;
+ }
+
+ QList<PendingOperation*> readyOps;
+ QList<ChannelPtr> newChannels;
+
+ foreach (const ChannelPtr &channel, channels) {
+ if (mIncompleteChannels.contains(channel) ||
+ mChannels.contains(channel)) {
+ // we are already observing this channel
+ continue;
+ }
+
+ // this shouldn't happen, but in any case
+ if (!channel->isValid()) {
+ warning() << "Channel received to observe is invalid. "
+ "Ignoring channel";
+ continue;
+ }
+
+ SimpleObserver::Private::ChannelWrapper *wrapper =
+ new SimpleObserver::Private::ChannelWrapper(account, channel,
+ featuresFor(ChannelClassSpec(channel->immutableProperties())), this);
+ mIncompleteChannels.insert(channel, wrapper);
+ connect(wrapper,
+ SIGNAL(channelInvalidated(Tp::AccountPtr,Tp::ChannelPtr,QString,QString)),
+ SLOT(onChannelInvalidated(Tp::AccountPtr,Tp::ChannelPtr,QString,QString)));
+
+ newChannels.append(channel);
+ readyOps.append(wrapper->becomeReady());
+ }
+
+ if (readyOps.isEmpty()) {
+ context->setFinished();
+ return;
+ }
+
+ PendingComposite *pc = new PendingComposite(readyOps,
+ false /* failOnFirstError */, SharedPtr<Observer>(this));
+ mObserveChannelsInfo.insert(pc, new ContextInfo(context, account, newChannels));
+ connect(pc,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onChannelsReady(Tp::PendingOperation*)));
+}
+
+void SimpleObserver::Private::Observer::onChannelInvalidated(const AccountPtr &channelAccount,
+ const ChannelPtr &channel, const QString &errorName, const QString &errorMessage)
+{
+ if (mIncompleteChannels.contains(channel)) {
+ // we are still handling the channel, wait for onChannelsReady that will properly remove
+ // it from mChannels
+ return;
+ }
+ emit channelInvalidated(channelAccount, channel, errorName, errorMessage);
+ Q_ASSERT(mChannels.contains(channel));
+ delete mChannels.take(channel);
+}
+
+void SimpleObserver::Private::Observer::onChannelsReady(PendingOperation *op)
+{
+ ContextInfo *info = mObserveChannelsInfo.value(op);
+
+ foreach (const ChannelPtr &channel, info->channels) {
+ Q_ASSERT(mIncompleteChannels.contains(channel));
+ ChannelWrapper *wrapper = mIncompleteChannels.take(channel);
+ mChannels.insert(channel, wrapper);
+ }
+ emit newChannels(info->account, info->channels);
+
+ foreach (const ChannelPtr &channel, info->channels) {
+ ChannelWrapper *wrapper = mChannels.value(channel);
+ if (!channel->isValid()) {
+ mChannels.remove(channel);
+ emit channelInvalidated(info->account, channel, channel->invalidationReason(),
+ channel->invalidationMessage());
+ delete wrapper;
+ }
+ }
+
+ mObserveChannelsInfo.remove(op);
+ info->context->setFinished();
+ delete info;
+}
+
+Features SimpleObserver::Private::Observer::featuresFor(
+ const ChannelClassSpec &channelClass) const
+{
+ Features features;
+
+ foreach (const ChannelClassFeatures &spec, mExtraChannelFeatures) {
+ if (spec.first.isSubsetOf(channelClass)) {
+ features.unite(spec.second);
+ }
+ }
+
+ return features;
+}
+
+SimpleObserver::Private::ChannelWrapper::ChannelWrapper(const AccountPtr &channelAccount,
+ const ChannelPtr &channel, const Features &extraChannelFeatures, QObject *parent)
+ : QObject(parent),
+ mChannelAccount(channelAccount),
+ mChannel(channel),
+ mExtraChannelFeatures(extraChannelFeatures)
+{
+ connect(channel.data(),
+ SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
+ SLOT(onChannelInvalidated(Tp::DBusProxy*,QString,QString)));
+}
+
+PendingOperation *SimpleObserver::Private::ChannelWrapper::becomeReady()
+{
+ PendingOperation *op;
+
+ if (!mChannel->isReady(mExtraChannelFeatures)) {
+ // The channel factory passed to the Account used by SimpleObserver does
+ // not contain the extra features, request them
+ op = mChannel->becomeReady(mExtraChannelFeatures);
+ } else {
+ op = new PendingSuccess(mChannel);
+ }
+
+ return op;
+}
+
+void SimpleObserver::Private::ChannelWrapper::onChannelInvalidated(DBusProxy *proxy,
+ const QString &errorName, const QString &errorMessage)
+{
+ Q_ASSERT(proxy == mChannel.data());
+ emit channelInvalidated(mChannelAccount, mChannel, errorName, errorMessage);
+}
+
+/**
+ * \class SimpleObserver
+ * \ingroup utils
+ * \headerfile TelepathyQt/simple-observer.h <TelepathyQt/SimpleObserver>
+ *
+ * \brief The SimpleObserver class provides an easy way to track channels
+ * in an account and can be optionally filtered by a contact.
+ */
+
+/**
+ * Create a new SimpleObserver object.
+ *
+ * Events will be signalled for all channels in \a account that match
+ * \a channelFilter for all contacts.
+ *
+ * \param channelFilter A specification of the channels in which this observer
+ * is interested.
+ * \param account The account used to listen to events.
+ * \param extraChannelFeatures Extra channel features to be enabled. All channels emitted in
+ * newChannels() will have the extra features that match their
+ * immutable properties enabled.
+ * \return An SimpleObserverPtr object pointing to the newly created SimpleObserver object.
+ */
+SimpleObserverPtr SimpleObserver::create(
+ const AccountPtr &account,
+ const ChannelClassSpecList &channelFilter,
+ const QList<ChannelClassFeatures> &extraChannelFeatures)
+{
+ return create(account, channelFilter, extraChannelFeatures);
+}
+
+/**
+ * Create a new SimpleObserver object.
+ *
+ * Events will be signalled for all channels in \a account established with
+ * \a contact, if not null, and that match \a channelFilter.
+ *
+ * \param channelFilter A specification of the channels in which this observer
+ * is interested.
+ * \param account The account used to listen to events.
+ * \param contact The contact used to filter events.
+ * \param extraChannelFeatures Extra channel features to be enabled. All channels emitted in
+ * newChannels() will have the extra features that match their
+ * immutable properties enabled.
+ * \return An SimpleObserverPtr object pointing to the newly created SimpleObserver object.
+ */
+SimpleObserverPtr SimpleObserver::create(
+ const AccountPtr &account,
+ const ChannelClassSpecList &channelFilter,
+ const ContactPtr &contact,
+ const QList<ChannelClassFeatures> &extraChannelFeatures)
+{
+ if (contact) {
+ return create(account, channelFilter, contact->id(), false, extraChannelFeatures);
+ }
+ return create(account, channelFilter, QString(), false, extraChannelFeatures);
+}
+
+/**
+ * Create a new SimpleObserver object.
+ *
+ * Events will be signalled for all channels in \a account established
+ * with a contact identified by \a contactIdentifier, if non-empty, and that match
+ * \a channelFilter.
+ *
+ * \param channelFilter A specification of the channels in which this observer
+ * is interested.
+ * \param account The account used to listen to events.
+ * \param contactIdentifier The identifier of the contact used to filter events.
+ * \param extraChannelFeatures Extra channel features to be enabled. All channels emitted in
+ * newChannels() will have the extra features that match their
+ * immutable properties enabled.
+ * \return An SimpleObserverPtr object pointing to the newly created SimpleObserver object.
+ */
+SimpleObserverPtr SimpleObserver::create(
+ const AccountPtr &account,
+ const ChannelClassSpecList &channelFilter,
+ const QString &contactIdentifier,
+ const QList<ChannelClassFeatures> &extraChannelFeatures)
+{
+ return create(account, channelFilter, contactIdentifier, true, extraChannelFeatures);
+}
+
+SimpleObserverPtr SimpleObserver::create(
+ const AccountPtr &account,
+ const ChannelClassSpecList &channelFilter,
+ const QString &contactIdentifier,
+ bool requiresNormalization,
+ const QList<ChannelClassFeatures> &extraChannelFeatures)
+{
+ return SimpleObserverPtr(new SimpleObserver(account, channelFilter, contactIdentifier,
+ requiresNormalization, extraChannelFeatures));
+}
+
+/**
+ * Construct a new SimpleObserver object.
+ *
+ * \param cr The ClientRegistrar used to register this observer.
+ * \param channelFilter The channel filter used by this observer.
+ * \param account The account used to listen to events.
+ * \param extraChannelFeatures Extra channel features to be enabled. All channels emitted in
+ * newChannels() will have the extra features that match their
+ * immutable properties enabled.
+ * \return An SimpleObserverPtr object pointing to the newly created SimpleObserver object.
+ */
+SimpleObserver::SimpleObserver(const AccountPtr &account,
+ const ChannelClassSpecList &channelFilter,
+ const QString &contactIdentifier, bool requiresNormalization,
+ const QList<ChannelClassFeatures> &extraChannelFeatures)
+ : mPriv(new Private(this, account, channelFilter, contactIdentifier,
+ requiresNormalization, extraChannelFeatures))
+{
+ if (mPriv->observer) {
+ // populate our channels list with current observer channels
+ QHash<AccountPtr, QList<ChannelPtr> > channels;
+ foreach (const Private::ChannelWrapper *wrapper, mPriv->observer->channels()) {
+ channels[wrapper->channelAccount()].append(wrapper->channel());
+ }
+
+ QHash<AccountPtr, QList<ChannelPtr> >::const_iterator it = channels.constBegin();
+ QHash<AccountPtr, QList<ChannelPtr> >::const_iterator end = channels.constEnd();
+ for (; it != end; ++it) {
+ onNewChannels(it.key(), it.value());
+ }
+
+ if (requiresNormalization) {
+ debug() << "Contact id requires normalization. "
+ "Queueing events until it is normalized";
+ onAccountConnectionChanged(account->connection());
+ }
+ }
+}
+
+/**
+ * Class destructor.
+ */
+SimpleObserver::~SimpleObserver()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the account used to listen to events.
+ *
+ * \return A pointer to the Account object.
+ */
+AccountPtr SimpleObserver::account() const
+{
+ return mPriv->account;
+}
+
+/**
+ * Return a specification of the channels that this observer is interested.
+ *
+ * \return The specification of the channels as a list of ChannelClassSpec objects.
+ */
+ChannelClassSpecList SimpleObserver::channelFilter() const
+{
+ return mPriv->channelFilter;
+}
+
+/**
+ * Return the extra channel features to be enabled based on the channels immutable properties.
+ *
+ * \return The features as a list of ChannelClassFeatures objects.
+ */
+QList<ChannelClassFeatures> SimpleObserver::extraChannelFeatures() const
+{
+ return mPriv->extraChannelFeatures;
+}
+
+/**
+ * Return the channels being observed.
+ *
+ * \return A list of pointers to Channel objects.
+ */
+QList<ChannelPtr> SimpleObserver::channels() const
+{
+ return mPriv->channels.toList();
+}
+
+/**
+ * Return the identifier of the contact used to filter events, or an empty string if none was
+ * provided at construction.
+ *
+ * \return The identifier of the contact.
+ */
+QString SimpleObserver::contactIdentifier() const
+{
+ return mPriv->contactIdentifier;
+}
+
+void SimpleObserver::onAccountConnectionChanged(const Tp::ConnectionPtr &connection)
+{
+ if (connection) {
+ connect(connection->becomeReady(Connection::FeatureConnected),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onAccountConnectionConnected()));
+ }
+}
+
+void SimpleObserver::onAccountConnectionConnected()
+{
+ ConnectionPtr conn = mPriv->account->connection();
+
+ // check here again as the account connection may have changed and the op failed
+ if (!conn || conn->status() != ConnectionStatusConnected) {
+ return;
+ }
+
+ debug() << "Normalizing contact id" << mPriv->contactIdentifier;
+ ContactManagerPtr contactManager = conn->contactManager();
+ connect(contactManager->contactsForIdentifiers(QStringList() << mPriv->contactIdentifier),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onContactConstructed(Tp::PendingOperation*)));
+}
+
+void SimpleObserver::onContactConstructed(Tp::PendingOperation *op)
+{
+ if (op->isError()) {
+ // what should we do here? retry? wait for a new connection?
+ warning() << "Normalizing contact id failed with" <<
+ op->errorName() << " : " << op->errorMessage();
+ return;
+ }
+
+ PendingContacts *pc = qobject_cast<PendingContacts*>(op);
+ Q_ASSERT((pc->contacts().size() + pc->invalidIdentifiers().size()) == 1);
+ if (!pc->invalidIdentifiers().isEmpty()) {
+ warning() << "Normalizing contact id failed with invalid id" <<
+ mPriv->contactIdentifier;
+ return;
+ }
+
+ ContactPtr contact = pc->contacts().first();
+ debug() << "Contact id" << mPriv->contactIdentifier <<
+ "normalized to" << contact->id();
+ mPriv->normalizedContactIdentifier = contact->id();
+ mPriv->processChannelsQueue();
+
+ // disconnect all account signals we are handling
+ disconnect(mPriv->account.data(), 0, this, 0);
+}
+
+void SimpleObserver::onNewChannels(const AccountPtr &channelsAccount,
+ const QList<ChannelPtr> &channels)
+{
+ if (!mPriv->contactIdentifier.isEmpty() && mPriv->normalizedContactIdentifier.isEmpty()) {
+ mPriv->newChannelsQueue.append(Private::NewChannelsInfo(channelsAccount, channels));
+ mPriv->channelsQueue.append(&SimpleObserver::Private::processNewChannelsQueue);
+ return;
+ }
+
+ mPriv->insertChannels(channelsAccount, channels);
+}
+
+void SimpleObserver::onChannelInvalidated(const AccountPtr &channelAccount,
+ const ChannelPtr &channel, const QString &errorName, const QString &errorMessage)
+{
+ if (!mPriv->contactIdentifier.isEmpty() && mPriv->normalizedContactIdentifier.isEmpty()) {
+ mPriv->channelsInvalidationQueue.append(Private::ChannelInvalidationInfo(channelAccount,
+ channel, errorName, errorMessage));
+ mPriv->channelsQueue.append(&SimpleObserver::Private::processChannelsInvalidationQueue);
+ return;
+ }
+
+ mPriv->removeChannel(channelAccount, channel, errorName, errorMessage);
+}
+
+/**
+ * \fn void SimpleObserver::newChannels(const QList<Tp::ChannelPtr> &channels)
+ *
+ * Emitted whenever new channels that match this observer's criteria are created.
+ *
+ * \param channels The new channels.
+ */
+
+/**
+ * \fn void SimpleObserver::channelInvalidated(const Tp::ChannelPtr &channel,
+ * const QString &errorName, const QString &errorMessage)
+ *
+ * Emitted whenever a channel that is being observed is invalidated.
+ *
+ * \param channel The channel that was invalidated.
+ * \param errorName A D-Bus error name (a string in a subset
+ * of ASCII, prefixed with a reversed domain name).
+ * \param errorMessage A debugging message associated with the error.
+ */
+
+} // Tp
diff --git a/TelepathyQt/simple-observer.h b/TelepathyQt/simple-observer.h
new file mode 100644
index 00000000..e460d747
--- /dev/null
+++ b/TelepathyQt/simple-observer.h
@@ -0,0 +1,105 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_simple_observer_h_HEADER_GUARD_
+#define _TelepathyQt_simple_observer_h_HEADER_GUARD_
+
+#include <TelepathyQt/AbstractClientObserver>
+#include <TelepathyQt/ChannelClassFeatures>
+#include <TelepathyQt/Types>
+
+#include <QObject>
+
+namespace Tp
+{
+
+class PendingOperation;
+
+class TP_QT_EXPORT SimpleObserver : public QObject, public RefCounted
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(SimpleObserver)
+
+public:
+ static SimpleObserverPtr create(const AccountPtr &account,
+ const ChannelClassSpecList &channelFilter,
+ const QList<ChannelClassFeatures> &extraChannelFeatures =
+ QList<ChannelClassFeatures>());
+ static SimpleObserverPtr create(const AccountPtr &account,
+ const ChannelClassSpecList &channelFilter,
+ const ContactPtr &contact,
+ const QList<ChannelClassFeatures> &extraChannelFeatures =
+ QList<ChannelClassFeatures>());
+ static SimpleObserverPtr create(const AccountPtr &account,
+ const ChannelClassSpecList &channelFilter,
+ const QString &contactIdentifier,
+ const QList<ChannelClassFeatures> &extraChannelFeatures =
+ QList<ChannelClassFeatures>());
+
+ virtual ~SimpleObserver();
+
+ AccountPtr account() const;
+ ChannelClassSpecList channelFilter() const;
+ QString contactIdentifier() const;
+ QList<ChannelClassFeatures> extraChannelFeatures() const;
+
+ QList<ChannelPtr> channels() const;
+
+Q_SIGNALS:
+ void newChannels(const QList<Tp::ChannelPtr> &channels);
+ void channelInvalidated(const Tp::ChannelPtr &channel, const QString &errorName,
+ const QString &errorMessage);
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onAccountConnectionChanged(const Tp::ConnectionPtr &connection);
+ TP_QT_NO_EXPORT void onAccountConnectionConnected();
+ TP_QT_NO_EXPORT void onContactConstructed(Tp::PendingOperation *op);
+
+ TP_QT_NO_EXPORT void onNewChannels(const Tp::AccountPtr &channelsAccount,
+ const QList<Tp::ChannelPtr> &channels);
+ TP_QT_NO_EXPORT void onChannelInvalidated(const Tp::AccountPtr &channelAccount,
+ const Tp::ChannelPtr &channel, const QString &errorName, const QString &errorMessage);
+
+private:
+ friend class SimpleCallObserver;
+ friend class SimpleTextObserver;
+
+ TP_QT_NO_EXPORT static SimpleObserverPtr create(const AccountPtr &account,
+ const ChannelClassSpecList &channelFilter,
+ const QString &contactIdentifier,
+ bool requiresNormalization,
+ const QList<ChannelClassFeatures> &extraChannelFeatures);
+
+ TP_QT_NO_EXPORT SimpleObserver(const AccountPtr &account,
+ const ChannelClassSpecList &channelFilter,
+ const QString &contactIdentifier,
+ bool requiresNormalization,
+ const QList<ChannelClassFeatures> &extraChannelFeatures);
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/simple-pending-operations.h b/TelepathyQt/simple-pending-operations.h
new file mode 100644
index 00000000..dce87852
--- /dev/null
+++ b/TelepathyQt/simple-pending-operations.h
@@ -0,0 +1,110 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_pending_operations_h_HEADER_GUARD_
+#define _TelepathyQt_pending_operations_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <QObject>
+
+#include <TelepathyQt/PendingOperation>
+
+namespace Tp
+{
+
+class TP_QT_EXPORT PendingSuccess : public PendingOperation
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(PendingSuccess)
+
+public:
+ PendingSuccess(const SharedPtr<RefCounted> &object)
+ : PendingOperation(object)
+ {
+ setFinished();
+ }
+};
+
+class TP_QT_EXPORT PendingFailure : public PendingOperation
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(PendingFailure)
+
+public:
+ PendingFailure(const QString &name, const QString &message,
+ const SharedPtr<RefCounted> &object)
+ : PendingOperation(object)
+ {
+ setFinishedWithError(name, message);
+ }
+
+ PendingFailure(const QDBusError &error,
+ const SharedPtr<RefCounted> &object)
+ : PendingOperation(object)
+ {
+ setFinishedWithError(error);
+ }
+};
+
+class TP_QT_EXPORT PendingVoid : public PendingOperation
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(PendingVoid)
+
+public:
+ PendingVoid(QDBusPendingCall call, const SharedPtr<RefCounted> &object);
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void watcherFinished(QDBusPendingCallWatcher*);
+
+private:
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+class TP_QT_EXPORT PendingComposite : public PendingOperation
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(PendingComposite)
+
+public:
+ PendingComposite(const QList<PendingOperation*> &operations, const SharedPtr<RefCounted> &object);
+ PendingComposite(const QList<PendingOperation*> &operations, bool failOnFirstError,
+ const SharedPtr<RefCounted> &object);
+ ~PendingComposite();
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onOperationFinished(Tp::PendingOperation *);
+
+private:
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/simple-stream-tube-handler.cpp b/TelepathyQt/simple-stream-tube-handler.cpp
new file mode 100644
index 00000000..7df9e4ea
--- /dev/null
+++ b/TelepathyQt/simple-stream-tube-handler.cpp
@@ -0,0 +1,254 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 "TelepathyQt/simple-stream-tube-handler.h"
+
+#include "TelepathyQt/_gen/simple-stream-tube-handler.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/Account>
+#include <TelepathyQt/ChannelClassSpec>
+#include <TelepathyQt/PendingComposite>
+#include <TelepathyQt/PendingReady>
+#include <TelepathyQt/StreamTubeChannel>
+
+namespace Tp
+{
+
+namespace
+{
+ ChannelClassSpecList buildFilter(const QStringList &p2pServices,
+ const QStringList &roomServices, bool requested)
+ {
+ ChannelClassSpecList filter;
+
+ // Convert to QSet to weed out duplicates
+ foreach (const QString &service, p2pServices.toSet())
+ {
+ filter.append(requested ?
+ ChannelClassSpec::outgoingStreamTube(service) :
+ ChannelClassSpec::incomingStreamTube(service));
+ }
+
+ // Convert to QSet to weed out duplicates
+ foreach (const QString &service, roomServices.toSet())
+ {
+ filter.append(requested ?
+ ChannelClassSpec::outgoingRoomStreamTube(service) :
+ ChannelClassSpec::incomingRoomStreamTube(service));
+ }
+
+ return filter;
+ }
+}
+
+SharedPtr<SimpleStreamTubeHandler> SimpleStreamTubeHandler::create(
+ const QStringList &p2pServices,
+ const QStringList &roomServices,
+ bool requested,
+ bool monitorConnections,
+ bool bypassApproval)
+{
+ return SharedPtr<SimpleStreamTubeHandler>(
+ new SimpleStreamTubeHandler(
+ p2pServices,
+ roomServices,
+ requested,
+ monitorConnections,
+ bypassApproval));
+}
+
+SimpleStreamTubeHandler::SimpleStreamTubeHandler(
+ const QStringList &p2pServices,
+ const QStringList &roomServices,
+ bool requested,
+ bool monitorConnections,
+ bool bypassApproval)
+ : AbstractClient(),
+ AbstractClientHandler(buildFilter(p2pServices, roomServices, requested)),
+ mMonitorConnections(monitorConnections),
+ mBypassApproval(bypassApproval)
+{
+}
+
+SimpleStreamTubeHandler::~SimpleStreamTubeHandler()
+{
+ if (!mTubes.empty()) {
+ debug() << "~SSTubeHandler(): Closing" << mTubes.size() << "leftover tubes";
+
+ foreach (const StreamTubeChannelPtr &tube, mTubes.keys()) {
+ tube->requestClose();
+ }
+ }
+}
+
+void SimpleStreamTubeHandler::handleChannels(
+ const MethodInvocationContextPtr<> &context,
+ const AccountPtr &account,
+ const ConnectionPtr &connection,
+ const QList<ChannelPtr> &channels,
+ const QList<ChannelRequestPtr> &requestsSatisfied,
+ const QDateTime &userActionTime,
+ const HandlerInfo &handlerInfo)
+{
+ debug() << "SimpleStreamTubeHandler::handleChannels() invoked for " <<
+ channels.size() << "channels on account" << account->objectPath();
+
+ SharedPtr<InvocationData> invocation(new InvocationData());
+ QList<PendingOperation *> readyOps;
+
+ foreach (const ChannelPtr &chan, channels) {
+ StreamTubeChannelPtr tube = StreamTubeChannelPtr::qObjectCast(chan);
+
+ if (!tube) {
+ // TODO: if Channel ever starts utilizing its immutable props for the immutable
+ // accessors, use Channel::channelType() here
+ const QString channelType =
+ chan->immutableProperties()[QLatin1String(
+ TELEPATHY_INTERFACE_CHANNEL ".ChannelType")].toString();
+
+ if (channelType != TP_QT_IFACE_CHANNEL_TYPE_STREAM_TUBE) {
+ debug() << "We got a non-StreamTube channel" << chan->objectPath() <<
+ "of type" << channelType << ", ignoring";
+ } else {
+ warning() << "The channel factory used for a simple StreamTube handler must" <<
+ "construct StreamTubeChannel subclasses for stream tubes";
+ }
+ continue;
+ }
+
+ Features features = StreamTubeChannel::FeatureStreamTube;
+ if (mMonitorConnections) {
+ features.insert(StreamTubeChannel::FeatureConnectionMonitoring);
+ }
+ readyOps.append(tube->becomeReady(features));
+
+ invocation->tubes.append(tube);
+ }
+
+ invocation->ctx = context;
+ invocation->acc = account;
+ invocation->time = userActionTime;
+
+ if (!requestsSatisfied.isEmpty()) {
+ invocation->hints = requestsSatisfied.first()->hints();
+ }
+
+ mInvocations.append(invocation);
+
+ if (invocation->tubes.isEmpty()) {
+ warning() << "SSTH::HandleChannels got no suitable channels, admitting we're Confused";
+ invocation->readyOp = 0;
+ invocation->error = TP_QT_ERROR_CONFUSED;
+ invocation->message = QLatin1String("Got no suitable channels");
+ onReadyOpFinished(0);
+ } else {
+ invocation->readyOp = new PendingComposite(readyOps, SharedPtr<SimpleStreamTubeHandler>(this));
+ connect(invocation->readyOp,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onReadyOpFinished(Tp::PendingOperation*)));
+ }
+}
+
+void SimpleStreamTubeHandler::onReadyOpFinished(Tp::PendingOperation *op)
+{
+ Q_ASSERT(!mInvocations.isEmpty());
+ Q_ASSERT(!op || op->isFinished());
+
+ for (QLinkedList<SharedPtr<InvocationData> >::iterator i = mInvocations.begin();
+ op != 0 && i != mInvocations.end(); ++i) {
+ if ((*i)->readyOp != op) {
+ continue;
+ }
+
+ (*i)->readyOp = 0;
+
+ if (op->isError()) {
+ warning() << "Preparing proxies for SSTubeHandler failed with" << op->errorName()
+ << op->errorMessage();
+ (*i)->error = op->errorName();
+ (*i)->message = op->errorMessage();
+ }
+
+ break;
+ }
+
+ while (!mInvocations.isEmpty() && !mInvocations.first()->readyOp) {
+ SharedPtr<InvocationData> invocation = mInvocations.takeFirst();
+
+ if (!invocation->error.isEmpty()) {
+ // We guarantee that the proxies were ready - so we can't invoke the client if they
+ // weren't made ready successfully. Fix the introspection code if this happens :)
+ invocation->ctx->setFinishedWithError(invocation->error, invocation->message);
+ continue;
+ }
+
+ debug() << "Emitting SSTubeHandler::invokedForTube for" << invocation->tubes.size()
+ << "tubes";
+
+ foreach (const StreamTubeChannelPtr &tube, invocation->tubes) {
+ if (!tube->isValid()) {
+ debug() << "Skipping already invalidated tube" << tube->objectPath();
+ continue;
+ }
+
+ if (!mTubes.contains(tube)) {
+ connect(tube.data(),
+ SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
+ SLOT(onTubeInvalidated(Tp::DBusProxy*,QString,QString)));
+
+ mTubes.insert(tube, invocation->acc);
+ }
+
+ emit invokedForTube(
+ invocation->acc,
+ tube,
+ invocation->time,
+ invocation->hints);
+ }
+
+ invocation->ctx->setFinished();
+ }
+}
+
+void SimpleStreamTubeHandler::onTubeInvalidated(DBusProxy *proxy,
+ const QString &errorName, const QString &errorMessage)
+{
+ StreamTubeChannelPtr tube(qobject_cast<StreamTubeChannel *>(proxy));
+
+ Q_ASSERT(!tube.isNull());
+ Q_ASSERT(mTubes.contains(tube));
+
+ debug() << "Tube" << tube->objectPath() << "invalidated - " << errorName << ':' << errorMessage;
+
+ AccountPtr acc = mTubes.value(tube);
+ mTubes.remove(tube);
+
+ emit tubeInvalidated(
+ acc,
+ tube,
+ errorName,
+ errorMessage);
+}
+
+} // Tp
diff --git a/TelepathyQt/simple-stream-tube-handler.h b/TelepathyQt/simple-stream-tube-handler.h
new file mode 100644
index 00000000..d5b78dcb
--- /dev/null
+++ b/TelepathyQt/simple-stream-tube-handler.h
@@ -0,0 +1,120 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_simple_stream_tube_handler_h_HEADER_GUARD_
+#define _TelepathyQt_simple_stream_tube_handler_h_HEADER_GUARD_
+
+#include <TelepathyQt/AbstractClientHandler>
+#include <TelepathyQt/ChannelRequestHints>
+#include <TelepathyQt/RefCounted>
+#include <TelepathyQt/Types>
+
+#include <QDateTime>
+#include <QLinkedList>
+#include <QHash>
+#include <QQueue>
+#include <QSet>
+
+namespace Tp
+{
+
+class PendingOperation;
+
+class TP_QT_NO_EXPORT SimpleStreamTubeHandler : public QObject,
+ public AbstractClientHandler
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(SimpleStreamTubeHandler)
+
+public:
+ static SharedPtr<SimpleStreamTubeHandler> create(
+ const QStringList &p2pServices,
+ const QStringList &roomServices,
+ bool requested,
+ bool monitorConnections,
+ bool bypassApproval = false);
+ ~SimpleStreamTubeHandler();
+
+ bool monitorsConnections() const
+ {
+ return mMonitorConnections;
+ }
+
+ bool bypassApproval() const
+ {
+ return mBypassApproval;
+ }
+
+ void handleChannels(const MethodInvocationContextPtr<> &context,
+ const AccountPtr &account,
+ const ConnectionPtr &connection,
+ const QList<ChannelPtr> &channels,
+ const QList<ChannelRequestPtr> &requestsSatisfied,
+ const QDateTime &userActionTime,
+ const HandlerInfo &handlerInfo);
+
+Q_SIGNALS:
+ void invokedForTube(
+ const Tp::AccountPtr &account,
+ const Tp::StreamTubeChannelPtr &tube,
+ const QDateTime &userActionTime,
+ const Tp::ChannelRequestHints &requestHints);
+ void tubeInvalidated(
+ const Tp::AccountPtr &account,
+ const Tp::StreamTubeChannelPtr &tube,
+ const QString &errorName,
+ const QString &errorMessage);
+
+private Q_SLOTS:
+ void onReadyOpFinished(Tp::PendingOperation *);
+ void onTubeInvalidated(Tp::DBusProxy *, const QString &, const QString &);
+
+private:
+ SimpleStreamTubeHandler(
+ const QStringList &p2pServices,
+ const QStringList &roomServices,
+ bool requested,
+ bool monitorConnections,
+ bool bypassApproval);
+
+ bool mMonitorConnections;
+
+ struct InvocationData : RefCounted {
+ InvocationData() : readyOp(0) {}
+
+ PendingOperation *readyOp;
+ QString error, message;
+
+ MethodInvocationContextPtr<> ctx;
+ AccountPtr acc;
+ QList<StreamTubeChannelPtr> tubes;
+ QDateTime time;
+ ChannelRequestHints hints;
+ };
+ QLinkedList<SharedPtr<InvocationData> > mInvocations;
+ QHash<StreamTubeChannelPtr, AccountPtr> mTubes;
+ bool mBypassApproval;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/simple-text-observer-internal.h b/TelepathyQt/simple-text-observer-internal.h
new file mode 100644
index 00000000..91b5e1d9
--- /dev/null
+++ b/TelepathyQt/simple-text-observer-internal.h
@@ -0,0 +1,70 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/Account>
+#include <TelepathyQt/Message>
+#include <TelepathyQt/SimpleObserver>
+#include <TelepathyQt/TextChannel>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT SimpleTextObserver::Private
+{
+ Private(SimpleTextObserver *parent, const AccountPtr &account,
+ const QString &contactIdentifier, bool requiresNormalization);
+ ~Private();
+
+ class TextChannelWrapper;
+
+ SimpleTextObserver *parent;
+ AccountPtr account;
+ QString contactIdentifier;
+ SimpleObserverPtr observer;
+ QHash<ChannelPtr, TextChannelWrapper*> channels;
+};
+
+class TP_QT_NO_EXPORT SimpleTextObserver::Private::TextChannelWrapper :
+ public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(TextChannelWrapper)
+
+public:
+ TextChannelWrapper(const Tp::TextChannelPtr &channel);
+ ~TextChannelWrapper() { }
+
+Q_SIGNALS:
+ void channelMessageSent(const Tp::Message &message, Tp::MessageSendingFlags flags,
+ const QString &sentMessageToken, const Tp::TextChannelPtr &channel);
+ void channelMessageReceived(const Tp::ReceivedMessage &message, const Tp::TextChannelPtr &channel);
+
+private Q_SLOTS:
+ void onChannelMessageSent(const Tp::Message &message, Tp::MessageSendingFlags flags,
+ const QString &sentMessageToken);
+ void onChannelMessageReceived(const Tp::ReceivedMessage &message);
+
+private:
+ TextChannelPtr mChannel;
+};
+
+} // Tp
diff --git a/TelepathyQt/simple-text-observer.cpp b/TelepathyQt/simple-text-observer.cpp
new file mode 100644
index 00000000..2de0b6e1
--- /dev/null
+++ b/TelepathyQt/simple-text-observer.cpp
@@ -0,0 +1,298 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/SimpleTextObserver>
+#include "TelepathyQt/simple-text-observer-internal.h"
+
+#include "TelepathyQt/_gen/simple-text-observer.moc.hpp"
+#include "TelepathyQt/_gen/simple-text-observer-internal.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/ChannelClassSpec>
+#include <TelepathyQt/ChannelClassSpecList>
+#include <TelepathyQt/Connection>
+#include <TelepathyQt/Contact>
+#include <TelepathyQt/ContactManager>
+#include <TelepathyQt/Message>
+#include <TelepathyQt/PendingContacts>
+#include <TelepathyQt/PendingComposite>
+#include <TelepathyQt/PendingReady>
+#include <TelepathyQt/PendingSuccess>
+
+namespace Tp
+{
+
+SimpleTextObserver::Private::Private(SimpleTextObserver *parent,
+ const AccountPtr &account,
+ const QString &contactIdentifier, bool requiresNormalization)
+ : parent(parent),
+ account(account),
+ contactIdentifier(contactIdentifier)
+{
+ debug() << "Creating a new SimpleTextObserver";
+ ChannelClassSpec channelFilter = ChannelClassSpec::textChat();
+ observer = SimpleObserver::create(account, ChannelClassSpecList() << channelFilter,
+ contactIdentifier, requiresNormalization,
+ QList<ChannelClassFeatures>() << ChannelClassFeatures(channelFilter,
+ TextChannel::FeatureMessageQueue | TextChannel::FeatureMessageSentSignal));
+
+ parent->connect(observer.data(),
+ SIGNAL(newChannels(QList<Tp::ChannelPtr>)),
+ SLOT(onNewChannels(QList<Tp::ChannelPtr>)));
+ parent->connect(observer.data(),
+ SIGNAL(channelInvalidated(Tp::ChannelPtr,QString,QString)),
+ SLOT(onChannelInvalidated(Tp::ChannelPtr)));
+}
+
+SimpleTextObserver::Private::~Private()
+{
+ foreach (TextChannelWrapper *wrapper, channels) {
+ delete wrapper;
+ }
+}
+
+SimpleTextObserver::Private::TextChannelWrapper::TextChannelWrapper(const TextChannelPtr &channel)
+ : mChannel(channel)
+{
+ connect(mChannel.data(),
+ SIGNAL(messageSent(Tp::Message,Tp::MessageSendingFlags,QString)),
+ SLOT(onChannelMessageSent(Tp::Message,Tp::MessageSendingFlags,QString)));
+ connect(mChannel.data(),
+ SIGNAL(messageReceived(Tp::ReceivedMessage)),
+ SLOT(onChannelMessageReceived(Tp::ReceivedMessage)));
+}
+
+void SimpleTextObserver::Private::TextChannelWrapper::onChannelMessageSent(
+ const Tp::Message &message, Tp::MessageSendingFlags flags,
+ const QString &sentMessageToken)
+{
+ emit channelMessageSent(message, flags, sentMessageToken, mChannel);
+}
+
+void SimpleTextObserver::Private::TextChannelWrapper::onChannelMessageReceived(
+ const Tp::ReceivedMessage &message)
+{
+ emit channelMessageReceived(message, mChannel);
+}
+
+/**
+ * \class SimpleTextObserver
+ * \ingroup utils
+ * \headerfile TelepathyQt/simple-text-observer.h <TelepathyQt/SimpleTextObserver>
+ *
+ * \brief The SimpleTextObserver class provides an easy way to track sent/received text messages
+ * in an account and can be optionally filtered by a contact.
+ */
+
+/**
+ * Create a new SimpleTextObserver object.
+ *
+ * Events will be signalled for all messages sent/received by all contacts in \a account.
+ *
+ * \param account The account used to listen to events.
+ * \return An SimpleTextObserverPtr object pointing to the newly created SimpleTextObserver object.
+ */
+SimpleTextObserverPtr SimpleTextObserver::create(const AccountPtr &account)
+{
+ return create(account, QString(), false);
+}
+
+/**
+ * Create a new SimpleTextObserver object.
+ *
+ * If \a contact is not null, events will be signalled for all messages sent/received by \a
+ * contact, otherwise this method works the same as create(const Tp::AccountPtr &).
+ *
+ * \param account The account used to listen to events.
+ * \param contact The contact used to filter events.
+ * \return An SimpleTextObserverPtr object pointing to the newly created SimpleTextObserver object.
+ */
+SimpleTextObserverPtr SimpleTextObserver::create(const AccountPtr &account,
+ const ContactPtr &contact)
+{
+ if (contact) {
+ return create(account, contact->id(), false);
+ }
+ return create(account, QString(), false);
+}
+
+/**
+ * Create a new SimpleTextObserver object.
+ *
+ * If \a contactIdentifier is non-empty, events will be signalled for all messages sent/received
+ * by a contact identified by \a contactIdentifier, otherwise this method works the same as
+ * create(const Tp::AccountPtr &).
+ *
+ * \param account The account used to listen to events.
+ * \param contactIdentifier The identifier of the contact used to filter events.
+ * \return An SimpleTextObserverPtr object pointing to the newly created SimpleTextObserver object.
+ */
+SimpleTextObserverPtr SimpleTextObserver::create(const AccountPtr &account,
+ const QString &contactIdentifier)
+{
+ return create(account, contactIdentifier, true);
+}
+
+SimpleTextObserverPtr SimpleTextObserver::create(const AccountPtr &account,
+ const QString &contactIdentifier, bool requiresNormalization)
+{
+ return SimpleTextObserverPtr(
+ new SimpleTextObserver(account, contactIdentifier, requiresNormalization));
+}
+
+/**
+ * Construct a new SimpleTextObserver object.
+ *
+ * \param account The account used to listen to events.
+ * \param contactIdentifier The identifier of the contact used to filter events.
+ * \param requiresNormalization Whether \a contactIdentifier needs to be normalized.
+ * \return An SimpleTextObserverPtr object pointing to the newly created SimpleTextObserver object.
+ */
+SimpleTextObserver::SimpleTextObserver(const AccountPtr &account,
+ const QString &contactIdentifier, bool requiresNormalization)
+ : mPriv(new Private(this, account, contactIdentifier, requiresNormalization))
+{
+ if (!mPriv->observer->channels().isEmpty()) {
+ onNewChannels(mPriv->observer->channels());
+ }
+}
+
+/**
+ * Class destructor.
+ */
+SimpleTextObserver::~SimpleTextObserver()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the account used to listen to events.
+ *
+ * \return A pointer to the Account object.
+ */
+AccountPtr SimpleTextObserver::account() const
+{
+ return mPriv->account;
+}
+
+/**
+ * Return the identifier of the contact used to filter events, or an empty string if none was
+ * provided at construction.
+ *
+ * \return The identifier of the contact.
+ */
+QString SimpleTextObserver::contactIdentifier() const
+{
+ return mPriv->contactIdentifier;
+}
+
+/**
+ * Return the list of text chats currently being observed.
+ *
+ * \return A list of pointers to TextChannel objects.
+ */
+QList<TextChannelPtr> SimpleTextObserver::textChats() const
+{
+ QList<TextChannelPtr> ret;
+ foreach (const ChannelPtr &channel, mPriv->observer->channels()) {
+ TextChannelPtr textChannel = TextChannelPtr::qObjectCast(channel);
+ if (textChannel) {
+ ret << textChannel;
+ }
+ }
+ return ret;
+}
+
+void SimpleTextObserver::onNewChannels(const QList<ChannelPtr> &channels)
+{
+ foreach (const ChannelPtr &channel, channels) {
+ TextChannelPtr textChannel = TextChannelPtr::qObjectCast(channel);
+ if (!textChannel) {
+ if (channel->channelType() != TP_QT_IFACE_CHANNEL_TYPE_TEXT) {
+ warning() << "Channel received to observe is not of type Text, service confused. "
+ "Ignoring channel";
+ } else {
+ warning() << "Channel received to observe is not a subclass of TextChannel. "
+ "ChannelFactory set on this observer's account must construct TextChannel "
+ "subclasses for channels of type Text. Ignoring channel";
+ }
+ continue;
+ }
+
+ if (mPriv->channels.contains(channel)) {
+ // we are already observing this channel
+ continue;
+ }
+
+ Private::TextChannelWrapper *wrapper = new Private::TextChannelWrapper(textChannel);
+ mPriv->channels.insert(channel, wrapper);
+ connect(wrapper,
+ SIGNAL(channelMessageSent(Tp::Message,Tp::MessageSendingFlags,QString,Tp::TextChannelPtr)),
+ SIGNAL(messageSent(Tp::Message,Tp::MessageSendingFlags,QString,Tp::TextChannelPtr)));
+ connect(wrapper,
+ SIGNAL(channelMessageReceived(Tp::ReceivedMessage,Tp::TextChannelPtr)),
+ SIGNAL(messageReceived(Tp::ReceivedMessage,Tp::TextChannelPtr)));
+
+ foreach (const ReceivedMessage &message, textChannel->messageQueue()) {
+ emit messageReceived(message, textChannel);
+ }
+ }
+}
+
+void SimpleTextObserver::onChannelInvalidated(const ChannelPtr &channel)
+{
+ // it may happen that the channel received in onNewChannels is not a text channel somehow, thus
+ // the channel won't be added to mPriv->channels
+ if (mPriv->channels.contains(channel)) {
+ delete mPriv->channels.take(channel);
+ }
+}
+
+/**
+ * \fn void SimpleTextObserver::messageSent(const Tp::Message &message,
+ * Tp::MessageSendingFlags flags, const QString &sentMessageToken,
+ * const Tp::TextChannelPtr &channel);
+ *
+ * Emitted whenever a text message on account() is sent.
+ * If contactIdentifier() is non-empty, only messages sent to the contact identified by it will
+ * be signalled.
+ *
+ * \param message The message sent.
+ * \param flags The message flags,
+ * \param sentMessageToken The message token.
+ * \param channel The channel which received the message.
+ */
+
+/**
+ * \fn void SimpleTextObserver::messageReceived(const Tp::ReceivedMessage &message,
+ * const Tp::TextChannelPtr &channel);
+ *
+ * Emitted whenever a text message on account() is received.
+ * If contactIdentifier() is non-empty, only messages received by the contact identified by it will
+ * be signalled.
+ *
+ * \param message The message received.
+ * \param channel The channel which received the message.
+ */
+
+} // Tp
diff --git a/TelepathyQt/simple-text-observer.h b/TelepathyQt/simple-text-observer.h
new file mode 100644
index 00000000..87b16736
--- /dev/null
+++ b/TelepathyQt/simple-text-observer.h
@@ -0,0 +1,80 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_simple_text_observer_h_HEADER_GUARD_
+#define _TelepathyQt_simple_text_observer_h_HEADER_GUARD_
+
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/Types>
+
+#include <QObject>
+
+namespace Tp
+{
+
+class Message;
+class PendingOperation;
+class ReceivedMessage;
+
+class TP_QT_EXPORT SimpleTextObserver : public QObject, public RefCounted
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(SimpleTextObserver)
+
+public:
+ static SimpleTextObserverPtr create(const AccountPtr &account);
+ static SimpleTextObserverPtr create(const AccountPtr &account,
+ const ContactPtr &contact);
+ static SimpleTextObserverPtr create(const AccountPtr &account,
+ const QString &contactIdentifier);
+
+ virtual ~SimpleTextObserver();
+
+ AccountPtr account() const;
+ QString contactIdentifier() const;
+
+ QList<TextChannelPtr> textChats() const;
+
+Q_SIGNALS:
+ void messageSent(const Tp::Message &message, Tp::MessageSendingFlags flags,
+ const QString &sentMessageToken, const Tp::TextChannelPtr &channel);
+ void messageReceived(const Tp::ReceivedMessage &message, const Tp::TextChannelPtr &channel);
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onNewChannels(const QList<Tp::ChannelPtr> &channels);
+ TP_QT_NO_EXPORT void onChannelInvalidated(const Tp::ChannelPtr &channel);
+
+private:
+ TP_QT_NO_EXPORT static SimpleTextObserverPtr create(const AccountPtr &account,
+ const QString &contactIdentifier, bool requiresNormalization);
+
+ TP_QT_NO_EXPORT SimpleTextObserver(const AccountPtr &account,
+ const QString &contactIdentifier, bool requiresNormalization);
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/stable-interfaces.xml b/TelepathyQt/stable-interfaces.xml
new file mode 100644
index 00000000..fb9a0aca
--- /dev/null
+++ b/TelepathyQt/stable-interfaces.xml
@@ -0,0 +1,26 @@
+<tp:spec
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<tp:title>Telepathy D-Bus Interface Specification, TelepathyQt copy</tp:title>
+<tp:version>0.17.7</tp:version>
+
+<xi:include href="connection-manager.xml"/>
+<xi:include href="connection.xml"/>
+<xi:include href="channel.xml"/>
+<xi:include href="channel-dispatcher.xml"/>
+<xi:include href="channel-dispatch-operation.xml"/>
+<xi:include href="channel-request.xml"/>
+<xi:include href="media-session-handler.xml"/>
+<xi:include href="media-stream-handler.xml"/>
+<xi:include href="dbus.xml"/>
+<xi:include href="properties.xml"/>
+<xi:include href="account-manager.xml"/>
+<xi:include href="account.xml"/>
+<xi:include href="client.xml"/>
+<xi:include href="tls-certificate.xml"/>
+
+<xi:include href="../spec/generic-types.xml"/>
+<xi:include href="../spec/errors.xml"/>
+
+</tp:spec>
diff --git a/TelepathyQt/stream-tube-channel.cpp b/TelepathyQt/stream-tube-channel.cpp
new file mode 100644
index 00000000..8a8b83c5
--- /dev/null
+++ b/TelepathyQt/stream-tube-channel.cpp
@@ -0,0 +1,740 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010-2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/StreamTubeChannel>
+
+#include "TelepathyQt/_gen/stream-tube-channel.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/Connection>
+#include <TelepathyQt/ContactManager>
+#include <TelepathyQt/PendingContacts>
+#include <TelepathyQt/PendingVariantMap>
+
+#include <QHostAddress>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT StreamTubeChannel::Private
+{
+ Private(StreamTubeChannel *parent);
+
+ static void introspectStreamTube(Private *self);
+ static void introspectConnectionMonitoring(Private *self);
+
+ void extractStreamTubeProperties(const QVariantMap &props);
+
+ // Public object
+ StreamTubeChannel *parent;
+
+ ReadinessHelper *readinessHelper;
+
+ // Introspection
+ SupportedSocketMap socketTypes;
+ QString serviceName;
+
+ QSet<uint> connections;
+ QPair<QHostAddress, quint16> ipAddress;
+ QString unixAddress;
+ SocketAddressType addressType;
+ SocketAccessControl accessControl;
+ bool droppingConnections;
+};
+
+StreamTubeChannel::Private::Private(StreamTubeChannel *parent)
+ : parent(parent),
+ readinessHelper(parent->readinessHelper()),
+ addressType(SocketAddressTypeUnix),
+ accessControl(SocketAccessControlLocalhost),
+ droppingConnections(false)
+{
+ ReadinessHelper::Introspectables introspectables;
+
+ ReadinessHelper::Introspectable introspectableStreamTube(
+ QSet<uint>() << 0, // makesSenseForStatuses
+ Features() << TubeChannel::FeatureCore, // dependsOnFeatures (core)
+ QStringList(), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &StreamTubeChannel::Private::introspectStreamTube,
+ this);
+ introspectables[StreamTubeChannel::FeatureCore] = introspectableStreamTube;
+
+ ReadinessHelper::Introspectable introspectableConnectionMonitoring(
+ QSet<uint>() << 0, // makesSenseForStatuses
+ Features() << StreamTubeChannel::FeatureCore, // dependsOnFeatures (core)
+ QStringList(), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc)
+ &StreamTubeChannel::Private::introspectConnectionMonitoring,
+ this);
+ introspectables[StreamTubeChannel::FeatureConnectionMonitoring] =
+ introspectableConnectionMonitoring;
+
+ parent->connect(
+ parent,
+ SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
+ SLOT(dropConnections()));
+
+ readinessHelper->addIntrospectables(introspectables);
+}
+
+void StreamTubeChannel::Private::introspectStreamTube(
+ StreamTubeChannel::Private *self)
+{
+ StreamTubeChannel *parent = self->parent;
+
+ debug() << "Introspecting stream tube properties";
+ Client::ChannelTypeStreamTubeInterface *streamTubeInterface =
+ parent->interface<Client::ChannelTypeStreamTubeInterface>();
+
+ PendingVariantMap *pvm = streamTubeInterface->requestAllProperties();
+ parent->connect(pvm,
+ SIGNAL(finished(Tp::PendingOperation *)),
+ SLOT(gotStreamTubeProperties(Tp::PendingOperation *)));
+}
+
+void StreamTubeChannel::Private::introspectConnectionMonitoring(
+ StreamTubeChannel::Private *self)
+{
+ StreamTubeChannel *parent = self->parent;
+
+ Client::ChannelTypeStreamTubeInterface *streamTubeInterface =
+ parent->interface<Client::ChannelTypeStreamTubeInterface>();
+
+ parent->connect(streamTubeInterface,
+ SIGNAL(ConnectionClosed(uint,QString,QString)),
+ SLOT(onConnectionClosed(uint,QString,QString)));
+
+ if (parent->isRequested()) {
+ parent->connect(streamTubeInterface,
+ SIGNAL(NewRemoteConnection(uint,QDBusVariant,uint)),
+ SLOT(onNewRemoteConnection(uint,QDBusVariant,uint)));
+ } else {
+ parent->connect(streamTubeInterface,
+ SIGNAL(NewLocalConnection(uint)),
+ SLOT(onNewLocalConnection(uint)));
+ }
+
+ self->readinessHelper->setIntrospectCompleted(
+ StreamTubeChannel::FeatureConnectionMonitoring, true);
+}
+
+void StreamTubeChannel::Private::extractStreamTubeProperties(const QVariantMap &props)
+{
+ serviceName = qdbus_cast<QString>(props[QLatin1String("Service")]);
+ socketTypes = qdbus_cast<SupportedSocketMap>(props[QLatin1String("SupportedSocketTypes")]);
+}
+
+/**
+ * \class StreamTubeChannel
+ * \ingroup clientchannel
+ * \headerfile TelepathyQt/stream-tube-channel.h <TelepathyQt/StreamTubeChannel>
+ *
+ * \brief The StreamTubeChannel class represents a Telepathy channel of type StreamTube.
+ *
+ * It provides a transport for reliable and ordered data transfer, similar to SOCK_STREAM sockets.
+ *
+ * StreamTubeChannel is an intermediate base class; OutgoingStreamTubeChannel and
+ * IncomingStreamTubeChannel are the specialized classes used for locally and remotely initiated
+ * tubes respectively.
+ *
+ * For more details, please refer to \telepathy_spec.
+ *
+ * See \ref async_model, \ref shared_ptr
+ */
+
+/**
+ * Feature representing the core that needs to become ready to make the
+ * StreamTubeChannel object usable.
+ *
+ * Note that this feature must be enabled in order to use most
+ * StreamTubeChannel methods.
+ * See specific methods documentation for more details.
+ */
+const Feature StreamTubeChannel::FeatureCore =
+ Feature(QLatin1String(StreamTubeChannel::staticMetaObject.className()), 0);
+
+/**
+ * \deprecated Use StreamTubeChannel::FeatureCore instead.
+ */
+const Feature StreamTubeChannel::FeatureStreamTube = StreamTubeChannel::FeatureCore;
+
+/**
+ * Feature used in order to monitor connections to this stream tube.
+ *
+ * See connection monitoring specific methods' documentation for more details.
+ *
+ * \sa newConnection(), connectionClosed()
+ */
+const Feature StreamTubeChannel::FeatureConnectionMonitoring =
+ Feature(QLatin1String(StreamTubeChannel::staticMetaObject.className()), 1);
+
+/**
+ * Create a new StreamTubeChannel channel.
+ *
+ * \param connection Connection owning this channel, and specifying the
+ * service.
+ * \param objectPath The channel object path.
+ * \param immutableProperties The channel immutable properties.
+ * \return A StreamTubeChannelPtr object pointing to the newly created
+ * StreamTubeChannel object.
+ */
+StreamTubeChannelPtr StreamTubeChannel::create(const ConnectionPtr &connection,
+ const QString &objectPath, const QVariantMap &immutableProperties)
+{
+ return StreamTubeChannelPtr(new StreamTubeChannel(connection, objectPath,
+ immutableProperties, StreamTubeChannel::FeatureCore));
+}
+
+/**
+ * Construct a new StreamTubeChannel object.
+ *
+ * \param connection Connection owning this channel, and specifying the
+ * service.
+ * \param objectPath The channel object path.
+ * \param immutableProperties The channel immutable properties.
+ * \param coreFeature The core feature of the channel type, if any. The corresponding introspectable should
+ * depend on StreamTubeChannel::FeatureCore.
+ */
+StreamTubeChannel::StreamTubeChannel(const ConnectionPtr &connection,
+ const QString &objectPath,
+ const QVariantMap &immutableProperties,
+ const Feature &coreFeature)
+ : TubeChannel(connection, objectPath, immutableProperties, coreFeature),
+ mPriv(new Private(this))
+{
+}
+
+/**
+ * Class destructor.
+ */
+StreamTubeChannel::~StreamTubeChannel()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the service name which will be used over this stream tube. This should be a
+ * well-known TCP service name, for instance "rsync" or "daap".
+ *
+ * This method requires StreamTubeChannel::FeatureCore to be ready.
+ *
+ * \return The service name.
+ */
+QString StreamTubeChannel::service() const
+{
+ if (!isReady(FeatureCore)) {
+ warning() << "StreamTubeChannel::service() used with "
+ "FeatureCore not ready";
+ return QString();
+ }
+
+ return mPriv->serviceName;
+}
+
+/**
+ * Return whether this stream tube is capable to accept or offer an IPv4 socket accepting all
+ * incoming connections coming from localhost.
+ *
+ * Note that the \telepathy_spec implies that any connection manager, if capable of providing
+ * stream tubes, must at least support IPv4 sockets with localhost access control.
+ * For this reason, this method should always return \c true.
+ *
+ * This method requires StreamTubeChannel::FeatureCore to be ready.
+ *
+ * \return \c true if the stream tube is capable to accept or offer an IPv4 socket
+ * accepting all incoming connections coming from localhost, \c false otherwise.
+ * \sa IncomingStreamTubeChannel::acceptTubeAsTcpSocket(),
+ * OutgoingStreamTubeChannel::offerTcpSocket(),
+ * supportsIPv4SocketsWithSpecifiedAddress()
+ */
+bool StreamTubeChannel::supportsIPv4SocketsOnLocalhost() const
+{
+ if (!isReady(FeatureCore)) {
+ warning() << "StreamTubeChannel::supportsIPv4SocketsOnLocalhost() used with "
+ "FeatureCore not ready";
+ return false;
+ }
+
+ return mPriv->socketTypes.value(SocketAddressTypeIPv4).contains(SocketAccessControlLocalhost);
+}
+
+/**
+ * Return whether this stream tube is capable to accept an IPv4 socket accepting all
+ * incoming connections coming from a specific address for incoming tubes or whether
+ * this stream tube is capable of mapping connections to the socket's source address for outgoing
+ * tubes.
+ *
+ * For incoming tubes, when this capability is available, the stream tube can be accepted specifying
+ * an IPv4 address. Every connection coming from any other address than the specified one will be
+ * rejected.
+ *
+ * For outgoing tubes, when this capability is available, one can keep track of incoming connections
+ * by enabling StreamTubeChannel::FeatureConnectionMonitoring (possibly before
+ * opening the stream tube itself), and checking OutgoingStreamTubeChannel::contactsForConnections()
+ * or OutgoingStreamTubeChannel::connectionsForSourceAddresses().
+ *
+ * Note that it is strongly advised to call this method before attempting to call
+ * IncomingStreamTubeChannel::acceptTubeAsTcpSocket() or
+ * OutgoingStreamTubeChannel::offerTcpSocket() with a specified address to prevent failures,
+ * as the spec implies this feature is not compulsory for connection managers.
+ *
+ * This method requires StreamTubeChannel::FeatureCore to be ready.
+ *
+ * \return \c true if the stream tube is capable to accept an IPv4 socket accepting all
+ * incoming connections coming from a specific address for incoming tubes or
+ * the stream tube is capable of mapping connections to the socket's source address for
+ * outgoing tubes, \c false otherwise.
+ * \sa IncomingStreamTubeChannel::acceptTubeAsTcpSocket(),
+ * OutgoingStreamTubeChannel::offerTcpSocket(),
+ * OutgoingStreamTubeChannel::connectionsForSourceAddresses(),
+ * OutgoingStreamTubeChannel::contactsForConnections(),
+ * supportsIPv4SocketsOnLocalhost()
+ */
+bool StreamTubeChannel::supportsIPv4SocketsWithSpecifiedAddress() const
+{
+ if (!isReady(FeatureCore)) {
+ warning() << "StreamTubeChannel::supportsIPv4SocketsWithSpecifiedAddress() used with "
+ "FeatureCore not ready";
+ return false;
+ }
+
+ return mPriv->socketTypes.value(SocketAddressTypeIPv4).contains(SocketAccessControlPort);
+}
+
+/**
+ * Return whether this stream tube is capable to accept or offer an IPv6 socket accepting all
+ * incoming connections coming from localhost.
+ *
+ * Note that it is strongly advised to call this method before attempting to call
+ * IncomingStreamTubeChannel::acceptTubeAsTcpSocket() or
+ * OutgoingStreamTubeChannel::offerTcpSocket() with a specified address to prevent failures,
+ * as the spec implies this feature is not compulsory for connection managers.
+ *
+ * This method requires StreamTubeChannel::FeatureCore to be ready.
+ *
+ * \return \c true if the stream tube is capable to accept or offer an IPv6 socket
+ * accepting all incoming connections coming from localhost, \c false otherwise.
+ * \sa IncomingStreamTubeChannel::acceptTubeAsTcpSocket(),
+ * OutgoingStreamTubeChannel::offerTcpSocket(),
+ * supportsIPv6SocketsWithSpecifiedAddress()
+ */
+bool StreamTubeChannel::supportsIPv6SocketsOnLocalhost() const
+{
+ if (!isReady(FeatureCore)) {
+ warning() << "StreamTubeChannel::supportsIPv6SocketsOnLocalhost() used with "
+ "FeatureCore not ready";
+ return false;
+ }
+
+ return mPriv->socketTypes.value(SocketAddressTypeIPv6).contains(SocketAccessControlLocalhost);
+}
+
+/**
+ * Return whether this stream tube is capable to accept an IPv6 socket accepting all
+ * incoming connections coming from a specific address for incoming tubes or whether
+ * this stream tube is capable of mapping connections to the socket's source address for outgoing
+ * tubes.
+ *
+ * For incoming tubes, when this capability is available, the stream tube can be accepted specifying
+ * an IPv6 address. Every connection coming from any other address than the specified one will be
+ * rejected.
+ *
+ * For outgoing tubes, when this capability is available, one can keep track of incoming connections
+ * by enabling StreamTubeChannel::FeatureConnectionMonitoring (possibly before
+ * opening the stream tube itself), and checking OutgoingStreamTubeChannel::contactsForConnections()
+ * or OutgoingStreamTubeChannel::connectionsForSourceAddresses().
+ *
+ * Note that it is strongly advised to call this method before attempting to call
+ * IncomingStreamTubeChannel::acceptTubeAsTcpSocket() or
+ * OutgoingStreamTubeChannel::offerTcpSocket() with a specified address to prevent failures,
+ * as the spec implies this feature is not compulsory for connection managers.
+ *
+ * This method requires StreamTubeChannel::FeatureCore to be ready.
+ *
+ * \return \c true if the stream tube is capable to accept an IPv6 socket accepting all
+ * incoming connections coming from a specific address for incoming tubes or
+ * the stream tube is capable of mapping connections to the socket's source address for
+ * outgoing tubes, \c false otherwise.
+ * \sa IncomingStreamTubeChannel::acceptTubeAsTcpSocket(),
+ * OutgoingStreamTubeChannel::offerTcpSocket(),
+ * OutgoingStreamTubeChannel::connectionsForSourceAddresses(),
+ * OutgoingStreamTubeChannel::contactsForConnections(),
+ * supportsIPv6SocketsOnLocalhost()
+ */
+bool StreamTubeChannel::supportsIPv6SocketsWithSpecifiedAddress() const
+{
+ if (!isReady(FeatureCore)) {
+ warning() << "StreamTubeChannel::supportsIPv6SocketsWithSpecifiedAddress() used with "
+ "FeatureCore not ready";
+ return false;
+ }
+
+ return mPriv->socketTypes.value(SocketAddressTypeIPv6).contains(SocketAccessControlPort);
+}
+
+/**
+ * Return whether this stream tube is capable to accept or offer an Unix socket accepting all
+ * incoming connections coming from localhost.
+ *
+ * Note that it is strongly advised to call this method before attempting to call
+ * IncomingStreamTubeChannel::acceptTubeAsUnixSocket() or
+ * OutgoingStreamTubeChannel::offerUnixSocket() without credentials enabled, as the spec implies
+ * this feature is not compulsory for connection managers.
+ *
+ * This method requires StreamTubeChannel::FeatureCore to be ready.
+ *
+ * \return \c true if the stream tube is capable to accept or offer an Unix socket
+ * accepting all incoming connections coming from localhost, \c false otherwise.
+ * \sa IncomingStreamTubeChannel::acceptTubeAsUnixSocket(),
+ * OutgoingStreamTubeChannel::offerUnixSocket(),
+ * supportsUnixSocketsWithCredentials()
+ * supportsAbstractUnixSocketsOnLocalhost(),
+ * supportsAbstractUnixSocketsWithCredentials(),
+ */
+bool StreamTubeChannel::supportsUnixSocketsOnLocalhost() const
+{
+ if (!isReady(FeatureCore)) {
+ warning() << "StreamTubeChannel::supportsUnixSocketsOnLocalhost() used with "
+ "FeatureCore not ready";
+ return false;
+ }
+
+ return mPriv->socketTypes.value(SocketAddressTypeUnix).contains(SocketAccessControlLocalhost);
+}
+
+/**
+ * Return whether this stream tube is capable to accept or offer an Unix socket which will require
+ * credentials upon connection.
+ *
+ * When this capability is available and enabled, the connecting process must send a byte when
+ * it first connects, which is not considered to be part of the data stream.
+ * If the operating system uses sendmsg() with SCM_CREDS or SCM_CREDENTIALS to pass
+ * credentials over sockets, the connecting process must do so if possible;
+ * if not, it must still send the byte.
+ *
+ * The listening process will disconnect the connection unless it can determine
+ * by OS-specific means that the connecting process has the same user ID as the listening process.
+ *
+ * Note that it is strongly advised to call this method before attempting to call
+ * IncomingStreamTubeChannel::acceptTubeAsUnixSocket() or
+ * OutgoingStreamTubeChannel::offerUnixSocket() with credentials enabled, as the spec implies
+ * this feature is not compulsory for connection managers.
+ *
+ * This method requires StreamTubeChannel::FeatureCore to be ready.
+ *
+ * \return \c true if the stream tube is capable to accept or offer an Unix socket
+ * which will require credentials upon connection, \c false otherwise.
+ * \sa IncomingStreamTubeChannel::acceptTubeAsUnixSocket(),
+ * OutgoingStreamTubeChannel::offerUnixSocket(),
+ * supportsUnixSocketsOnLocalhost(),
+ * supportsAbstractUnixSocketsOnLocalhost(),
+ * supportsAbstractUnixSocketsWithCredentials(),
+ */
+bool StreamTubeChannel::supportsUnixSocketsWithCredentials() const
+{
+ if (!isReady(FeatureCore)) {
+ warning() << "StreamTubeChannel::supportsUnixSocketsWithCredentials() used with "
+ "FeatureCore not ready";
+ return false;
+ }
+
+ return mPriv->socketTypes[SocketAddressTypeUnix].contains(SocketAccessControlCredentials);
+}
+
+/**
+ * Return whether this stream tube is capable to accept or offer an abstract Unix socket accepting
+ * all incoming connections coming from localhost.
+ *
+ * Note that it is strongly advised to call this method before attempting to call
+ * IncomingStreamTubeChannel::acceptTubeAsUnixSocket() or
+ * OutgoingStreamTubeChannel::offerUnixSocket() without credentials enabled, as the spec implies
+ * this feature is not compulsory for connection managers.
+ *
+ * This method requires StreamTubeChannel::FeatureCore to be ready.
+ *
+ * \return \c true if the stream tube is capable to accept or offer an abstract Unix socket
+ * accepting all incoming connections coming from localhost, \c false otherwise.
+ * \sa IncomingStreamTubeChannel::acceptTubeAsUnixSocket(),
+ * OutgoingStreamTubeChannel::offerUnixSocket(),
+ * supportsUnixSocketsOnLocalhost(),
+ * supportsUnixSocketsWithCredentials(),
+ * supportsAbstractUnixSocketsWithCredentials()
+ */
+bool StreamTubeChannel::supportsAbstractUnixSocketsOnLocalhost() const
+{
+ if (!isReady(FeatureCore)) {
+ warning() << "StreamTubeChannel::supportsAbstractUnixSocketsOnLocalhost() used with "
+ "FeatureCore not ready";
+ return false;
+ }
+
+ return mPriv->socketTypes[SocketAddressTypeAbstractUnix].contains(SocketAccessControlLocalhost);
+}
+
+/**
+ * Return whether this stream tube is capable to accept or offer an abstract Unix socket which will
+ * require credentials upon connection.
+ *
+ * When this capability is available and enabled, the connecting process must send a byte when
+ * it first connects, which is not considered to be part of the data stream.
+ * If the operating system uses sendmsg() with SCM_CREDS or SCM_CREDENTIALS to pass
+ * credentials over sockets, the connecting process must do so if possible;
+ * if not, it must still send the byte.
+ *
+ * The listening process will disconnect the connection unless it can determine
+ * by OS-specific means that the connecting process has the same user ID as the listening process.
+ *
+ * Note that it is strongly advised to call this method before attempting to call
+ * IncomingStreamTubeChannel::acceptTubeAsUnixSocket() or
+ * OutgoingStreamTubeChannel::offerUnixSocket() with credentials enabled, as the spec implies
+ * this feature is not compulsory for connection managers.
+ *
+ * This method requires StreamTubeChannel::FeatureCore to be ready.
+ *
+ * \return \c true if the stream tube is capable to accept or offer an abstract Unix socket
+ * which will require credentials upon connection, \c false otherwise.
+ * \sa IncomingStreamTubeChannel::acceptTubeAsUnixSocket(),
+ * OutgoingStreamTubeChannel::offerUnixSocket(),
+ * supportsUnixSocketsOnLocalhost(),
+ * supportsUnixSocketsWithCredentials(),
+ * supportsAbstractUnixSocketsOnLocalhost()
+ */
+bool StreamTubeChannel::supportsAbstractUnixSocketsWithCredentials() const
+{
+ if (!isReady(FeatureCore)) {
+ warning() << "StreamTubeChannel::supportsAbstractUnixSocketsWithCredentials() used with "
+ "FeatureCore not ready";
+ return false;
+ }
+
+ return mPriv->socketTypes[SocketAddressTypeAbstractUnix].contains(SocketAccessControlCredentials);
+}
+
+/**
+ * Return all the known active connections since StreamTubeChannel::FeatureConnectionMonitoring has
+ * been enabled.
+ *
+ * For this method to return all known connections, you need to make
+ * StreamTubeChannel::FeatureConnectionMonitoring ready before accepting or offering the stream
+ * tube.
+ *
+ * This method requires StreamTubeChannel::FeatureConnectionMonitoring to be ready.
+ *
+ * \return The list of active connection ids.
+ * \sa newConnection(), connectionClosed()
+ */
+UIntList StreamTubeChannel::connections() const
+{
+ if (!isReady(FeatureConnectionMonitoring)) {
+ warning() << "StreamTubeChannel::connections() used with "
+ "FeatureConnectionMonitoring not ready";
+ return UIntList();
+ }
+
+ return mPriv->connections.toList();
+}
+
+/**
+ * Return the type of the tube's local endpoint socket.
+ *
+ * Note that this function will return a valid value only after state() has gone #TubeStateOpen.
+ *
+ * \return The socket type as #SocketAddressType.
+ * \sa localAddress(), ipAddress()
+ */
+SocketAddressType StreamTubeChannel::addressType() const
+{
+ return mPriv->addressType;
+}
+
+/**
+ * Return the access control used by this stream tube.
+ *
+ * Note that this function will only return a valid value after state() has gone #TubeStateOpen.
+ *
+ * \return The access control as #SocketAccessControl.
+ * \sa addressType()
+ */
+SocketAccessControl StreamTubeChannel::accessControl() const
+{
+ return mPriv->accessControl;
+}
+
+/**
+ * Return the IP address/port combination used by this stream tube.
+ *
+ * This method will return a meaningful value only if the local endpoint socket for the tube is a
+ * TCP socket, i.e. addressType() is #SocketAddressTypeIPv4 or #SocketAddressTypeIPv6.
+ *
+ * Note that this function will return a valid value only after state() has gone #TubeStateOpen.
+ *
+ * \return Pair of IP address as QHostAddress and port if using a TCP socket,
+ * or an undefined value otherwise.
+ * \sa localAddress()
+ */
+QPair<QHostAddress, quint16> StreamTubeChannel::ipAddress() const
+{
+ if (state() != TubeChannelStateOpen) {
+ warning() << "Tube not open, returning invalid IP address";
+ return qMakePair<QHostAddress, quint16>(QHostAddress::Null, 0);
+ }
+
+ return mPriv->ipAddress;
+}
+
+/**
+ * Return the local address used by this stream tube.
+ *
+ * This method will return a meaningful value only if the local endpoint socket for the tube is an
+ * UNIX socket, i.e. addressType() is #SocketAddressTypeUnix or #SocketAddressTypeAbstractUnix.
+ *
+ * Note that this function will return a valid value only after state() has gone #TubeStateOpen.
+ *
+ * \return Unix socket address if using an Unix socket,
+ * or an undefined value otherwise.
+ * \sa ipAddress()
+ */
+QString StreamTubeChannel::localAddress() const
+{
+ if (state() != TubeChannelStateOpen) {
+ warning() << "Tube not open, returning invalid local socket address";
+ return QString();
+ }
+
+ return mPriv->unixAddress;
+}
+
+/**
+ * \deprecated This method never did anything useful when called from outside,
+ * and now does nothing at all. It will be removed in the next API/ABI break.
+ */
+void StreamTubeChannel::setBaseTubeType(uint type)
+{
+ Q_UNUSED(type);
+}
+
+void StreamTubeChannel::setConnections(UIntList connections)
+{
+ // This is rather sub-optimal: we'll do a O(n) replace of the old connections list every time a
+ // connection is added, so O(n^2) in total for adding n connections
+ mPriv->connections = QSet<uint>::fromList(connections);
+}
+
+void StreamTubeChannel::addConnection(uint connection)
+{
+ if (!mPriv->connections.contains(connection)) {
+ mPriv->connections.insert(connection);
+ emit newConnection(connection);
+ } else {
+ warning() << "Tried to add connection" << connection << "on StreamTube" << objectPath()
+ << "but it already was there";
+ }
+}
+
+void StreamTubeChannel::removeConnection(uint connection, const QString &error,
+ const QString &message)
+{
+ if (mPriv->connections.contains(connection)) {
+ mPriv->connections.remove(connection);
+ emit connectionClosed(connection, error, message);
+ } else {
+ warning() << "Tried to remove connection" << connection << "from StreamTube" << objectPath()
+ << "but it wasn't there";
+ }
+}
+
+void StreamTubeChannel::setAddressType(SocketAddressType type)
+{
+ mPriv->addressType = type;
+}
+
+void StreamTubeChannel::setAccessControl(SocketAccessControl accessControl)
+{
+ mPriv->accessControl = accessControl;
+}
+
+void StreamTubeChannel::setIpAddress(const QPair<QHostAddress, quint16> &address)
+{
+ mPriv->ipAddress = address;
+}
+
+void StreamTubeChannel::setLocalAddress(const QString &address)
+{
+ mPriv->unixAddress = address;
+}
+
+bool StreamTubeChannel::isDroppingConnections() const
+{
+ return mPriv->droppingConnections;
+}
+
+void StreamTubeChannel::gotStreamTubeProperties(PendingOperation *op)
+{
+ if (!op->isError()) {
+ PendingVariantMap *pvm = qobject_cast<PendingVariantMap *>(op);
+
+ mPriv->extractStreamTubeProperties(pvm->result());
+
+ debug() << "Got reply to Properties::GetAll(StreamTubeChannel)";
+ mPriv->readinessHelper->setIntrospectCompleted(StreamTubeChannel::FeatureCore, true);
+ }
+ else {
+ warning().nospace() << "Properties::GetAll(StreamTubeChannel) failed "
+ "with " << op->errorName() << ": " << op->errorMessage();
+ mPriv->readinessHelper->setIntrospectCompleted(StreamTubeChannel::FeatureCore, false,
+ op->errorName(), op->errorMessage());
+ }
+}
+
+void StreamTubeChannel::onConnectionClosed(uint connId, const QString &error,
+ const QString &message)
+{
+ removeConnection(connId, error, message);
+}
+
+void StreamTubeChannel::dropConnections()
+{
+ if (!mPriv->connections.isEmpty()) {
+ debug() << "StreamTubeChannel invalidated with" << mPriv->connections.size()
+ << "connections remaining, synthesizing close events";
+ mPriv->droppingConnections = true;
+ foreach (uint connId, mPriv->connections) {
+ removeConnection(connId, TP_QT_ERROR_ORPHANED,
+ QLatin1String("parent tube invalidated, streams closing"));
+ }
+ mPriv->droppingConnections = false;
+ }
+}
+
+/**
+ * \fn void StreamTubeChannel::connectionClosed(uint connectionId,
+ * const QString &errorName, const QString &errorMessage)
+ *
+ * Emitted when a connection on this stream tube has been closed.
+ *
+ * \param connectionId The unique ID associated with the connection that was closed.
+ * \param errorName The name of a D-Bus error describing the error that occurred.
+ * \param errorMessage A debugging message associated with the error.
+ * \sa newConnection(), connections()
+ */
+
+} // Tp
diff --git a/TelepathyQt/stream-tube-channel.h b/TelepathyQt/stream-tube-channel.h
new file mode 100644
index 00000000..38ec6c43
--- /dev/null
+++ b/TelepathyQt/stream-tube-channel.h
@@ -0,0 +1,108 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010-2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_stream_tube_channel_h_HEADER_GUARD_
+#define _TelepathyQt_stream_tube_channel_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/TubeChannel>
+
+class QHostAddress;
+
+namespace Tp
+{
+
+class TP_QT_EXPORT StreamTubeChannel : public TubeChannel
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(StreamTubeChannel)
+
+public:
+ static const Feature FeatureCore;
+ // FIXME (API/ABI break) Remove FeatureStreamTube in favour of FeatureCore
+ static const Feature FeatureStreamTube;
+ static const Feature FeatureConnectionMonitoring;
+
+ static StreamTubeChannelPtr create(const ConnectionPtr &connection,
+ const QString &objectPath, const QVariantMap &immutableProperties);
+
+ virtual ~StreamTubeChannel();
+
+ QString service() const;
+
+ bool supportsIPv4SocketsOnLocalhost() const;
+ bool supportsIPv4SocketsWithSpecifiedAddress() const;
+
+ bool supportsIPv6SocketsOnLocalhost() const;
+ bool supportsIPv6SocketsWithSpecifiedAddress() const;
+
+ bool supportsUnixSocketsOnLocalhost() const;
+ bool supportsUnixSocketsWithCredentials() const;
+
+ bool supportsAbstractUnixSocketsOnLocalhost() const;
+ bool supportsAbstractUnixSocketsWithCredentials() const;
+
+ // API/ABI break TODO: return QSet<uint> instead
+ UIntList connections() const;
+
+ SocketAddressType addressType() const;
+
+ QPair< QHostAddress, quint16 > ipAddress() const;
+ QString localAddress() const;
+
+Q_SIGNALS:
+ void newConnection(uint connectionId);
+ void connectionClosed(uint connectionId, const QString &errorName,
+ const QString &errorMessage);
+
+protected:
+ StreamTubeChannel(const ConnectionPtr &connection, const QString &objectPath,
+ const QVariantMap &immutableProperties,
+ const Feature &coreFeature = StreamTubeChannel::FeatureCore);
+
+ TP_QT_DEPRECATED void setBaseTubeType(uint type);
+ TP_QT_DEPRECATED void setConnections(UIntList connections); // -> {add,remove}Connection
+ void addConnection(uint connection);
+ void removeConnection(uint connection, const QString &error, const QString &message);
+ void setAddressType(SocketAddressType type);
+ SocketAccessControl accessControl() const;
+ void setAccessControl(SocketAccessControl accessControl);
+ void setIpAddress(const QPair<QHostAddress, quint16> &address);
+ void setLocalAddress(const QString &address);
+ bool isDroppingConnections() const;
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void gotStreamTubeProperties(Tp::PendingOperation *op);
+ TP_QT_NO_EXPORT void onConnectionClosed(uint, const QString &, const QString &);
+ TP_QT_NO_EXPORT void dropConnections();
+
+private:
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+}
+
+#endif
diff --git a/TelepathyQt/stream-tube-client-internal.h b/TelepathyQt/stream-tube-client-internal.h
new file mode 100644
index 00000000..b190e816
--- /dev/null
+++ b/TelepathyQt/stream-tube-client-internal.h
@@ -0,0 +1,61 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/Account>
+#include <TelepathyQt/ClientRegistrar>
+#include <TelepathyQt/IncomingStreamTubeChannel>
+#include <TelepathyQt/StreamTubeClient>
+
+namespace Tp
+{
+
+class TP_QT_NO_EXPORT StreamTubeClient::TubeWrapper :
+ public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(TubeWrapper)
+
+public:
+ TubeWrapper(const AccountPtr &acc, const IncomingStreamTubeChannelPtr &tube,
+ const QHostAddress &sourceAddress, quint16 sourcePort, StreamTubeClient *parent);
+ TubeWrapper(const AccountPtr &acc, const IncomingStreamTubeChannelPtr &tube,
+ bool requireCredentials, StreamTubeClient *parent);
+ ~TubeWrapper() { }
+
+ AccountPtr mAcc;
+ IncomingStreamTubeChannelPtr mTube;
+ QHostAddress mSourceAddress;
+ quint16 mSourcePort;
+
+Q_SIGNALS:
+ void acceptFinished(TubeWrapper *wrapper, Tp::PendingStreamTubeConnection *conn);
+ void newConnection(TubeWrapper *wrapper, uint conn);
+ void connectionClosed(TubeWrapper *wrapper, uint conn, const QString &error,
+ const QString &message);
+
+private Q_SLOTS:
+ void onTubeAccepted(Tp::PendingOperation *);
+ void onNewConnection(uint);
+ void onConnectionClosed(uint, const QString &, const QString &);
+};
+
+} // Tp
diff --git a/TelepathyQt/stream-tube-client.cpp b/TelepathyQt/stream-tube-client.cpp
new file mode 100644
index 00000000..85ee2001
--- /dev/null
+++ b/TelepathyQt/stream-tube-client.cpp
@@ -0,0 +1,1048 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/StreamTubeClient>
+
+#include "TelepathyQt/stream-tube-client-internal.h"
+#include "TelepathyQt/_gen/stream-tube-client.moc.hpp"
+#include "TelepathyQt/_gen/stream-tube-client-internal.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+#include "TelepathyQt/simple-stream-tube-handler.h"
+
+#include <TelepathyQt/AccountManager>
+#include <TelepathyQt/ClientRegistrar>
+#include <TelepathyQt/IncomingStreamTubeChannel>
+#include <TelepathyQt/PendingStreamTubeConnection>
+#include <TelepathyQt/StreamTubeChannel>
+
+#include <QAbstractSocket>
+#include <QHash>
+
+namespace Tp
+{
+
+/**
+ * \class StreamTubeClient::TcpSourceAddressGenerator
+ * \ingroup serverclient
+ * \headerfile TelepathyQt/stream-tube-client.h <TelepathyQt/StreamTubeClient>
+ *
+ * \brief The StreamTubeClient::TcpSourceAddressGenerator abstract interface allows using socket
+ * source address/port based access control for connecting to tubes accepted as TCP sockets.
+ *
+ * By default, every application on the local computer is allowed to connect to the socket created
+ * by the protocol backend as the local endpoint of the tube. This is not always desirable, as that
+ * includes even other users.
+ *
+ * Note that since every TCP connection must have an unique source address, only one simultaneous
+ * connection can be made through each tube for which this type of access control has been used.
+ */
+
+/**
+ * \fn QPair<QHostAddress, quint16> StreamTubeClient::TcpSourceAddressGenerator::nextSourceAddress(const AccountPtr &, const
+ * IncomingStreamTubeChannelPtr &)
+ *
+ * Return the source address from which connections will be allowed to the given \a tube once it has
+ * been accepted.
+ *
+ * Returning the pair (QHostAddress::Any, 0) makes the protocol backend allow connections from any
+ * address on the local computer. This can be used on a tube-by-tube basis if for some tubes its
+ * known that multiple connections need to be made, so a single source address doesn't suffice.
+ *
+ * The \a account and \a tube parameters can be inspected to make the decision; typically by looking
+ * at the tube's service type, parameters and/or initiator contact.
+ *
+ * The general pattern for implementing this method is:
+ * <ol>
+ * <li>Determine whether \a tube needs to allow multiple connections, and if so, skip source address
+ * access control completely</li>
+ * <li>Otherwise, create a socket and bind it to a free address</li>
+ * <li>Return this socket's address</li>
+ * <li>Keep the socket bound so that no other process can (accidentally or maliciously) take the
+ * address until it's used to connect to the tube when StreamTubeClient::tubeAcceptedAsTcp() is
+ * emitted for the tube</li>
+ * </ol>
+ *
+ * \param account The account from which the tube originates.
+ * \param tube The tube channel which is going to be accepted by the StreamTubeClient.
+ *
+ * \return A pair containing the host address and port allowed to connect.
+ */
+
+/**
+ * \fn StreamTubeClient::TcpSourceAddressGenerator::~TcpSourceAddressGenerator
+ *
+ * Class destructor. Protected, because StreamTubeClient never deletes a TcpSourceAddressGenerator passed
+ * to it.
+ */
+
+struct TP_QT_NO_EXPORT StreamTubeClient::Tube::Private : public QSharedData
+{
+ // empty placeholder for now
+};
+
+/**
+ * \class StreamTubeClient::Tube
+ * \ingroup serverclient
+ * \headerfile TelepathyQt/stream-tube-client.h <TelepathyQt/StreamTubeClient>
+ *
+ * \brief The StreamTubeClient::Tube class represents a tube being handled by the client.
+ */
+
+/**
+ * Constructs a new invalid Tube instance.
+ */
+StreamTubeClient::Tube::Tube()
+{
+ // invalid instance
+}
+
+/**
+ * Constructs a Tube instance for the given tube \a channel from the given \a account.
+ *
+ * \param account A pointer to the account the online connection of which the tube originates from.
+ * \param channel A pointer to the tube channel object.
+ */
+StreamTubeClient::Tube::Tube(
+ const AccountPtr &account,
+ const IncomingStreamTubeChannelPtr &channel)
+ : QPair<AccountPtr, IncomingStreamTubeChannelPtr>(account, channel), mPriv(new Private)
+{
+}
+
+/**
+ * Copy constructor.
+ */
+StreamTubeClient::Tube::Tube(
+ const Tube &other)
+ : QPair<AccountPtr, IncomingStreamTubeChannelPtr>(other.account(), other.channel()),
+ mPriv(other.mPriv)
+{
+}
+
+/**
+ * Class destructor.
+ */
+StreamTubeClient::Tube::~Tube()
+{
+ // mPriv deleted automatically
+}
+
+/**
+ * Assignment operator.
+ */
+StreamTubeClient::Tube &StreamTubeClient::Tube::operator=(
+ const Tube &other)
+{
+ if (&other == this) {
+ return *this;
+ }
+
+ first = other.account();
+ second = other.channel();
+ mPriv = other.mPriv;
+
+ return *this;
+}
+
+/**
+ * \fn bool StreamTubeClient::Tube::isValid() const
+ *
+ * Return whether or not the tube is valid or is just the null object created using the default
+ * constructor.
+ *
+ * \return \c true if valid, \c false otherwise.
+ */
+
+/**
+ * \fn AccountPtr StreamTubeClient::Tube::account() const
+ *
+ * Return the account from which the tube originates.
+ *
+ * \return A pointer to the account object.
+ */
+
+/**
+ * \fn IncomingStreamTubeChannelPtr StreamTubeClient::Tube::channel() const
+ *
+ * Return the actual tube channel.
+ *
+ * \return A pointer to the channel.
+ */
+
+struct TP_QT_NO_EXPORT StreamTubeClient::Private
+{
+ Private(const ClientRegistrarPtr &registrar,
+ const QStringList &p2pServices,
+ const QStringList &roomServices,
+ const QString &maybeClientName,
+ bool monitorConnections,
+ bool bypassApproval)
+ : registrar(registrar),
+ handler(SimpleStreamTubeHandler::create(
+ p2pServices, roomServices, false, monitorConnections, bypassApproval)),
+ clientName(maybeClientName),
+ isRegistered(false),
+ acceptsAsTcp(false), acceptsAsUnix(false),
+ tcpGenerator(0), requireCredentials(false)
+ {
+ if (clientName.isEmpty()) {
+ clientName = QString::fromLatin1("TpQt4STubeClient_%1_%2")
+ .arg(registrar->dbusConnection().baseService()
+ .replace(QLatin1Char(':'), QLatin1Char('_'))
+ .replace(QLatin1Char('.'), QLatin1Char('_')))
+ .arg((intptr_t) this, 0, 16);
+ }
+ }
+
+ void ensureRegistered()
+ {
+ if (isRegistered) {
+ return;
+ }
+
+ debug() << "Register StreamTubeClient with name " << clientName;
+
+ if (registrar->registerClient(handler, clientName)) {
+ isRegistered = true;
+ } else {
+ warning() << "StreamTubeClient" << clientName
+ << "registration failed";
+ }
+ }
+
+ ClientRegistrarPtr registrar;
+ SharedPtr<SimpleStreamTubeHandler> handler;
+ QString clientName;
+ bool isRegistered;
+
+ bool acceptsAsTcp, acceptsAsUnix;
+ TcpSourceAddressGenerator *tcpGenerator;
+ bool requireCredentials;
+
+ QHash<StreamTubeChannelPtr, TubeWrapper *> tubes;
+};
+
+StreamTubeClient::TubeWrapper::TubeWrapper(
+ const AccountPtr &acc,
+ const IncomingStreamTubeChannelPtr &tube,
+ const QHostAddress &sourceAddress,
+ quint16 sourcePort,
+ StreamTubeClient *parent)
+ : QObject(parent), mAcc(acc), mTube(tube), mSourceAddress(sourceAddress), mSourcePort(sourcePort)
+{
+ if (sourcePort != 0) {
+ if ((sourceAddress.protocol() == QAbstractSocket::IPv4Protocol &&
+ !tube->supportsIPv4SocketsWithSpecifiedAddress()) ||
+ (sourceAddress.protocol() == QAbstractSocket::IPv6Protocol &&
+ !tube->supportsIPv6SocketsWithSpecifiedAddress())) {
+ debug() << "StreamTubeClient falling back to Localhost AC for tube" <<
+ tube->objectPath();
+ mSourceAddress = sourceAddress.protocol() == QAbstractSocket::IPv4Protocol ?
+ QHostAddress::Any : QHostAddress::AnyIPv6;
+ mSourcePort = 0;
+ }
+ }
+
+ connect(tube->acceptTubeAsTcpSocket(mSourceAddress, mSourcePort),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onTubeAccepted(Tp::PendingOperation*)));
+ connect(tube.data(),
+ SIGNAL(newConnection(uint)),
+ SLOT(onNewConnection(uint)));
+ connect(tube.data(),
+ SIGNAL(connectionClosed(uint,QString,QString)),
+ SLOT(onConnectionClosed(uint,QString,QString)));
+}
+
+StreamTubeClient::TubeWrapper::TubeWrapper(
+ const AccountPtr &acc,
+ const IncomingStreamTubeChannelPtr &tube,
+ bool requireCredentials,
+ StreamTubeClient *parent)
+ : QObject(parent), mAcc(acc), mTube(tube), mSourcePort(0)
+{
+ if (requireCredentials && !tube->supportsUnixSocketsWithCredentials()) {
+ debug() << "StreamTubeClient falling back to Localhost AC for tube" << tube->objectPath();
+ requireCredentials = false;
+ }
+
+ connect(tube->acceptTubeAsUnixSocket(requireCredentials),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onTubeAccepted(Tp::PendingOperation*)));
+ connect(tube.data(),
+ SIGNAL(newConnection(uint)),
+ SLOT(onNewConnection(uint)));
+ connect(tube.data(),
+ SIGNAL(connectionClosed(uint,QString,QString)),
+ SLOT(onConnectionClosed(uint,QString,QString)));
+}
+
+void StreamTubeClient::TubeWrapper::onTubeAccepted(Tp::PendingOperation *op)
+{
+ emit acceptFinished(this, qobject_cast<Tp::PendingStreamTubeConnection *>(op));
+}
+
+void StreamTubeClient::TubeWrapper::onNewConnection(uint conn)
+{
+ emit newConnection(this, conn);
+}
+
+void StreamTubeClient::TubeWrapper::onConnectionClosed(uint conn, const QString &error,
+ const QString &message)
+{
+ emit connectionClosed(this, conn, error, message);
+}
+
+/**
+ * \class StreamTubeClient
+ * \ingroup serverclient
+ * \headerfile TelepathyQt/stream-tube-client.h <TelepathyQt/StreamTubeClient>
+ *
+ * \brief The StreamTubeClient class is a Handler implementation for incoming %Stream %Tube channels,
+ * allowing an application to easily get notified about services they can connect to offered to them
+ * over Telepathy Tubes without worrying about the channel dispatching details.
+ *
+ * Telepathy Tubes is a technology for connecting arbitrary applications together through the IM
+ * network (and sometimes with direct peer-to-peer connections), such that issues like firewall/NAT
+ * traversal are automatically handled. Stream Tubes in particular offer properties similar to
+ * SOCK_STREAM sockets. The StreamTubeClient class negotiates tubes offered to us so that an
+ * application can connect such bytestream sockets of theirs to them. The StreamTubeServer class is
+ * the counterpart, offering services from a bytestream socket server to tubes requested to be
+ * initiated.
+ *
+ * Both peer-to-peer (\c TargetHandleType == \ref HandleTypeContact) and group (\c TargetHandleType
+ * == \ref HandleTypeRoom) channels are supported, and it's possible to specify the tube services to
+ * handle for each separately. There must be at least one service in total declared, as it never
+ * makes sense to handle stream tubes without considering the protocol of the service offered
+ * through them.
+ *
+ * %Connection monitoring allows fine-grained error reporting for connections made through tubes,
+ * and observing connections being made and broken even if the application code running
+ * StreamTubeClient can't easily get this information from the code actually connecting through it.
+ * Such a setting might occur e.g. when a wrapper application is developed to connect some existing
+ * "black box" networked application through TCP tubes by launching it with the appropriate command
+ * line arguments or alike for accepted tubes.
+ *
+ * Enabling connection monitoring adds a small overhead and latency to handling each incoming tube
+ * and signaling each new incoming connection over them, though, so use it only when needed.
+ *
+ * A service activated Handler can be implemented using StreamTubeClient by passing a predefined \a
+ * clientName manually to the chosen create() method, and installing Telepathy \c .client and D-Bus
+ * \c .service files declaring the implemented tube services as channel classes and a path to the
+ * executable. If this is not needed, the \a clientName can be omitted, in which case a random
+ * unique client name is generated and used instead. However, then the tube client application must
+ * already be running for remote contacts to be able to offer services to us over tubes.
+ *
+ * Whether the Handler application implemented using StreamTubeClient is service activatable or not,
+ * incoming channels will typically first be given to an Approver, if there is one for tube services
+ * corresponding to the tube in question. Only if the Approver decides that the tube communication
+ * should be allowed (usually by asking the user), or if there is no matching Approver at all, is
+ * the channel given to the actual Handler tube client. This can be overridden by setting \a
+ * bypassApproval to \c true, which skips approval for the given services completely and directs
+ * them straight to the Handler.
+ *
+ * StreamTubeClient shares Account, Connection and Channel proxies and Contact objects with the
+ * rest of the application as long as a reference to the AccountManager, ClientRegistrar, or the
+ * factories used elsewhere is passed to the create() method. A stand-alone tube client Handler can
+ * get away without passing these however, or just passing select factories to make the desired
+ * features prepared and subclasses employed for these objects for their own convenience.
+ *
+ * Whichever method is used, the ChannelFactory (perhaps indirectly) given must construct
+ * IncomingStreamTubeChannel instances or subclasses thereof for all channel classes corresponding
+ * to the tube services the client should be able to connect to. This is the default; overriding it
+ * without obeying these constraints using ChannelFactory::setSubclassForIncomingStreamTubes() or
+ * the related methods for room tubes prevents StreamTubeClient from operating correctly.
+ *
+ * \todo Coin up a small Python script or alike to easily generate the .client and .service files.
+ * (fd.o #41614)
+ */
+
+/**
+ * Create a new StreamTubeClient, which will register itself on the session bus using an internal
+ * ClientRegistrar and use the given factories.
+ *
+ * \param p2pServices Names of the tube services to accept on peer-to-peer tube channels.
+ * \param roomServices Names of the tube services to accept on room/group tube channels.
+ * \param clientName The client name (without the \c org.freedesktop.Telepathy.Client. prefix).
+ * \param monitorConnections Whether to enable connection monitoring or not.
+ * \param bypassApproval \c true to skip approval, \c false to invoke an Approver for incoming
+ * channels if there is one.
+ * \param accountFactory The account factory to use.
+ * \param connectionFactory The connection factory to use.
+ * \param channelFactory The channel factory to use.
+ * \param contactFactory The contact factory to use.
+ */
+StreamTubeClientPtr StreamTubeClient::create(
+ const QStringList &p2pServices,
+ const QStringList &roomServices,
+ const QString &clientName,
+ bool monitorConnections,
+ bool bypassApproval,
+ const AccountFactoryConstPtr &accountFactory,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory)
+{
+ return create(
+ QDBusConnection::sessionBus(),
+ accountFactory,
+ connectionFactory,
+ channelFactory,
+ contactFactory,
+ p2pServices,
+ roomServices,
+ clientName,
+ monitorConnections,
+ bypassApproval);
+}
+
+/**
+ * Create a new StreamTubeClient, which will register itself on the given \a bus using an internal
+ * ClientRegistrar and use the given factories.
+ *
+ * The factories must all be created for the given \a bus.
+ *
+ * \param bus Connection to the bus to register on.
+ * \param accountFactory The account factory to use.
+ * \param connectionFactory The connection factory to use.
+ * \param channelFactory The channel factory to use.
+ * \param contactFactory The contact factory to use.
+ * \param p2pServices Names of the tube services to handle on peer-to-peer tube channels.
+ * \param roomServices Names of the tube services to handle on room/group tube channels.
+ * \param clientName The client name (without the \c org.freedesktop.Telepathy.Client. prefix).
+ * \param monitorConnections Whether to enable connection monitoring or not.
+ * \param bypassApproval \c true to skip approval, \c false to invoke an Approver for incoming
+ * channels if there is one.
+ */
+StreamTubeClientPtr StreamTubeClient::create(
+ const QDBusConnection &bus,
+ const AccountFactoryConstPtr &accountFactory,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory,
+ const QStringList &p2pServices,
+ const QStringList &roomServices,
+ const QString &clientName,
+ bool monitorConnections,
+ bool bypassApproval)
+{
+ return create(
+ ClientRegistrar::create(
+ bus,
+ accountFactory,
+ connectionFactory,
+ channelFactory,
+ contactFactory),
+ p2pServices,
+ roomServices,
+ clientName,
+ monitorConnections,
+ bypassApproval);
+}
+
+/**
+ * Create a new StreamTubeClient, which will register itself on the bus of and share objects with
+ * the given \a accountManager, creating an internal ClientRegistrar.
+ *
+ * \param accountManager A pointer to the account manager to link up with.
+ * \param p2pServices Names of the tube services to handle on peer-to-peer tube channels.
+ * \param roomServices Names of the tube services to handle on room/group tube channels.
+ * \param clientName The client name (without the \c org.freedesktop.Telepathy.Client. prefix).
+ * \param monitorConnections Whether to enable connection monitoring or not.
+ * \param bypassApproval \c true to skip approval, \c false to invoke an Approver for incoming
+ * channels if there is one.
+ */
+StreamTubeClientPtr StreamTubeClient::create(
+ const AccountManagerPtr &accountManager,
+ const QStringList &p2pServices,
+ const QStringList &roomServices,
+ const QString &clientName,
+ bool monitorConnections,
+ bool bypassApproval)
+{
+ return create(
+ accountManager->dbusConnection(),
+ accountManager->accountFactory(),
+ accountManager->connectionFactory(),
+ accountManager->channelFactory(),
+ accountManager->contactFactory(),
+ p2pServices,
+ roomServices,
+ clientName,
+ monitorConnections,
+ bypassApproval);
+}
+
+/**
+ * Create a new StreamTubeClient, which will register itself on the bus of and using the given
+ * client \a registrar, and share objects with it.
+ *
+ * \param registrar The client registrar to use.
+ * \param p2pServices Names of the tube services to handle on peer-to-peer tube channels.
+ * \param roomServices Names of the tube services to handle on room/group tube channels.
+ * \param clientName The client name (without the \c org.freedesktop.Telepathy.Client. prefix).
+ * \param monitorConnections Whether to enable connection monitoring or not.
+ * \param bypassApproval \c true to skip approval, \c false to invoke an Approver for incoming
+ * channels if there is one.
+ */
+StreamTubeClientPtr StreamTubeClient::create(
+ const ClientRegistrarPtr &registrar,
+ const QStringList &p2pServices,
+ const QStringList &roomServices,
+ const QString &clientName,
+ bool monitorConnections,
+ bool bypassApproval)
+{
+ if (p2pServices.isEmpty() && roomServices.isEmpty()) {
+ warning() << "Tried to create a StreamTubeClient with no services, returning NULL";
+ return StreamTubeClientPtr();
+ }
+
+ return StreamTubeClientPtr(
+ new StreamTubeClient(registrar, p2pServices, roomServices, clientName,
+ monitorConnections, bypassApproval));
+}
+
+StreamTubeClient::StreamTubeClient(
+ const ClientRegistrarPtr &registrar,
+ const QStringList &p2pServices,
+ const QStringList &roomServices,
+ const QString &clientName,
+ bool monitorConnections,
+ bool bypassApproval)
+ : mPriv(new Private(registrar, p2pServices, roomServices, clientName, monitorConnections, bypassApproval))
+{
+ connect(mPriv->handler.data(),
+ SIGNAL(invokedForTube(
+ Tp::AccountPtr,
+ Tp::StreamTubeChannelPtr,
+ QDateTime,
+ Tp::ChannelRequestHints)),
+ SLOT(onInvokedForTube(
+ Tp::AccountPtr,
+ Tp::StreamTubeChannelPtr,
+ QDateTime,
+ Tp::ChannelRequestHints)));
+}
+
+/**
+ * Class destructor.
+ */
+StreamTubeClient::~StreamTubeClient()
+{
+ if (isRegistered()) {
+ mPriv->registrar->unregisterClient(mPriv->handler);
+ }
+
+ delete mPriv;
+}
+
+/**
+ * Return the client registrar used by the client to register itself as a Telepathy channel Handler
+ * %Client.
+ *
+ * This is the registrar originally passed to
+ * create(const ClientRegistrarPtr &, const QStringList &, const QStringList &, const QString &, bool, bool)
+ * if that was used, and an internally constructed one otherwise. In any case, it can be used to
+ * e.g. register further clients, just like any other ClientRegistrar.
+ *
+ * \return A pointer to the registrar.
+ */
+ClientRegistrarPtr StreamTubeClient::registrar() const
+{
+ return mPriv->registrar;
+}
+
+/**
+ * Return the Telepathy %Client name of the client.
+ *
+ * \return The name, without the \c org.freedesktop.Telepathy.Client. prefix of the full D-Bus service name.
+ */
+QString StreamTubeClient::clientName() const
+{
+ return mPriv->clientName;
+}
+
+/**
+ * Return whether the client has been successfully registered or not.
+ *
+ * Registration is attempted, at the latest, when the client is first set to accept incoming tubes,
+ * either as TCP sockets (setToAcceptAsTcp()) or Unix ones (setToAcceptAsUnix()). It can fail e.g.
+ * because the connection to the bus has failed, or a predefined \a clientName has been passed to
+ * create(), and a %Client with the same name is already registered. Typically, failure registering
+ * would be a fatal error for a stand-alone tube handler, but only a warning event for an
+ * application serving other purposes. In any case, a high-quality user of the API will check the
+ * return value of this accessor after choosing the desired address family.
+ *
+ * \return \c true if the client has been successfully registered, \c false if not.
+ */
+bool StreamTubeClient::isRegistered() const
+{
+ return mPriv->isRegistered;
+}
+
+/**
+ * Return whether connection monitoring is enabled on this client.
+ *
+ * For technical reasons, connection monitoring can't be enabled when the client is already running,
+ * so there is no corresponding setter method. It has to be enabled by passing \c true as the \a
+ * monitorConnections parameter to the create() method.
+ *
+ * If connection monitoring isn't enabled, newConnection() and connectionClosed() won't be
+ * emitted and connections() won't be populated.
+ *
+ * \return \c true if monitoring is enabled, \c false if not.
+ */
+bool StreamTubeClient::monitorsConnections() const
+{
+ return mPriv->handler->monitorsConnections();
+}
+
+/**
+ * Return whether the client is currently set to accept incoming tubes as TCP sockets.
+ *
+ * \return \c true if the client will accept tubes as TCP sockets, \c false if it will accept them
+ * as Unix ones or hasn't been set to accept at all yet.
+ */
+bool StreamTubeClient::acceptsAsTcp() const
+{
+ return mPriv->acceptsAsTcp;
+}
+
+/**
+ * Return the TCP source address generator, if any, set by setToAcceptAsTcp() previously.
+ *
+ * \return A pointer to the generator instance.
+ */
+StreamTubeClient::TcpSourceAddressGenerator *StreamTubeClient::tcpGenerator() const
+{
+ if (!acceptsAsTcp()) {
+ warning() << "StreamTubeClient::tcpGenerator() used, but not accepting as TCP, returning 0";
+ return 0;
+ }
+
+ return mPriv->tcpGenerator;
+}
+
+/**
+ * Return whether the client is currently set to accept incoming tubes as Unix sockets.
+ *
+ * \return \c true if the client will accept tubes as Unix sockets, \c false if it will accept them
+ * as TCP ones or hasn't been set to accept at all yet.
+ */
+bool StreamTubeClient::acceptsAsUnix() const
+{
+ return mPriv->acceptsAsUnix;
+}
+
+/**
+ * Set the client to accept tubes received to handle in the future in a fashion which will yield a
+ * TCP socket as the local endpoint to connect to.
+ *
+ * A source address generator can optionally be set. If non-null, it will be invoked for each new
+ * tube received to handle and an attempt is made to restrict connections to the tube's local socket
+ * endpoint to those from that source address.
+ *
+ * However, if the protocol backend doesn't actually support source address based access control,
+ * tubeAcceptedAsTcp() will be emitted with QHostAddress::Any as the allowed source address to
+ * signal that it doesn't matter where we connect from, but more importantly, that anybody else on
+ * the same host could have, and can, connect to the tube. The tube can be closed at this point if
+ * this would be unacceptable security-wise. To totally prevent the tube from being accepted in the
+ * first place, one can close it already when tubeOffered() is emitted for it - support for the
+ * needed security mechanism can be queried using its supportsIPv4SocketsWithSpecifiedAddress()
+ * accessor.
+ *
+ * The handler is registered on the bus at the latest when this method or setToAcceptAsUnix() is
+ * called for the first time, so one should check the return value of isRegistered() at that point
+ * to verify that was successful.
+ *
+ * \param generator A pointer to the source address generator to use, or 0 to allow all
+ * connections from the local host.
+ *
+ * \todo Make it possible to set the tube client to auto-close tubes if the desired access control
+ * level is not achieved, as an alternative to the current best-effort behavior.
+ */
+void StreamTubeClient::setToAcceptAsTcp(TcpSourceAddressGenerator *generator)
+{
+ mPriv->tcpGenerator = generator;
+ mPriv->acceptsAsTcp = true;
+ mPriv->acceptsAsUnix = false;
+
+ mPriv->ensureRegistered();
+}
+
+/**
+ * Set the client to accept tubes received to handle in the future in a fashion which will yield a
+ * Unix socket as the local endpoint to connect to.
+ *
+ * If that doesn't cause problems for the payload protocol, it's possible to increase security by
+ * restricting the processes allowed to connect to the local endpoint socket to those from the same
+ * user ID as the protocol backend is running as by setting \a requireCredentials to \c true. This
+ * requires transmitting a single byte, signaled as the \a credentialByte parameter to the
+ * tubeAcceptedAsUnix() signal, in a \c SCM_CREDS or SCM_CREDENTIALS message, whichever is supported
+ * by the platform, as the first thing after having connected to the socket. Even if the platform
+ * doesn't implement either concept, the byte must still be sent.
+ *
+ * However, not all protocol backends support the credential passing based access control on all the
+ * platforms they can run on. If a tube is offered through such a backend, tubeAcceptedAsUnix() will
+ * be emitted with \a requiresCredentials set to \c false, to signal that a credential byte should
+ * NOT be sent for that tube, and that any local process can or could have connected to the tube
+ * already. The tube can be closed at this point if this would be unacceptable security-wise. To
+ * totally prevent the tube from being accepted in the first place, one can close it already when
+ * tubeOffered() is emitted for it - support for the needed security mechanism can be queried using
+ * its supportsIPv4SocketsWithSpecifiedAddress()
+ * accessor.
+ *
+ * The handler is registered on the bus at the latest when this method or setToAcceptAsTcp() is
+ * called for the first time, so one should check the return value of isRegistered() at that point
+ * to verify that was successful.
+ *
+ * \param requireCredentials \c true to try and restrict connecting by UID, \c false to allow all
+ * connections.
+ *
+ * \todo Make it possible to set the tube client to auto-close tubes if the desired access control
+ * level is not achieved, as an alternative to the current best-effort behavior.
+ */
+void StreamTubeClient::setToAcceptAsUnix(bool requireCredentials)
+{
+ mPriv->tcpGenerator = 0;
+ mPriv->acceptsAsTcp = false;
+ mPriv->acceptsAsUnix = true;
+ mPriv->requireCredentials = requireCredentials;
+
+ mPriv->ensureRegistered();
+}
+
+/**
+ * Return the tubes currently handled by the client.
+ *
+ * \return A list of Tube structures containing pointers to the account and tube channel for each
+ * tube.
+ */
+QList<StreamTubeClient::Tube> StreamTubeClient::tubes() const
+{
+ QList<Tube> tubes;
+
+ foreach (TubeWrapper *wrapper, mPriv->tubes.values()) {
+ tubes.push_back(Tube(wrapper->mAcc, wrapper->mTube));
+ }
+
+ return tubes;
+}
+
+/**
+ * Return the ongoing connections established through tubes signaled by this client.
+ *
+ * The returned mapping has for each Tube a structure containing pointers to the account and tube
+ * channel objects as keys, with the integer identifiers for the current connections on them as the
+ * values. The IDs are unique amongst the connections active on a single tube at any given time, but
+ * not globally.
+ *
+ * This is effectively a state recovery accessor corresponding to the change notification signals
+ * newConnection() and connectionClosed().
+ *
+ * The mapping is only populated if connection monitoring was requested when creating the client (so
+ * monitorsConnections() returns \c true).
+ *
+ * \return The connections in a mapping with Tube structures containing pointers to the account and
+ * channel objects for each tube as keys, and the sets of numerical IDs as values.
+ */
+QHash<StreamTubeClient::Tube, QSet<uint> > StreamTubeClient::connections() const
+{
+ QHash<Tube, QSet<uint> > conns;
+ if (!monitorsConnections()) {
+ warning() << "StreamTubeClient::connections() used, but connection monitoring is disabled";
+ return conns;
+ }
+
+ foreach (const Tube &tube, tubes()) {
+ if (!tube.channel()->isValid()) {
+ // The tube has been invalidated, so skip it to avoid warnings
+ // We're going to get rid of the wrapper in the next mainloop iteration when we get the
+ // invalidation signal
+ continue;
+ }
+
+ QSet<uint> tubeConns = QSet<uint>::fromList(tube.channel()->connections());
+ if (!tubeConns.empty()) {
+ conns.insert(tube, tubeConns);
+ }
+ }
+
+ return conns;
+}
+
+void StreamTubeClient::onInvokedForTube(
+ const AccountPtr &acc,
+ const StreamTubeChannelPtr &tube,
+ const QDateTime &time,
+ const ChannelRequestHints &hints)
+{
+ Q_ASSERT(isRegistered()); // our SSTH shouldn't be receiving any channels unless it's registered
+ Q_ASSERT(!tube->isRequested());
+ Q_ASSERT(tube->isValid()); // SSTH won't emit invalid tubes
+
+ if (mPriv->tubes.contains(tube)) {
+ debug() << "Ignoring StreamTubeClient reinvocation for tube" << tube->objectPath();
+ return;
+ }
+
+ IncomingStreamTubeChannelPtr incoming = IncomingStreamTubeChannelPtr::qObjectCast(tube);
+
+ if (!incoming) {
+ warning() << "The ChannelFactory used by StreamTubeClient must construct" <<
+ "IncomingStreamTubeChannel subclasses for Requested=false StreamTubes";
+ tube->requestClose();
+ return;
+ }
+
+ TubeWrapper *wrapper = 0;
+
+ if (mPriv->acceptsAsTcp) {
+ QPair<QHostAddress, quint16> srcAddr =
+ qMakePair(QHostAddress(QHostAddress::Any), quint16(0));
+
+ if (mPriv->tcpGenerator) {
+ srcAddr = mPriv->tcpGenerator->nextSourceAddress(acc, incoming);
+ }
+
+ wrapper = new TubeWrapper(acc, incoming, srcAddr.first, srcAddr.second, this);
+ } else {
+ Q_ASSERT(mPriv->acceptsAsUnix); // we should only be registered when we're set to accept as either TCP or Unix
+ wrapper = new TubeWrapper(acc, incoming, mPriv->requireCredentials, this);
+ }
+
+ connect(wrapper,
+ SIGNAL(acceptFinished(TubeWrapper*,Tp::PendingStreamTubeConnection*)),
+ SLOT(onAcceptFinished(TubeWrapper*,Tp::PendingStreamTubeConnection*)));
+ connect(tube.data(),
+ SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
+ SLOT(onTubeInvalidated(Tp::DBusProxy*,QString,QString)));
+
+ if (monitorsConnections()) {
+ connect(wrapper,
+ SIGNAL(newConnection(TubeWrapper*,uint)),
+ SLOT(onNewConnection(TubeWrapper*,uint)));
+ connect(wrapper,
+ SIGNAL(connectionClosed(TubeWrapper*,uint,QString,QString)),
+ SLOT(onConnectionClosed(TubeWrapper*,uint,QString,QString)));
+ }
+
+ mPriv->tubes.insert(tube, wrapper);
+
+ emit tubeOffered(acc, incoming);
+}
+
+void StreamTubeClient::onAcceptFinished(TubeWrapper *wrapper, PendingStreamTubeConnection *conn)
+{
+ Q_ASSERT(wrapper != NULL);
+ Q_ASSERT(conn != NULL);
+
+ if (!mPriv->tubes.contains(wrapper->mTube)) {
+ debug() << "StreamTubeClient ignoring Accept result for invalidated tube"
+ << wrapper->mTube->objectPath();
+ return;
+ }
+
+ if (conn->isError()) {
+ warning() << "StreamTubeClient couldn't accept tube" << wrapper->mTube->objectPath() << '-'
+ << conn->errorName() << ':' << conn->errorMessage();
+
+ if (wrapper->mTube->isValid()) {
+ wrapper->mTube->requestClose();
+ }
+
+ wrapper->mTube->disconnect(this);
+ emit tubeClosed(wrapper->mAcc, wrapper->mTube, conn->errorName(), conn->errorMessage());
+ mPriv->tubes.remove(wrapper->mTube);
+ wrapper->deleteLater();
+ return;
+ }
+
+ debug() << "StreamTubeClient accepted tube" << wrapper->mTube->objectPath();
+
+ if (conn->addressType() == SocketAddressTypeIPv4
+ || conn->addressType() == SocketAddressTypeIPv6) {
+ QPair<QHostAddress, quint16> addr = conn->ipAddress();
+ emit tubeAcceptedAsTcp(addr.first, addr.second, wrapper->mSourceAddress,
+ wrapper->mSourcePort, wrapper->mAcc, wrapper->mTube);
+ } else {
+ emit tubeAcceptedAsUnix(conn->localAddress(), conn->requiresCredentials(),
+ conn->credentialByte(), wrapper->mAcc, wrapper->mTube);
+ }
+}
+
+void StreamTubeClient::onTubeInvalidated(Tp::DBusProxy *proxy, const QString &error,
+ const QString &message)
+{
+ StreamTubeChannelPtr tube(qobject_cast<StreamTubeChannel *>(proxy));
+ Q_ASSERT(!tube.isNull());
+
+ TubeWrapper *wrapper = mPriv->tubes.value(tube);
+ if (!wrapper) {
+ // Accept finish with error already removed it
+ return;
+ }
+
+ debug() << "Client StreamTube" << tube->objectPath() << "invalidated - " << error << ':'
+ << message;
+
+ emit tubeClosed(wrapper->mAcc, wrapper->mTube, error, message);
+ mPriv->tubes.remove(tube);
+ delete wrapper;
+}
+
+void StreamTubeClient::onNewConnection(
+ TubeWrapper *wrapper,
+ uint conn)
+{
+ Q_ASSERT(monitorsConnections());
+ emit newConnection(wrapper->mAcc, wrapper->mTube, conn);
+}
+
+void StreamTubeClient::onConnectionClosed(
+ TubeWrapper *wrapper,
+ uint conn,
+ const QString &error,
+ const QString &message)
+{
+ Q_ASSERT(monitorsConnections());
+ emit connectionClosed(wrapper->mAcc, wrapper->mTube, conn, error, message);
+}
+
+/**
+ * \fn void StreamTubeClient::tubeOffered(const AccountPtr &account, const
+ * IncomingStreamTubeChannelPtr &tube)
+ *
+ * Emitted when one of the services we're interested in connecting to has been offered by us as a
+ * tube, which we've began handling.
+ *
+ * This is emitted before invoking the TcpSourceAddressGenerator, if any, for the tube.
+ *
+ * \param account A pointer to the account through which the tube was offered.
+ * \param tube A pointer to the actual tube channel.
+ */
+
+/**
+ * \fn void StreamTubeClient::tubeAcceptedAsTcp(const QHostAddress &listenAddress, quint16
+ * listenPort, const QHostAddress &sourceAddress, quint16 sourcePort, const AccountPtr &account,
+ * const IncomingStreamTubeChannelPtr &tube)
+ *
+ * Emitted when a tube offered to us (previously announced with tubeOffered()) has been successfully
+ * accepted and a TCP socket established as the local endpoint, as specified by setToAcceptAsTcp().
+ *
+ * The allowed source address and port are signaled here if there was a TcpSourceAddressGenerator set
+ * at the time of accepting this tube, it yielded a non-zero address, and the protocol backend
+ * supports source address based access control. This enables the application to use the correct one
+ * of the sockets it currently has bound to the generated addresses.
+ *
+ * \param listenAddress The listen address of the local endpoint socket.
+ * \param listenPort The listen port of the local endpoint socket.
+ * \param sourceAddress The host address allowed to connect to the tube, or QHostAddress::Any if
+ * source address based access control is not in use.
+ * \param sourcePort The port from which connections are allowed to the tube, or 0 if source address
+ * based access control is not in use.
+ * \param account A pointer to the account object through which the tube was offered.
+ * \param tube A pointer to the actual tube channel object.
+ */
+
+// Explicitly specifying Tp:: for this signal's argument types works around a doxygen bug causing it
+// to confuse the doc here being for StreamTubeServer::tubeClosed :/
+/**
+ * \fn void StreamTubeClient::tubeClosed(const Tp::AccountPtr &account, const
+ * Tp::IncomingStreamTubeChannelPtr &tube, const QString &error, const QString &message)
+ *
+ * Emitted when a tube we've been handling (previously announced with tubeOffered()) has
+ * encountered an error or has otherwise been closed from further communication.
+ *
+ * \param account A pointer to the account through which the tube was offered.
+ * \param tube A pointer to the actual tube channel.
+ * \param error The D-Bus error name corresponding to the reason for the closure.
+ * \param message A freeform debug message associated with the error.
+ */
+
+/**
+ * \fn void StreamTubeClient::tubeAcceptedAsUnix(const QHostAddress &listenAddress, quint16
+ * listenPort, const QHostAddress &sourceAddress, quint16 sourcePort, const AccountPtr &account,
+ * const IncomingStreamTubeChannelPtr &tube)
+ *
+ * Emitted when a tube offered to us (previously announced with tubeOffered()) has been successfully
+ * accepted and a Unix socket established as the local endpoint, as specified by setToAcceptAsUnix().
+ *
+ * The credential byte which should be sent after connecting to the tube (in a SCM_CREDENTIALS or
+ * SCM_CREDS message if supported by the platform) will be signaled here if the client was set to
+ * attempt requiring credentials at the time of accepting this tube, and the protocol backend
+ * supports credential passing based access control. Otherwise, \a requiresCredentials will be false
+ * and no byte or associated out-of-band credentials metadata should be sent.
+ *
+ * \param listenAddress The listen address of the local endpoint socket.
+ * \param requiresCredentials \c true if \a credentialByte should be sent after connecting to the
+ * socket, \c false if not.
+ * \param credentialByte The byte to send if \a requiresCredentials is \c true.
+ * \param account A pointer to the account object through which the tube was offered.
+ * \param tube A pointer to the actual tube channel object.
+ */
+
+/**
+ * \fn void StreamTubeClient::newConnection(const AccountPtr &account, const
+ * IncomingStreamTubeChannelPtr &tube, uint connectionId)
+ *
+ * Emitted when a new connection has been made to the local endpoint socket for \a tube.
+ *
+ * This can be used to later associate connection errors reported by connectionClosed() with the
+ * corresponding application sockets. However, establishing the association generally requires
+ * connecting only one socket at a time, waiting for newConnection() to be emitted, and only then
+ * proceeding, as there is no identification for the connections unlike the incoming connections in
+ * StreamTubeServer.
+ *
+ * Note that the connection IDs are only unique within a given tube, so identification of the tube
+ * channel must also be recorded together with the ID to establish global uniqueness. Even then, the
+ * a connection ID can be reused after the previous connection identified by it having been
+ * signaled as closed with connectionClosed().
+ *
+ * This is only emitted if connection monitoring was enabled when creating the StreamTubeClient.
+ *
+ * \param account A pointer to the account through which the tube was offered.
+ * \param tube A pointer to the tube channel through which the connection has been made.
+ * \param connectionId The integer ID of the new connection.
+ */
+
+/**
+ * \fn void StreamTubeClient::connectionClosed(const AccountPtr &account, const
+ * IncomingStreamTubeChannelPtr &tube, uint connectionId, const QString &error, const
+ * QString &message)
+ *
+ * Emitted when a connection (previously announced with newConnection()) through one of our
+ * handled tubes has been closed due to an error or by a graceful disconnect (in which case the
+ * error is ::TP_QT_ERROR_CANCELLED).
+ *
+ * This is only emitted if connection monitoring was enabled when creating the StreamTubeClient.
+ *
+ * \param account A pointer to the account through which the tube was offered.
+ * \param tube A pointer to the tube channel through which the connection had been made.
+ * \param connectionId The integer ID of the connection closed.
+ * \param error The D-Bus error name corresponding to the reason for the closure.
+ * \param message A freeform debug message associated with the error.
+ */
+
+} // Tp
diff --git a/TelepathyQt/stream-tube-client.h b/TelepathyQt/stream-tube-client.h
new file mode 100644
index 00000000..3d3f32ac
--- /dev/null
+++ b/TelepathyQt/stream-tube-client.h
@@ -0,0 +1,217 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_stream_tube_client_h_HEADER_GUARD_
+#define _TelepathyQt_stream_tube_client_h_HEADER_GUARD_
+
+#include <TelepathyQt/AccountFactory>
+#include <TelepathyQt/ChannelFactory>
+#include <TelepathyQt/ConnectionFactory>
+#include <TelepathyQt/ContactFactory>
+#include <TelepathyQt/RefCounted>
+#include <TelepathyQt/Types>
+
+class QHostAddress;
+
+namespace Tp
+{
+
+class PendingStreamTubeConnection;
+
+class TP_QT_EXPORT StreamTubeClient : public QObject, public RefCounted
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(StreamTubeClient)
+
+ class TubeWrapper;
+
+public:
+
+ class TcpSourceAddressGenerator
+ {
+ public:
+ virtual QPair<QHostAddress, quint16>
+ nextSourceAddress(const AccountPtr &account, const IncomingStreamTubeChannelPtr &tube) = 0;
+
+ protected:
+ virtual ~TcpSourceAddressGenerator() {}
+ };
+
+ class Tube : public QPair<AccountPtr, IncomingStreamTubeChannelPtr>
+ {
+ public:
+ Tube();
+ Tube(const AccountPtr &account, const IncomingStreamTubeChannelPtr &channel);
+ Tube(const Tube &other);
+ ~Tube();
+
+ bool isValid() const { return mPriv.constData() != 0; }
+
+ Tube &operator=(const Tube &other);
+
+ const AccountPtr &account() const
+ {
+ return first;
+ }
+
+ const IncomingStreamTubeChannelPtr &channel() const
+ {
+ return second;
+ }
+
+ private:
+ struct Private;
+ friend struct Private;
+ QSharedDataPointer<Private> mPriv;
+ };
+
+ static StreamTubeClientPtr create(
+ const QStringList &p2pServices,
+ const QStringList &roomServices = QStringList(),
+ const QString &clientName = QString(),
+ bool monitorConnections = false,
+ bool bypassApproval = false,
+ const AccountFactoryConstPtr &accountFactory =
+ AccountFactory::create(QDBusConnection::sessionBus()),
+ const ConnectionFactoryConstPtr &connectionFactory =
+ ConnectionFactory::create(QDBusConnection::sessionBus()),
+ const ChannelFactoryConstPtr &channelFactory =
+ ChannelFactory::create(QDBusConnection::sessionBus()),
+ const ContactFactoryConstPtr &contactFactory =
+ ContactFactory::create());
+
+ static StreamTubeClientPtr create(
+ const QDBusConnection &bus,
+ const AccountFactoryConstPtr &accountFactory,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory,
+ const QStringList &p2pServices,
+ const QStringList &roomServices = QStringList(),
+ const QString &clientName = QString(),
+ bool monitorConnections = false,
+ bool bypassApproval = false);
+
+ static StreamTubeClientPtr create(
+ const AccountManagerPtr &accountManager,
+ const QStringList &p2pServices,
+ const QStringList &roomServices = QStringList(),
+ const QString &clientName = QString(),
+ bool monitorConnections = false,
+ bool bypassApproval = false);
+
+ static StreamTubeClientPtr create(
+ const ClientRegistrarPtr &registrar,
+ const QStringList &p2pServices,
+ const QStringList &roomServices = QStringList(),
+ const QString &clientName = QString(),
+ bool monitorConnections = false,
+ bool bypassApproval = false);
+
+ virtual ~StreamTubeClient();
+
+ ClientRegistrarPtr registrar() const;
+ QString clientName() const;
+ bool isRegistered() const;
+ bool monitorsConnections() const;
+
+ bool acceptsAsTcp() const;
+ TcpSourceAddressGenerator *tcpGenerator() const;
+ bool acceptsAsUnix() const;
+
+ void setToAcceptAsTcp(TcpSourceAddressGenerator *generator = 0);
+ void setToAcceptAsUnix(bool requireCredentials = false);
+
+ QList<Tube> tubes() const;
+ QHash<Tube, QSet<uint> > connections() const;
+
+Q_SIGNALS:
+ void tubeOffered(
+ const Tp::AccountPtr &account,
+ const Tp::IncomingStreamTubeChannelPtr &tube);
+ void tubeClosed(
+ const Tp::AccountPtr &account,
+ const Tp::IncomingStreamTubeChannelPtr &tube,
+ const QString &error,
+ const QString &message);
+
+ void tubeAcceptedAsTcp(
+ const QHostAddress &listenAddress,
+ quint16 listenPort,
+ const QHostAddress &sourceAddress,
+ quint16 sourcePort,
+ const Tp::AccountPtr &account,
+ const Tp::IncomingStreamTubeChannelPtr &tube);
+ void tubeAcceptedAsUnix(
+ const QString &listenAddress,
+ bool requiresCredentials,
+ uchar credentialByte,
+ const Tp::AccountPtr &account,
+ const Tp::IncomingStreamTubeChannelPtr &tube);
+
+ void newConnection(
+ const Tp::AccountPtr &account,
+ const Tp::IncomingStreamTubeChannelPtr &tube,
+ uint connectionId);
+ void connectionClosed(
+ const Tp::AccountPtr &account,
+ const Tp::IncomingStreamTubeChannelPtr &tube,
+ uint connectionId,
+ const QString &error,
+ const QString &message);
+
+private Q_SLOTS:
+
+ TP_QT_NO_EXPORT void onInvokedForTube(
+ const Tp::AccountPtr &account,
+ const Tp::StreamTubeChannelPtr &tube,
+ const QDateTime &userActionTime,
+ const Tp::ChannelRequestHints &requestHints);
+
+ TP_QT_NO_EXPORT void onAcceptFinished(TubeWrapper *, Tp::PendingStreamTubeConnection *);
+ TP_QT_NO_EXPORT void onTubeInvalidated(Tp::DBusProxy *, const QString &, const QString &);
+
+ TP_QT_NO_EXPORT void onNewConnection(
+ TubeWrapper *wrapper,
+ uint conn);
+ TP_QT_NO_EXPORT void onConnectionClosed(
+ TubeWrapper *wrapper,
+ uint conn,
+ const QString &error,
+ const QString &message);
+
+private:
+ TP_QT_NO_EXPORT StreamTubeClient(
+ const ClientRegistrarPtr &registrar,
+ const QStringList &p2pServices,
+ const QStringList &roomServices,
+ const QString &clientName,
+ bool monitorConnections,
+ bool bypassApproval);
+
+ struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/stream-tube-server-internal.h b/TelepathyQt/stream-tube-server-internal.h
new file mode 100644
index 00000000..224f4b1e
--- /dev/null
+++ b/TelepathyQt/stream-tube-server-internal.h
@@ -0,0 +1,56 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/StreamTubeServer>
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+class TP_QT_NO_EXPORT StreamTubeServer::TubeWrapper :
+ public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(TubeWrapper)
+
+public:
+ TubeWrapper(const AccountPtr &acc, const OutgoingStreamTubeChannelPtr &tube,
+ const QHostAddress &exportedAddr, quint16 exportedPort, const QVariantMap &params,
+ StreamTubeServer *parent);
+ ~TubeWrapper() { }
+
+ AccountPtr mAcc;
+ OutgoingStreamTubeChannelPtr mTube;
+
+Q_SIGNALS:
+ void offerFinished(TubeWrapper *wrapper, Tp::PendingOperation *op);
+ void newConnection(TubeWrapper *wrapper, uint conn);
+ void connectionClosed(TubeWrapper *wrapper, uint conn, const QString &error,
+ const QString &message);
+
+private Q_SLOTS:
+ void onTubeOffered(Tp::PendingOperation *);
+ void onNewConnection(uint);
+ void onConnectionClosed(uint, const QString &, const QString &);
+};
+
+} // Tp
diff --git a/TelepathyQt/stream-tube-server.cpp b/TelepathyQt/stream-tube-server.cpp
new file mode 100644
index 00000000..56e0afe2
--- /dev/null
+++ b/TelepathyQt/stream-tube-server.cpp
@@ -0,0 +1,1134 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/StreamTubeServer>
+#include "TelepathyQt/stream-tube-server-internal.h"
+
+#include "TelepathyQt/_gen/stream-tube-server.moc.hpp"
+#include "TelepathyQt/_gen/stream-tube-server-internal.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+#include "TelepathyQt/simple-stream-tube-handler.h"
+
+#include <QScopedPointer>
+#include <QSharedData>
+#include <QTcpServer>
+
+#include <TelepathyQt/AccountManager>
+#include <TelepathyQt/ClientRegistrar>
+#include <TelepathyQt/OutgoingStreamTubeChannel>
+#include <TelepathyQt/StreamTubeChannel>
+
+namespace Tp
+{
+
+/**
+ * \class StreamTubeServer::ParametersGenerator
+ * \ingroup serverclient
+ * \headerfile TelepathyQt/stream-tube-server.h <TelepathyQt/StreamTubeServer>
+ *
+ * \brief The StreamTubeServer::ParametersGenerator abstract interface allows sending a different
+ * set of parameters with each tube offer.
+ *
+ * %Tube parameters are arbitrary data sent with the tube offer, which can be retrieved in the
+ * receiving end with IncomingStreamTubeChannel::parameters(). They can be used to transfer
+ * e.g. session identification information, authentication credentials or alike, for bootstrapping
+ * the protocol used for communicating over the tube.
+ *
+ * For usecases where the parameters don't need to change between each tube, just passing a fixed
+ * set of parameters to a suitable StreamTubeServer::exportTcpSocket() overload is usually more
+ * convenient than implementing a ParametersGenerator. Note that StreamTubeServer::exportTcpSocket()
+ * can be called multiple times to change the parameters for future tubes when e.g. configuration
+ * settings have been changed, so a ParametersGenerator only needs to be implemented if each and
+ * every tube must have a different set of parameters.
+ */
+
+/**
+ * \fn QVariantMap StreamTubeServer::ParametersGenerator::nextParameters(const AccountPtr &, const
+ * OutgoingStreamTubeChannelPtr &, const ChannelRequestHints &)
+ *
+ * Return the parameters to send when offering the given \a tube.
+ *
+ * \param account The account from which the tube originates.
+ * \param tube The tube channel which is going to be offered by the StreamTubeServer.
+ * \param hints The hints associated with the request that led to the creation of this tube, if any.
+ *
+ * \return Parameters to send with the offer, or an empty QVariantMap if none are needed for this
+ * tube.
+ */
+
+/**
+ * \fn StreamTubeServer::ParametersGenerator::~ParametersGenerator
+ *
+ * Class destructor. Protected, because StreamTubeServer never deletes a ParametersGenerator passed
+ * to it.
+ */
+
+class TP_QT_NO_EXPORT FixedParametersGenerator : public StreamTubeServer::ParametersGenerator
+{
+public:
+
+ FixedParametersGenerator(const QVariantMap &params) : mParams(params) {}
+
+ QVariantMap nextParameters(const AccountPtr &, const OutgoingStreamTubeChannelPtr &,
+ const ChannelRequestHints &)
+ {
+ return mParams;
+ }
+
+private:
+
+ QVariantMap mParams;
+};
+
+struct TP_QT_NO_EXPORT StreamTubeServer::RemoteContact::Private : public QSharedData
+{
+ // empty placeholder for now
+};
+
+/**
+ * \class StreamTubeServer::RemoteContact
+ * \ingroup serverclient
+ * \headerfile TelepathyQt/stream-tube-server.h <TelepathyQt/StreamTubeServer>
+ *
+ * \brief The StreamTubeServer::RemoteContact class represents a contact from which a socket
+ * connection to our exported socket originates.
+ */
+
+/**
+ * Constructs a new invalid RemoteContact instance.
+ */
+StreamTubeServer::RemoteContact::RemoteContact()
+{
+ // invalid instance
+}
+
+/**
+ * Constructs a new RemoteContact for the given \a contact object from the given \a account.
+ *
+ * \param account A pointer to the account which this contact can be reached through.
+ * \param contact A pointer to the contact object.
+ */
+StreamTubeServer::RemoteContact::RemoteContact(
+ const AccountPtr &account,
+ const ContactPtr &contact)
+ : QPair<AccountPtr, ContactPtr>(account, contact), mPriv(new Private)
+{
+}
+
+/**
+ * Copy constructor.
+ */
+StreamTubeServer::RemoteContact::RemoteContact(
+ const RemoteContact &other)
+ : QPair<AccountPtr, ContactPtr>(other.account(), other.contact()), mPriv(other.mPriv)
+{
+}
+
+/**
+ * Class destructor.
+ */
+StreamTubeServer::RemoteContact::~RemoteContact()
+{
+ // mPriv deleted automatically
+}
+
+/**
+ * Assignment operator.
+ */
+StreamTubeServer::RemoteContact &StreamTubeServer::RemoteContact::operator=(
+ const RemoteContact &other)
+{
+ if (&other == this) {
+ return *this;
+ }
+
+ first = other.account();
+ second = other.contact();
+ mPriv = other.mPriv;
+
+ return *this;
+}
+
+/**
+ * \fn bool StreamTubeServer::RemoteContact::isValid() const
+ *
+ * Return whether or not the contact is valid or is just the null object created using the default
+ * constructor.
+ *
+ * \return \c true if valid, \c false otherwise.
+ */
+
+/**
+ * \fn AccountPtr StreamTubeServer::RemoteContact::account() const
+ *
+ * Return the account through which the contact can be reached.
+ *
+ * \return A pointer to the account object.
+ */
+
+/**
+ * \fn ContactPtr StreamTubeServer::RemoteContact::contact() const
+ *
+ * Return the actual contact object.
+ *
+ * \return A pointer to the object.
+ */
+
+struct TP_QT_NO_EXPORT StreamTubeServer::Tube::Private : public QSharedData
+{
+ // empty placeholder for now
+};
+
+/**
+ * \class StreamTubeServer::Tube
+ * \ingroup serverclient
+ * \headerfile TelepathyQt/stream-tube-server.h <TelepathyQt/StreamTubeServer>
+ *
+ * \brief The StreamTubeServer::Tube class represents a tube being handled by the server.
+ */
+
+/**
+ * Constructs a new invalid Tube instance.
+ */
+StreamTubeServer::Tube::Tube()
+{
+ // invalid instance
+}
+
+/**
+ * Constructs a Tube instance for the given tube \a channel originating from the given \a account.
+ *
+ * \param account A pointer to the account object.
+ * \param channel A pointer to the tube channel object.
+ */
+StreamTubeServer::Tube::Tube(
+ const AccountPtr &account,
+ const OutgoingStreamTubeChannelPtr &channel)
+ : QPair<AccountPtr, OutgoingStreamTubeChannelPtr>(account, channel), mPriv(new Private)
+{
+}
+
+/**
+ * Copy constructor.
+ */
+StreamTubeServer::Tube::Tube(
+ const Tube &other)
+ : QPair<AccountPtr, OutgoingStreamTubeChannelPtr>(other.account(), other.channel()),
+ mPriv(other.mPriv)
+{
+}
+
+/**
+ * Class destructor.
+ */
+StreamTubeServer::Tube::~Tube()
+{
+ // mPriv deleted automatically
+}
+
+/**
+ * Assignment operator.
+ */
+StreamTubeServer::Tube &StreamTubeServer::Tube::operator=(
+ const Tube &other)
+{
+ if (&other == this) {
+ return *this;
+ }
+
+ first = other.account();
+ second = other.channel();
+ mPriv = other.mPriv;
+
+ return *this;
+}
+
+/**
+ * \fn bool StreamTubeServer::Tube::isValid() const
+ *
+ * Return whether or not the tube is valid or is just the null object created using the default
+ * constructor.
+ *
+ * \return \c true if valid, \c false otherwise.
+ */
+
+/**
+ * \fn AccountPtr StreamTubeServer::Tube::account() const
+ *
+ * Return the account from which the tube originates.
+ *
+ * \return A pointer to the account object.
+ */
+
+/**
+ * \fn OutgoingStreamTubeChannelPtr StreamTubeServer::Tube::channel() const
+ *
+ * Return the actual tube channel.
+ *
+ * \return A pointer to the channel.
+ */
+
+struct StreamTubeServer::Private
+{
+ Private(const ClientRegistrarPtr &registrar,
+ const QStringList &p2pServices,
+ const QStringList &roomServices,
+ const QString &maybeClientName,
+ bool monitorConnections)
+ : registrar(registrar),
+ handler(SimpleStreamTubeHandler::create(p2pServices, roomServices, true, monitorConnections)),
+ clientName(maybeClientName),
+ isRegistered(false),
+ exportedPort(0),
+ generator(0)
+ {
+ if (clientName.isEmpty()) {
+ clientName = QString::fromLatin1("TpQt4STubeServer_%1_%2")
+ .arg(registrar->dbusConnection().baseService()
+ .replace(QLatin1Char(':'), QLatin1Char('_'))
+ .replace(QLatin1Char('.'), QLatin1Char('_')))
+ .arg((intptr_t) this, 0, 16);
+ }
+ }
+
+ void ensureRegistered()
+ {
+ if (isRegistered) {
+ return;
+ }
+
+ debug() << "Register StreamTubeServer with name " << clientName;
+
+ if (registrar->registerClient(handler, clientName)) {
+ isRegistered = true;
+ } else {
+ warning() << "StreamTubeServer" << clientName
+ << "registration failed";
+ }
+ }
+
+ ClientRegistrarPtr registrar;
+ SharedPtr<SimpleStreamTubeHandler> handler;
+ QString clientName;
+ bool isRegistered;
+
+ QHostAddress exportedAddr;
+ quint16 exportedPort;
+ ParametersGenerator *generator;
+ QScopedPointer<FixedParametersGenerator> fixedGenerator;
+
+ QHash<StreamTubeChannelPtr, TubeWrapper *> tubes;
+
+};
+
+StreamTubeServer::TubeWrapper::TubeWrapper(const AccountPtr &acc,
+ const OutgoingStreamTubeChannelPtr &tube, const QHostAddress &exportedAddr,
+ quint16 exportedPort, const QVariantMap &params, StreamTubeServer *parent)
+ : QObject(parent), mAcc(acc), mTube(tube)
+{
+ connect(tube->offerTcpSocket(exportedAddr, exportedPort, params),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onTubeOffered(Tp::PendingOperation*)));
+ connect(tube.data(),
+ SIGNAL(newConnection(uint)),
+ SLOT(onNewConnection(uint)));
+ connect(tube.data(),
+ SIGNAL(connectionClosed(uint,QString,QString)),
+ SLOT(onConnectionClosed(uint,QString,QString)));
+}
+
+void StreamTubeServer::TubeWrapper::onTubeOffered(Tp::PendingOperation *op)
+{
+ emit offerFinished(this, op);
+}
+
+void StreamTubeServer::TubeWrapper::onNewConnection(uint conn)
+{
+ emit newConnection(this, conn);
+}
+
+void StreamTubeServer::TubeWrapper::onConnectionClosed(uint conn, const QString &error,
+ const QString &message)
+{
+ emit connectionClosed(this, conn, error, message);
+}
+
+/**
+ * \class StreamTubeServer
+ * \ingroup serverclient
+ * \headerfile TelepathyQt/stream-tube-server.h <TelepathyQt/StreamTubeServer>
+ *
+ * \brief The StreamTubeServer class is a Handler implementation for outgoing %Stream %Tube channels,
+ * allowing an application to easily export a TCP network server over Telepathy Tubes without
+ * worrying about the channel dispatching details.
+ *
+ * Telepathy Tubes is a technology for connecting arbitrary applications together through the IM
+ * network (and sometimes with direct peer-to-peer connections), such that issues like firewall/NAT
+ * traversal are automatically handled. Stream Tubes in particular offer properties similar to
+ * SOCK_STREAM sockets. The StreamTubeServer class exports such a bytestream socket \b server over
+ * the tubes it \em handles as a Telepathy Handler %Client; the StreamTubeClient class is the
+ * counterpart, enabling TCP/UNIX socket clients to connect to services from such exported servers
+ * offered to them via tubes.
+ *
+ * Both peer-to-peer (\c TargetHandleType == \ref HandleTypeContact) and group (\c TargetHandleType
+ * == \ref HandleTypeRoom) channels are supported, and it's possible to specify the tube services to
+ * handle for each separately. It is also possible to not advertise handling capability for ANY tube
+ * service; instead just using the StreamTubeServer to handle tubes on an one-off basis by passing
+ * its corresponding %Client service name as the \a preferredHandler when requesting tubes via the
+ * Account::createStreamTube() methods (or equivalent).
+ *
+ * %Connection monitoring allows associating incoming connections on the exported server socket with
+ * the corresponding remote contacts. This allows an application to show the details of and/or
+ * initiate further communication with the remote contacts, without considering the actual tube
+ * channels the connections are being made through at all (in particular, their
+ * Channel::targetContact() accessor for peer-to-peer and the
+ * OutgoingStreamTubeChannel::connectionsForSourceAddresses() accessor for group tubes).
+ *
+ * Enabling connection monitoring adds a small overhead and latency to handling each incoming tube
+ * and signaling each new incoming connection over them, though, so use it only when needed.
+ * Additionally, some protocol backends or environments they're running in might not support the
+ * ::SocketAccessControlPort mechanism, in which case the source address won't be reported for
+ * connections through them. Even in this case, the remote contacts can be associated by accepting
+ * one incoming socket connection at a time, and waiting for the corresponding contact to be
+ * signaled (although its source address will be invalid, it's the only possibility given its the
+ * only accepted connection). However, it's not necessary to do this e.g. with the Gabble XMPP
+ * backend, because it fully supports the required mechanism.
+ *
+ * A service activated Handler can be implemented using StreamTubeServer by passing a predefined \a
+ * clientName manually to the chosen create() method, and installing Telepathy \c .client and D-Bus
+ * \c .service files declaring the implemented tube services as channel classes and a path to the
+ * executable. If this is not needed, the \a clientName can be omitted, in which case a random
+ * unique client name is generated and used instead.
+ *
+ * StreamTubeServer shares Account, Connection and Channel proxies and Contact objects with the
+ * rest of the application as long as a reference to the AccountManager, ClientRegistrar, or the
+ * factories used elsewhere is passed to the create() method. A stand-alone tube service Handler can
+ * get away without passing these however, or just passing select factories to make the desired
+ * features prepared and subclasses employed for these objects for their own convenience.
+ *
+ * Whichever method is used, the ChannelFactory (perhaps indirectly) given must construct
+ * OutgoingStreamTubeChannel instances or subclasses thereof for all channel classes corresponding
+ * to the tube services to handle. This is the default; overriding it without obeying these
+ * constraints using ChannelFactory::setSubclassForOutgoingStreamTubes() or the related methods
+ * for room tubes prevents StreamTubeServer from operating correctly.
+ *
+ * \todo Coin up a small Python script or alike to easily generate the .client and .service files.
+ * (fd.o #41614)
+ * \todo Support exporting Unix sockets as well. (fd.o #41615)
+ */
+
+/**
+ * Create a new StreamTubeServer, which will register itself on the session bus using an internal
+ * ClientRegistrar and use the given factories.
+ *
+ * \param p2pServices Names of the tube services to handle on peer-to-peer tube channels.
+ * \param roomServices Names of the tube services to handle on room/group tube channels.
+ * \param clientName The client name (without the \c org.freedesktop.Telepathy.Client. prefix).
+ * \param monitorConnections Whether to enable connection monitoring or not.
+ * \param accountFactory The account factory to use.
+ * \param connectionFactory The connection factory to use.
+ * \param channelFactory The channel factory to use.
+ * \param contactFactory The contact factory to use.
+ */
+StreamTubeServerPtr StreamTubeServer::create(
+ const QStringList &p2pServices,
+ const QStringList &roomServices,
+ const QString &clientName,
+ bool monitorConnections,
+ const AccountFactoryConstPtr &accountFactory,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory)
+{
+ return create(
+ QDBusConnection::sessionBus(),
+ accountFactory,
+ connectionFactory,
+ channelFactory,
+ contactFactory,
+ p2pServices,
+ roomServices,
+ clientName,
+ monitorConnections);
+}
+
+/**
+ * Create a new StreamTubeServer, which will register itself on the given \a bus using an internal
+ * ClientRegistrar and use the given factories.
+ *
+ * The factories must all be created for the given \a bus.
+ *
+ * \param bus Connection to the bus to register on.
+ * \param accountFactory The account factory to use.
+ * \param connectionFactory The connection factory to use.
+ * \param channelFactory The channel factory to use.
+ * \param contactFactory The contact factory to use.
+ * \param p2pServices Names of the tube services to handle on peer-to-peer tube channels.
+ * \param roomServices Names of the tube services to handle on room/group tube channels.
+ * \param clientName The client name (without the \c org.freedesktop.Telepathy.Client. prefix).
+ * \param monitorConnections Whether to enable connection monitoring or not.
+ */
+StreamTubeServerPtr StreamTubeServer::create(
+ const QDBusConnection &bus,
+ const AccountFactoryConstPtr &accountFactory,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory,
+ const QStringList &p2pServices,
+ const QStringList &roomServices,
+ const QString &clientName,
+ bool monitorConnections)
+{
+ return create(
+ ClientRegistrar::create(
+ bus,
+ accountFactory,
+ connectionFactory,
+ channelFactory,
+ contactFactory),
+ p2pServices,
+ roomServices,
+ clientName,
+ monitorConnections);
+}
+
+/**
+ * Create a new StreamTubeServer, which will register itself on the bus of and share objects with
+ * the given \a accountManager, creating an internal ClientRegistrar.
+ *
+ * \param accountManager A pointer to the account manager to link up with.
+ * \param p2pServices Names of the tube services to handle on peer-to-peer tube channels.
+ * \param roomServices Names of the tube services to handle on room/group tube channels.
+ * \param clientName The client name (without the \c org.freedesktop.Telepathy.Client. prefix).
+ * \param monitorConnections Whether to enable connection monitoring or not.
+ */
+StreamTubeServerPtr StreamTubeServer::create(
+ const AccountManagerPtr &accountManager,
+ const QStringList &p2pServices,
+ const QStringList &roomServices,
+ const QString &clientName,
+ bool monitorConnections)
+{
+ return create(
+ accountManager->dbusConnection(),
+ accountManager->accountFactory(),
+ accountManager->connectionFactory(),
+ accountManager->channelFactory(),
+ accountManager->contactFactory(),
+ p2pServices,
+ roomServices,
+ clientName,
+ monitorConnections);
+}
+
+/**
+ * Create a new StreamTubeServer, which will register itself on the bus of and using the given
+ * client \a registrar, and share objects with it.
+ *
+ * \param registrar The client registrar to use.
+ * \param p2pServices Names of the tube services to handle on peer-to-peer tube channels.
+ * \param roomServices Names of the tube services to handle on room/group tube channels.
+ * \param clientName The client name (without the \c org.freedesktop.Telepathy.Client. prefix).
+ * \param monitorConnections Whether to enable connection monitoring or not.
+ */
+StreamTubeServerPtr StreamTubeServer::create(
+ const ClientRegistrarPtr &registrar,
+ const QStringList &p2pServices,
+ const QStringList &roomServices,
+ const QString &clientName,
+ bool monitorConnections)
+{
+ return StreamTubeServerPtr(
+ new StreamTubeServer(registrar, p2pServices, roomServices, clientName,
+ monitorConnections));
+}
+
+StreamTubeServer::StreamTubeServer(
+ const ClientRegistrarPtr &registrar,
+ const QStringList &p2pServices,
+ const QStringList &roomServices,
+ const QString &clientName,
+ bool monitorConnections)
+ : mPriv(new Private(registrar, p2pServices, roomServices, clientName, monitorConnections))
+{
+ connect(mPriv->handler.data(),
+ SIGNAL(invokedForTube(
+ Tp::AccountPtr,
+ Tp::StreamTubeChannelPtr,
+ QDateTime,
+ Tp::ChannelRequestHints)),
+ SLOT(onInvokedForTube(
+ Tp::AccountPtr,
+ Tp::StreamTubeChannelPtr,
+ QDateTime,
+ Tp::ChannelRequestHints)));
+}
+
+/**
+ * Class destructor.
+ */
+StreamTubeServer::~StreamTubeServer()
+{
+ if (isRegistered()) {
+ mPriv->registrar->unregisterClient(mPriv->handler);
+ }
+
+ delete mPriv;
+}
+
+/**
+ * Return the client registrar used by the server to register itself as a Handler client.
+ *
+ * This is the registrar originally passed to
+ * create(const ClientRegistrarPtr &, const QStringList &, const QStringList &, const QString &, bool)
+ * if that was used, and an internally constructed one otherwise. In any case, it can be used to
+ * e.g. register further clients like any other ClientRegistrar.
+ *
+ * \return A pointer to the registrar.
+ */
+ClientRegistrarPtr StreamTubeServer::registrar() const
+{
+ return mPriv->registrar;
+}
+
+/**
+ * Return the Telepathy %Client name of the server.
+ *
+ * \return The name, without the \c org.freedesktop.Telepathy.Client. prefix of the full D-Bus service name.
+ */
+QString StreamTubeServer::clientName() const
+{
+ return mPriv->clientName;
+}
+
+/**
+ * Return whether the server has been successfully registered or not.
+ *
+ * Registration is attempted, at the latest, when a socket is first exported using exportTcpSocket().
+ * It can fail e.g. because the connection to the bus has failed, or a predefined \a clientName has
+ * been passed to create(), and a %Client with the same name is already registered. Typically, failure
+ * registering would be a fatal error for a stand-alone tube handler, but only a warning event for
+ * an application serving other purposes. In any case, a high-quality user of the API will check the
+ * return value of this accessor after exporting their socket.
+ *
+ * \return \c true if the server has been successfully registered, \c false if not.
+ */
+bool StreamTubeServer::isRegistered() const
+{
+ return mPriv->isRegistered;
+}
+
+/**
+ * Return whether connection monitoring is enabled on this server.
+ *
+ * For technical reasons, connection monitoring can't be enabled when the server is already running,
+ * so there is no corresponding setter method. It has to be enabled by passing \c true as the \a
+ * monitorConnections parameter to the create() method.
+ *
+ * If connection monitoring isn't enabled, newTcpConnection() and tcpConnectionClosed() won't be
+ * emitted and tcpConnections() won't be populated.
+ *
+ * \return \c true if monitoring is enabled, \c false if not.
+ */
+bool StreamTubeServer::monitorsConnections() const
+{
+ return mPriv->handler->monitorsConnections();
+}
+
+/**
+ * Return the host address and port of the currently exported TCP socket, if any.
+ *
+ * QHostAddress::Null is reported as the address and 0 as the port if no TCP socket has yet been
+ * successfully exported.
+ *
+ * \return The host address and port values in a pair structure.
+ */
+QPair<QHostAddress, quint16> StreamTubeServer::exportedTcpSocketAddress() const
+{
+ return qMakePair(mPriv->exportedAddr, mPriv->exportedPort);
+}
+
+/**
+ * Return the fixed parameters, if any, which are sent along when offering the exported socket on
+ * all handled tubes.
+ *
+ * To prevent accidentally leaving the current parameters to be sent when offering a different
+ * socket, or vice versa, the parameters can only be set together with the socket using
+ * exportTcpSocket(). Parameters often contain sensitive information such as session identifiers or
+ * authentication credentials, which could then be used to maliciously access the service listening
+ * on the other socket.
+ *
+ * If a custom dynamic ParametersGenerator was passed to exportTcpSocket() instead of a set of fixed
+ * parameters, an empty set of parameters is returned.
+ *
+ * \return The parameters in a string-variant map.
+ */
+QVariantMap StreamTubeServer::exportedParameters() const
+{
+ if (!mPriv->generator) {
+ return QVariantMap();
+ }
+
+ FixedParametersGenerator *generator =
+ dynamic_cast<FixedParametersGenerator *>(mPriv->generator);
+
+ if (generator) {
+ return generator->nextParameters(AccountPtr(), OutgoingStreamTubeChannelPtr(),
+ ChannelRequestHints());
+ } else {
+ return QVariantMap();
+ }
+}
+
+/**
+ * Set the server to offer the socket listening at the given (\a address, \a port) combination as the
+ * local endpoint of tubes handled in the future.
+ *
+ * A fixed set of protocol bootstrapping \a parameters can optionally be set to be sent along with all
+ * tube offers until the next call to exportTcpSocket(). See the ParametersGenerator documentation
+ * for an in-depth description of the parameter transfer mechanism, and a more flexible way to vary
+ * the parameters between each handled tube.
+ *
+ * The handler is registered on the bus at the latest when this method or another exportTcpSocket()
+ * overload is called for the first time, so one should check the return value of isRegistered() at
+ * that point to verify that was successful.
+ *
+ * \param address The listen address of the socket.
+ * \param port The port of the socket.
+ * \param parameters The bootstrapping parameters in a string-value map.
+ */
+void StreamTubeServer::exportTcpSocket(
+ const QHostAddress &address,
+ quint16 port,
+ const QVariantMap &parameters)
+{
+ if (address.isNull() || port == 0) {
+ warning() << "Attempted to export null TCP socket address or zero port, ignoring";
+ return;
+ }
+
+ mPriv->exportedAddr = address;
+ mPriv->exportedPort = port;
+
+ mPriv->generator = 0;
+ if (!parameters.isEmpty()) {
+ mPriv->fixedGenerator.reset(new FixedParametersGenerator(parameters));
+ mPriv->generator = mPriv->fixedGenerator.data();
+ }
+
+ mPriv->ensureRegistered();
+}
+
+/**
+ * Set the StreamTubeServer to offer the already listening TCP \a server as the local endpoint of tubes
+ * handled in the future.
+ *
+ * This is just a convenience wrapper around
+ * exportTcpSocket(const QHostAddress &, quint16, const QVariantMap &) to be used when the TCP
+ * server code is implemented using the QtNetwork facilities.
+ *
+ * A fixed set of protocol bootstrapping \a parameters can optionally be set to be sent along with all
+ * tube offers until the next call to exportTcpSocket(). See the ParametersGenerator documentation
+ * for an in-depth description of the parameter transfer mechanism, and a more flexible way to vary
+ * the parameters between each handled tube.
+ *
+ * \param server A pointer to the TCP server.
+ * \param parameters The bootstrapping parameters in a string-value map.
+ */
+void StreamTubeServer::exportTcpSocket(
+ const QTcpServer *server,
+ const QVariantMap &parameters)
+{
+ if (!server->isListening()) {
+ warning() << "Attempted to export non-listening QTcpServer, ignoring";
+ return;
+ }
+
+ if (server->serverAddress() == QHostAddress::Any) {
+ return exportTcpSocket(QHostAddress::LocalHost, server->serverPort(), parameters);
+ } else if (server->serverAddress() == QHostAddress::AnyIPv6) {
+ return exportTcpSocket(QHostAddress::LocalHostIPv6, server->serverPort(), parameters);
+ } else {
+ return exportTcpSocket(server->serverAddress(), server->serverPort(), parameters);
+ }
+}
+
+/**
+ * Set the server to offer the socket listening at the given \a address - \a port combination as the
+ * local endpoint of tubes handled in the future, sending the parameters from the given \a generator
+ * along with the offers.
+ *
+ * The handler is registered on the bus at the latest when this method or another exportTcpSocket()
+ * overload is called for the first time, so one should check the return value of isRegistered() at
+ * that point to verify that was successful.
+ *
+ * \param address The listen address of the socket.
+ * \param port The port of the socket.
+ * \param generator A pointer to the bootstrapping parameters generator.
+ */
+void StreamTubeServer::exportTcpSocket(
+ const QHostAddress &address,
+ quint16 port,
+ ParametersGenerator *generator)
+{
+ if (address.isNull() || port == 0) {
+ warning() << "Attempted to export null TCP socket address or zero port, ignoring";
+ return;
+ }
+
+ mPriv->exportedAddr = address;
+ mPriv->exportedPort = port;
+ mPriv->generator = generator;
+
+ mPriv->ensureRegistered();
+}
+
+/**
+ * Set the server to offer the already listening TCP \a server as the local endpoint of tubes
+ * handled in the future, sending the parameters from the given \a generator along with the offers.
+ *
+ * This is just a convenience wrapper around
+ * exportTcpSocket(const QHostAddress &, quint16, ParametersGenerator *) to be used when the TCP
+ * server code is implemented using the QtNetwork facilities.
+ *
+ * \param server A pointer to the TCP server.
+ * \param generator A pointer to the bootstrapping parameters generator.
+ */
+void StreamTubeServer::exportTcpSocket(
+ const QTcpServer *server,
+ ParametersGenerator *generator)
+{
+ if (!server->isListening()) {
+ warning() << "Attempted to export non-listening QTcpServer, ignoring";
+ return;
+ }
+
+ if (server->serverAddress() == QHostAddress::Any) {
+ return exportTcpSocket(QHostAddress::LocalHost, server->serverPort(), generator);
+ } else if (server->serverAddress() == QHostAddress::AnyIPv6) {
+ return exportTcpSocket(QHostAddress::LocalHostIPv6, server->serverPort(), generator);
+ } else {
+ return exportTcpSocket(server->serverAddress(), server->serverPort(), generator);
+ }
+}
+
+/**
+ * Return the tubes currently handled by the server.
+ *
+ * \return A list of Tube structures containing pointers to the account and tube channel for each
+ * tube.
+ */
+QList<StreamTubeServer::Tube> StreamTubeServer::tubes() const
+{
+ QList<Tube> tubes;
+
+ foreach (TubeWrapper *wrapper, mPriv->tubes.values()) {
+ tubes.push_back(Tube(wrapper->mAcc, wrapper->mTube));
+ }
+
+ return tubes;
+}
+
+/**
+ * Return the ongoing TCP connections over tubes handled by this server.
+ *
+ * The returned mapping has the connection source addresses as keys and the contacts along with the
+ * accounts which can be used to reach them as values. Connections through protocol backends which
+ * don't support SocketAccessControlPort will be included as the potentially many values for the
+ * null source address key, the pair (\c QHostAddress::Null, 0).
+ *
+ * This is effectively a state recovery accessor corresponding to the change notification signals
+ * newTcpConnection() and tcpConnectionClosed().
+ *
+ * The mapping is only populated if connection monitoring was requested when creating the server (so
+ * monitorsConnections() returns \c true).
+ *
+ * \return The connections in a mapping with pairs of their source host addresses and ports as keys
+ * and structures containing pointers to the account and remote contacts they're from as values.
+ */
+QHash<QPair<QHostAddress, quint16>,
+ StreamTubeServer::RemoteContact>
+ StreamTubeServer::tcpConnections() const
+{
+ QHash<QPair<QHostAddress /* sourceAddress */, quint16 /* sourcePort */>, RemoteContact> conns;
+ if (!monitorsConnections()) {
+ warning() << "StreamTubeServer::tcpConnections() used, but connection monitoring is disabled";
+ return conns;
+ }
+
+ foreach (const Tube &tube, tubes()) {
+ // Ignore invalid and non-Open tubes to prevent a few useless warnings in corner cases where
+ // a tube is still being opened, or has been invalidated but we haven't processed that event
+ // yet.
+ if (!tube.channel()->isValid() || tube.channel()->state() != TubeChannelStateOpen) {
+ continue;
+ }
+
+ if (tube.channel()->addressType() != SocketAddressTypeIPv4 &&
+ tube.channel()->addressType() != SocketAddressTypeIPv6) {
+ continue;
+ }
+
+ QHash<QPair<QHostAddress,quint16>, uint> srcAddrConns =
+ tube.channel()->connectionsForSourceAddresses();
+ QHash<uint, ContactPtr> connContacts =
+ tube.channel()->contactsForConnections();
+
+ QPair<QHostAddress, quint16> srcAddr;
+ foreach (srcAddr, srcAddrConns.keys()) {
+ ContactPtr contact = connContacts.take(srcAddrConns.value(srcAddr));
+ conns.insert(srcAddr, RemoteContact(tube.account(), contact));
+ }
+
+ // The remaining values in our copy of connContacts are those which didn't have a
+ // corresponding source address, probably because the service doesn't properly implement
+ // Port AC
+ foreach (const ContactPtr &contact, connContacts.values()) {
+ // Insert them with an invalid source address as the key
+ conns.insertMulti(qMakePair(QHostAddress(QHostAddress::Null), quint16(0)),
+ RemoteContact(tube.account(), contact));
+ }
+ }
+
+ return conns;
+}
+
+void StreamTubeServer::onInvokedForTube(
+ const AccountPtr &acc,
+ const StreamTubeChannelPtr &tube,
+ const QDateTime &time,
+ const ChannelRequestHints &hints)
+{
+ Q_ASSERT(isRegistered()); // our SSTH shouldn't be receiving any channels unless it's registered
+ Q_ASSERT(tube->isRequested());
+ Q_ASSERT(tube->isValid()); // SSTH won't emit invalid tubes
+
+ OutgoingStreamTubeChannelPtr outgoing = OutgoingStreamTubeChannelPtr::qObjectCast(tube);
+
+ if (outgoing) {
+ emit tubeRequested(acc, outgoing, time, hints);
+ } else {
+ warning() << "The ChannelFactory used by StreamTubeServer must construct" <<
+ "OutgoingStreamTubeChannel subclasses for Requested=true StreamTubes";
+ tube->requestClose();
+ return;
+ }
+
+ if (!mPriv->tubes.contains(tube)) {
+ debug().nospace() << "Offering socket " << mPriv->exportedAddr << ":" << mPriv->exportedPort
+ << " on tube " << tube->objectPath();
+
+ QVariantMap params;
+ if (mPriv->generator) {
+ params = mPriv->generator->nextParameters(acc, outgoing, hints);
+ }
+
+ Q_ASSERT(!mPriv->exportedAddr.isNull() && mPriv->exportedPort != 0);
+
+ TubeWrapper *wrapper =
+ new TubeWrapper(acc, outgoing, mPriv->exportedAddr, mPriv->exportedPort, params, this);
+
+ connect(wrapper,
+ SIGNAL(offerFinished(TubeWrapper*,Tp::PendingOperation*)),
+ SLOT(onOfferFinished(TubeWrapper*,Tp::PendingOperation*)));
+ connect(tube.data(),
+ SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
+ SLOT(onTubeInvalidated(Tp::DBusProxy*,QString,QString)));
+
+ if (monitorsConnections()) {
+ connect(wrapper,
+ SIGNAL(newConnection(TubeWrapper*,uint)),
+ SLOT(onNewConnection(TubeWrapper*,uint)));
+ connect(wrapper,
+ SIGNAL(connectionClosed(TubeWrapper*,uint,QString,QString)),
+ SLOT(onConnectionClosed(TubeWrapper*,uint,QString,QString)));
+ }
+
+ mPriv->tubes.insert(outgoing, wrapper);
+ }
+}
+
+void StreamTubeServer::onOfferFinished(
+ TubeWrapper *wrapper,
+ Tp::PendingOperation *op)
+{
+ OutgoingStreamTubeChannelPtr tube = wrapper->mTube;
+
+ if (op->isError()) {
+ warning() << "Offer() failed, closing tube" << tube->objectPath() << '-' <<
+ op->errorName() << ':' << op->errorMessage();
+
+ if (wrapper->mTube->isValid()) {
+ wrapper->mTube->requestClose();
+ }
+
+ wrapper->mTube->disconnect(this);
+ emit tubeClosed(wrapper->mAcc, wrapper->mTube, op->errorName(), op->errorMessage());
+ mPriv->tubes.remove(wrapper->mTube);
+ wrapper->deleteLater();
+ } else {
+ debug() << "Tube" << tube->objectPath() << "offered successfully";
+ }
+}
+
+void StreamTubeServer::onTubeInvalidated(
+ Tp::DBusProxy *proxy,
+ const QString &error,
+ const QString &message)
+{
+ OutgoingStreamTubeChannelPtr tube(qobject_cast<OutgoingStreamTubeChannel *>(proxy));
+ Q_ASSERT(!tube.isNull());
+
+ TubeWrapper *wrapper = mPriv->tubes.value(tube);
+ if (!wrapper) {
+ // Offer finish with error already removed it
+ return;
+ }
+
+ debug() << "Tube" << tube->objectPath() << "invalidated with" << error << ':' << message;
+
+ emit tubeClosed(wrapper->mAcc, wrapper->mTube, error, message);
+ mPriv->tubes.remove(tube);
+ delete wrapper;
+}
+
+void StreamTubeServer::onNewConnection(
+ TubeWrapper *wrapper,
+ uint conn)
+{
+ Q_ASSERT(monitorsConnections());
+
+ if (wrapper->mTube->addressType() == SocketAddressTypeIPv4
+ || wrapper->mTube->addressType() == SocketAddressTypeIPv6) {
+ QHash<QPair<QHostAddress,quint16>, uint> srcAddrConns =
+ wrapper->mTube->connectionsForSourceAddresses();
+ QHash<uint, Tp::ContactPtr> connContacts =
+ wrapper->mTube->contactsForConnections();
+
+ QPair<QHostAddress, quint16> srcAddr = srcAddrConns.key(conn);
+ emit newTcpConnection(srcAddr.first, srcAddr.second, wrapper->mAcc,
+ connContacts.value(conn), wrapper->mTube);
+ } else {
+ // No UNIX socket should ever have been offered yet
+ Q_ASSERT(false);
+ }
+}
+
+void StreamTubeServer::onConnectionClosed(
+ TubeWrapper *wrapper,
+ uint conn,
+ const QString &error,
+ const QString &message)
+{
+ Q_ASSERT(monitorsConnections());
+
+ if (wrapper->mTube->addressType() == SocketAddressTypeIPv4
+ || wrapper->mTube->addressType() == SocketAddressTypeIPv6) {
+ QHash<QPair<QHostAddress,quint16>, uint> srcAddrConns =
+ wrapper->mTube->connectionsForSourceAddresses();
+ QHash<uint, Tp::ContactPtr> connContacts =
+ wrapper->mTube->contactsForConnections();
+
+ QPair<QHostAddress, quint16> srcAddr = srcAddrConns.key(conn);
+ emit tcpConnectionClosed(srcAddr.first, srcAddr.second, wrapper->mAcc,
+ connContacts.value(conn), error, message, wrapper->mTube);
+ } else {
+ // No UNIX socket should ever have been offered yet
+ Q_ASSERT(false);
+ }
+}
+
+/**
+ * \fn void StreamTubeServer::tubeRequested(const AccountPtr &account, const
+ * OutgoingStreamTubeChannelPtr &tube, const QDateTime &userActionTime, const ChannelRequestHints
+ * &hints)
+ *
+ * Emitted when a tube has been requested for one of our services, and we've began handling it.
+ *
+ * This is emitted before invoking the ParametersGenerator, if any, for the tube.
+ *
+ * \param account A pointer to the account from which the tube was requested from.
+ * \param tube A pointer to the actual tube channel.
+ * \param userActionTime The time the request occurred at, if it was an user action. Should be used
+ * for focus stealing prevention.
+ * \param hints The hints passed to the request, if any.
+ */
+
+/**
+ * \fn void StreamTubeServer::tubeClosed(const AccountPtr &account, const
+ * OutgoingStreamTubeChannelPtr &tube, const QString &error, const QString &message)
+ *
+ * Emitted when a tube we've been handling (previously announced with tubeRequested()) has
+ * encountered an error or has otherwise been closed from further communication.
+ *
+ * \param account A pointer to the account from which the tube was requested from.
+ * \param tube A pointer to the actual tube channel.
+ * \param error The D-Bus error name corresponding to the reason for the closure.
+ * \param message A freeform debug message associated with the error.
+ */
+
+/**
+ * \fn void StreamTubeServer::newTcpConnection(const QHostAddress &sourceAddress, quint16
+ * sourcePort, const AccountPtr &account, const ContactPtr &contact, const
+ * OutgoingStreamTubeChannelPtr &tube)
+ *
+ * Emitted when we have picked up a new TCP connection to the (current or previous) exported server
+ * socket. This can be used to associate connections the protocol backend relays to the exported
+ * socket with the remote contact who originally initiated them in the other end of the tube.
+ *
+ * This is only emitted if connection monitoring was enabled when creating the StreamTubeServer.
+ * Additionally, if the protocol backend the connection is from doesn't support the
+ * ::SocketAccessControlPort mechanism, the source address and port will always be invalid.
+ *
+ * \param sourceAddress The source address of the connection, or QHostAddress::Null if it can't be
+ * resolved.
+ * \param sourcePort The source port of the connection, or 0 if it can't be resolved.
+ * \param account A pointer to the account through which the remote contact can be reached.
+ * \param contact A pointer to the remote contact object.
+ * \param tube A pointer to the tube channel through which the connection has been made.
+ */
+
+/**
+ * \fn void StreamTubeServer::tcpConnectionClosed(const QHostAddress &sourceAddress, quint16
+ * sourcePort, const AccountPtr &account, const ContactPtr &contact, conts QString &error, const
+ * QString &message, const OutgoingStreamTubeChannelPtr &tube)
+ *
+ * Emitted when a TCP connection (previously announced with newTcpConnection()) through one of our
+ * handled tubes has been closed due to an error or by a graceful disconnect (in which case the
+ * error is ::TP_QT_ERROR_DISCONNECTED).
+ *
+ * This is only emitted if connection monitoring was enabled when creating the StreamTubeServer.
+ * Additionally, if the protocol backend the connection is from doesn't support the
+ * ::SocketAccessControlPort mechanism, the source address and port will always be invalid.
+ *
+ * \param sourceAddress The source address of the connection, or QHostAddress::Null if it couldn't
+ * be resolved.
+ * \param sourcePort The source port of the connection, or 0 if it couldn't be resolved.
+ * \param account A pointer to the account through which the remote contact can be reached.
+ * \param contact A pointer to the remote contact object.
+ * \param error The D-Bus error name corresponding to the reason for the closure.
+ * \param message A freeform debug message associated with the error.
+ * \param tube A pointer to the tube channel through which the connection has been made.
+ */
+
+} // Tp
diff --git a/TelepathyQt/stream-tube-server.h b/TelepathyQt/stream-tube-server.h
new file mode 100644
index 00000000..670b4a5f
--- /dev/null
+++ b/TelepathyQt/stream-tube-server.h
@@ -0,0 +1,253 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2011 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_stream_tube_server_h_HEADER_GUARD_
+#define _TelepathyQt_stream_tube_server_h_HEADER_GUARD_
+
+#include <QPair>
+#include <QSharedDataPointer>
+
+#include <TelepathyQt/AccountFactory>
+#include <TelepathyQt/ChannelFactory>
+#include <TelepathyQt/ConnectionFactory>
+#include <TelepathyQt/ContactFactory>
+#include <TelepathyQt/RefCounted>
+#include <TelepathyQt/Types>
+
+class QHostAddress;
+class QTcpServer;
+
+namespace Tp
+{
+
+class TP_QT_EXPORT StreamTubeServer : public QObject, public RefCounted
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(StreamTubeServer)
+
+ class TubeWrapper;
+
+public:
+
+ class ParametersGenerator
+ {
+ public:
+ virtual QVariantMap
+ nextParameters(const AccountPtr &account, const OutgoingStreamTubeChannelPtr &tube,
+ const ChannelRequestHints &hints) = 0;
+
+ protected:
+ virtual ~ParametersGenerator() {}
+ };
+
+ class RemoteContact : public QPair<AccountPtr, ContactPtr>
+ {
+ public:
+ RemoteContact();
+ RemoteContact(const AccountPtr &account, const ContactPtr &contact);
+ RemoteContact(const RemoteContact &other);
+ ~RemoteContact();
+
+ bool isValid() const { return mPriv.constData() != 0; }
+
+ RemoteContact &operator=(const RemoteContact &other);
+
+ const AccountPtr &account() const
+ {
+ return first;
+ }
+
+ const ContactPtr &contact() const
+ {
+ return second;
+ }
+
+ private:
+ struct Private;
+ friend struct Private;
+ QSharedDataPointer<Private> mPriv;
+ };
+
+ class Tube : public QPair<AccountPtr, OutgoingStreamTubeChannelPtr>
+ {
+ public:
+ Tube();
+ Tube(const AccountPtr &account, const OutgoingStreamTubeChannelPtr &channel);
+ Tube(const Tube &other);
+ ~Tube();
+
+ bool isValid() const { return mPriv.constData() != 0; }
+
+ Tube &operator=(const Tube &other);
+
+ const AccountPtr &account() const
+ {
+ return first;
+ }
+
+ const OutgoingStreamTubeChannelPtr &channel() const
+ {
+ return second;
+ }
+
+ private:
+ struct Private;
+ friend struct Private;
+ QSharedDataPointer<Private> mPriv;
+ };
+
+ static StreamTubeServerPtr create(
+ const QStringList &p2pServices,
+ const QStringList &roomServices = QStringList(),
+ const QString &clientName = QString(),
+ bool monitorConnections = false,
+ const AccountFactoryConstPtr &accountFactory =
+ AccountFactory::create(QDBusConnection::sessionBus()),
+ const ConnectionFactoryConstPtr &connectionFactory =
+ ConnectionFactory::create(QDBusConnection::sessionBus()),
+ const ChannelFactoryConstPtr &channelFactory =
+ ChannelFactory::create(QDBusConnection::sessionBus()),
+ const ContactFactoryConstPtr &contactFactory =
+ ContactFactory::create());
+
+ static StreamTubeServerPtr create(
+ const QDBusConnection &bus,
+ const AccountFactoryConstPtr &accountFactory,
+ const ConnectionFactoryConstPtr &connectionFactory,
+ const ChannelFactoryConstPtr &channelFactory,
+ const ContactFactoryConstPtr &contactFactory,
+ const QStringList &p2pServices,
+ const QStringList &roomServices = QStringList(),
+ const QString &clientName = QString(),
+ bool monitorConnections = false);
+
+ static StreamTubeServerPtr create(
+ const AccountManagerPtr &accountManager,
+ const QStringList &p2pServices,
+ const QStringList &roomServices = QStringList(),
+ const QString &clientName = QString(),
+ bool monitorConnections = false);
+
+ static StreamTubeServerPtr create(
+ const ClientRegistrarPtr &registrar,
+ const QStringList &p2pServices,
+ const QStringList &roomServices = QStringList(),
+ const QString &clientName = QString(),
+ bool monitorConnections = false);
+
+ virtual ~StreamTubeServer();
+
+ ClientRegistrarPtr registrar() const;
+ QString clientName() const;
+ bool isRegistered() const;
+ bool monitorsConnections() const;
+
+ QPair<QHostAddress, quint16> exportedTcpSocketAddress() const;
+ QVariantMap exportedParameters() const;
+
+ void exportTcpSocket(
+ const QHostAddress &address,
+ quint16 port,
+ const QVariantMap &parameters = QVariantMap());
+ void exportTcpSocket(
+ const QTcpServer *server,
+ const QVariantMap &parameters = QVariantMap());
+
+ void exportTcpSocket(
+ const QHostAddress &address,
+ quint16 port,
+ ParametersGenerator *generator);
+ void exportTcpSocket(
+ const QTcpServer *server,
+ ParametersGenerator *generator);
+
+ QList<Tube> tubes() const;
+
+ QHash<QPair<QHostAddress, quint16>, RemoteContact> tcpConnections() const;
+
+Q_SIGNALS:
+
+ void tubeRequested(
+ const Tp::AccountPtr &account,
+ const Tp::OutgoingStreamTubeChannelPtr &tube,
+ const QDateTime &userActionTime,
+ const Tp::ChannelRequestHints &hints);
+ void tubeClosed(
+ const Tp::AccountPtr &account,
+ const Tp::OutgoingStreamTubeChannelPtr &tube,
+ const QString &error,
+ const QString &message);
+
+ void newTcpConnection(
+ const QHostAddress &sourceAddress,
+ quint16 sourcePort,
+ const Tp::AccountPtr &account,
+ const Tp::ContactPtr &contact,
+ const Tp::OutgoingStreamTubeChannelPtr &tube);
+ void tcpConnectionClosed(
+ const QHostAddress &sourceAddress,
+ quint16 sourcePort,
+ const Tp::AccountPtr &account,
+ const Tp::ContactPtr &contact,
+ const QString &error,
+ const QString &message,
+ const Tp::OutgoingStreamTubeChannelPtr &tube);
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onInvokedForTube(
+ const Tp::AccountPtr &account,
+ const Tp::StreamTubeChannelPtr &tube,
+ const QDateTime &userActionTime,
+ const Tp::ChannelRequestHints &requestHints);
+
+ TP_QT_NO_EXPORT void onOfferFinished(
+ TubeWrapper *wrapper,
+ Tp::PendingOperation *op);
+ TP_QT_NO_EXPORT void onTubeInvalidated(
+ Tp::DBusProxy *proxy,
+ const QString &error,
+ const QString &message);
+
+ TP_QT_NO_EXPORT void onNewConnection(
+ TubeWrapper *wrapper,
+ uint conn);
+ TP_QT_NO_EXPORT void onConnectionClosed(
+ TubeWrapper *wrapper,
+ uint conn,
+ const QString &error,
+ const QString &message);
+
+private:
+ TP_QT_NO_EXPORT StreamTubeServer(
+ const ClientRegistrarPtr &registrar,
+ const QStringList &p2pServices,
+ const QStringList &roomServices,
+ const QString &clientName,
+ bool monitorConnections);
+
+ struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/streamed-media-channel.cpp b/TelepathyQt/streamed-media-channel.cpp
new file mode 100644
index 00000000..6901719d
--- /dev/null
+++ b/TelepathyQt/streamed-media-channel.cpp
@@ -0,0 +1,1539 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/StreamedMediaChannel>
+
+#include "TelepathyQt/_gen/streamed-media-channel.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/Connection>
+#include <TelepathyQt/ContactManager>
+#include <TelepathyQt/PendingComposite>
+#include <TelepathyQt/PendingContacts>
+#include <TelepathyQt/PendingReady>
+#include <TelepathyQt/PendingVoid>
+
+#include <QHash>
+
+namespace Tp
+{
+
+/* ====== PendingStreamedMediaStreams ====== */
+struct TP_QT_NO_EXPORT PendingStreamedMediaStreams::Private
+{
+ StreamedMediaStreams streams;
+ uint numStreams;
+ uint streamsReady;
+};
+
+/**
+ * \class PendingStreamedMediaStreams
+ * \ingroup clientchannel
+ * \headerfile TelepathyQt/streamed-media-channel.h <TelepathyQt/PendingStreamedMediaStreams>
+ *
+ * \brief Class containing the result of an asynchronous streamed media stream creation
+ * request.
+ *
+ * Instances of this class cannot be constructed directly; the only way to get
+ * one is via StreamedMediaChannel.
+ *
+ * See \ref async_model
+ */
+
+/**
+ * Construct a new PendingStreamedMediaStreams object.
+ *
+ * \param channel StreamedMediaChannel to use.
+ * \param contact The contact who the media stream is with.
+ * \param types A list of stream types to request.
+ */
+PendingStreamedMediaStreams::PendingStreamedMediaStreams(const StreamedMediaChannelPtr &channel,
+ const ContactPtr &contact,
+ const QList<MediaStreamType> &types)
+ : PendingOperation(channel),
+ mPriv(new Private)
+{
+ mPriv->numStreams = types.size();
+ mPriv->streamsReady = 0;
+
+ UIntList l;
+ foreach (MediaStreamType type, types) {
+ l << type;
+ }
+
+ Client::ChannelTypeStreamedMediaInterface *streamedMediaInterface =
+ channel->interface<Client::ChannelTypeStreamedMediaInterface>();
+ QDBusPendingCallWatcher *watcher =
+ new QDBusPendingCallWatcher(
+ streamedMediaInterface->RequestStreams(
+ contact->handle()[0], l), this);
+ connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotStreams(QDBusPendingCallWatcher*)));
+}
+
+/**
+ * Class destructor.
+ */
+PendingStreamedMediaStreams::~PendingStreamedMediaStreams()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the channel through which the request was made.
+ *
+ * \return A pointer to the StreamedMediaChannel object.
+ */
+StreamedMediaChannelPtr PendingStreamedMediaStreams::channel() const
+{
+ return StreamedMediaChannelPtr(qobject_cast<StreamedMediaChannel*>(
+ (StreamedMediaChannel*) _object().data()));
+}
+
+/**
+ * Return a list of the newly created StreamedMediaStreamPtr objects.
+ *
+ * \return A list of pointers to StreamedMediaStream objects, or an empty list if an error occurred.
+ */
+StreamedMediaStreams PendingStreamedMediaStreams::streams() const
+{
+ if (!isFinished()) {
+ warning() << "PendingStreamedMediaStreams::streams called before finished, "
+ "returning empty list";
+ return StreamedMediaStreams();
+ } else if (!isValid()) {
+ warning() << "PendingStreamedMediaStreams::streams called when not valid, "
+ "returning empty list";
+ return StreamedMediaStreams();
+ }
+
+ return mPriv->streams;
+}
+
+void PendingStreamedMediaStreams::gotStreams(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<MediaStreamInfoList> reply = *watcher;
+ if (reply.isError()) {
+ warning().nospace() << "StreamedMedia::RequestStreams()"
+ " failed with " << reply.error().name() << ": " <<
+ reply.error().message();
+ setFinishedWithError(reply.error());
+ watcher->deleteLater();
+ return;
+ }
+
+ debug() << "Got reply to StreamedMedia::RequestStreams()";
+
+ MediaStreamInfoList list = reply.value();
+ foreach (const MediaStreamInfo &streamInfo, list) {
+ StreamedMediaStreamPtr stream = channel()->lookupStreamById(
+ streamInfo.identifier);
+ if (!stream) {
+ stream = channel()->addStream(streamInfo);
+ } else {
+ channel()->onStreamDirectionChanged(streamInfo.identifier,
+ streamInfo.direction, streamInfo.pendingSendFlags);
+ channel()->onStreamStateChanged(streamInfo.identifier,
+ streamInfo.state);
+ }
+ mPriv->streams.append(stream);
+ connect(channel().data(),
+ SIGNAL(streamRemoved(Tp::StreamedMediaStreamPtr)),
+ SLOT(onStreamRemoved(Tp::StreamedMediaStreamPtr)));
+ connect(stream->becomeReady(),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onStreamReady(Tp::PendingOperation*)));
+ }
+
+ watcher->deleteLater();
+}
+
+void PendingStreamedMediaStreams::onStreamRemoved(const StreamedMediaStreamPtr &stream)
+{
+ if (isFinished()) {
+ return;
+ }
+
+ if (mPriv->streams.contains(stream)) {
+ // the stream was removed before becoming ready
+ setFinishedWithError(QLatin1String(TELEPATHY_ERROR_CANCELLED),
+ QLatin1String("Stream removed before ready"));
+ }
+}
+
+void PendingStreamedMediaStreams::onStreamReady(PendingOperation *op)
+{
+ if (isFinished()) {
+ return;
+ }
+
+ if (op->isError()) {
+ setFinishedWithError(op->errorName(), op->errorMessage());
+ return;
+ }
+
+ mPriv->streamsReady++;
+ debug() << "PendingStreamedMediaStreams:";
+ debug() << " Streams count:" << mPriv->numStreams;
+ debug() << " Streams ready:" << mPriv->streamsReady;
+ if (mPriv->streamsReady == mPriv->numStreams) {
+ debug() << "All streams are ready";
+ setFinished();
+ }
+}
+
+/* ====== StreamedMediaStream ====== */
+struct TP_QT_NO_EXPORT StreamedMediaStream::Private
+{
+ Private(StreamedMediaStream *parent, const StreamedMediaChannelPtr &channel,
+ const MediaStreamInfo &info);
+
+ static void introspectContact(Private *self);
+
+ PendingOperation *updateDirection(bool send, bool receive);
+ SendingState localSendingStateFromDirection();
+ SendingState remoteSendingStateFromDirection();
+
+ StreamedMediaStream *parent;
+ QPointer<StreamedMediaChannel> channel;
+ ReadinessHelper *readinessHelper;
+
+ uint id;
+ uint type;
+ uint contactHandle;
+ ContactPtr contact;
+ uint direction;
+ uint pendingSend;
+ uint state;
+};
+
+StreamedMediaStream::Private::Private(StreamedMediaStream *parent,
+ const StreamedMediaChannelPtr &channel,
+ const MediaStreamInfo &streamInfo)
+ : parent(parent),
+ channel(channel.data()),
+ readinessHelper(parent->readinessHelper()),
+ id(streamInfo.identifier),
+ type(streamInfo.type),
+ contactHandle(streamInfo.contact),
+ direction(MediaStreamDirectionNone),
+ pendingSend(0),
+ state(MediaStreamStateDisconnected)
+{
+ ReadinessHelper::Introspectables introspectables;
+
+ ReadinessHelper::Introspectable introspectableCore(
+ QSet<uint>() << 0, // makesSenseForStatuses
+ Features(), // dependsOnFeatures
+ QStringList(), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &Private::introspectContact,
+ this);
+ introspectables[FeatureCore] = introspectableCore;
+
+ readinessHelper->addIntrospectables(introspectables);
+}
+
+void StreamedMediaStream::Private::introspectContact(StreamedMediaStream::Private *self)
+{
+ debug() << "Introspecting stream";
+ if (self->contactHandle == 0) {
+ debug() << "Stream ready";
+ self->readinessHelper->setIntrospectCompleted(FeatureCore, true);
+ return;
+ }
+
+ debug() << "Introspecting stream contact";
+ ContactManagerPtr contactManager =
+ self->parent->channel()->connection()->contactManager();
+ debug() << "contact manager" << contactManager;
+ // TODO: pass id hints to ContactManager if we ever gain support to retrieve contact ids
+ // from MediaStreamInfo or something similar.
+ self->parent->connect(contactManager->contactsForHandles(
+ UIntList() << self->contactHandle),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(gotContact(Tp::PendingOperation*)));
+}
+
+PendingOperation *StreamedMediaStream::Private::updateDirection(
+ bool send, bool receive)
+{
+ uint newDirection = 0;
+
+ if (send) {
+ newDirection |= MediaStreamDirectionSend;
+ }
+
+ if (receive) {
+ newDirection |= MediaStreamDirectionReceive;
+ }
+
+ Client::ChannelTypeStreamedMediaInterface *streamedMediaInterface =
+ parent->channel()->interface<Client::ChannelTypeStreamedMediaInterface>();
+ return new PendingVoid(
+ streamedMediaInterface->RequestStreamDirection(
+ id, newDirection),
+ StreamedMediaStreamPtr(parent));
+}
+
+StreamedMediaStream::SendingState StreamedMediaStream::Private::localSendingStateFromDirection()
+{
+ if (pendingSend & MediaStreamPendingLocalSend) {
+ return SendingStatePendingSend;
+ }
+ if (direction & MediaStreamDirectionSend) {
+ return SendingStateSending;
+ }
+ return SendingStateNone;
+}
+
+StreamedMediaStream::SendingState StreamedMediaStream::Private::remoteSendingStateFromDirection()
+{
+ if (pendingSend & MediaStreamPendingRemoteSend) {
+ return SendingStatePendingSend;
+ }
+ if (direction & MediaStreamDirectionReceive) {
+ return SendingStateSending;
+ }
+ return SendingStateNone;
+}
+
+/**
+ * \class StreamedMediaStream
+ * \ingroup clientchannel
+ * \headerfile TelepathyQt/streamed-media-channel.h <TelepathyQt/StreamedMediaStream>
+ *
+ * \brief The StreamedMediaStream class represents a Telepathy streamed media
+ * stream.
+ *
+ * Instances of this class cannot be constructed directly; the only way to get
+ * one is via StreamedMediaChannel.
+ */
+
+/**
+ * Feature representing the core that needs to become ready to make the
+ * StreamedMediaStream object usable.
+ *
+ * Note that this feature must be enabled in order to use most StreamedMediaStream
+ * methods. See specific methods documentation for more details.
+ *
+ * When calling isReady(), becomeReady(), this feature is implicitly added
+ * to the requested features.
+ */
+const Feature StreamedMediaStream::FeatureCore = Feature(QLatin1String(StreamedMediaStream::staticMetaObject.className()), 0);
+
+/**
+ * Construct a new StreamedMediaStream object.
+ *
+ * \param channel The channel ownding this media stream.
+ * \param streamInfo The stream info of this media stream.
+ */
+StreamedMediaStream::StreamedMediaStream(const StreamedMediaChannelPtr &channel,
+ const MediaStreamInfo &streamInfo)
+ : Object(),
+ ReadyObject(this, FeatureCore),
+ mPriv(new Private(this, channel, streamInfo))
+{
+ gotDirection(streamInfo.direction, streamInfo.pendingSendFlags);
+ gotStreamState(streamInfo.state);
+}
+
+/**
+ * Class destructor.
+ */
+StreamedMediaStream::~StreamedMediaStream()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the channel owning this media stream.
+ *
+ * \return A pointer to the StreamedMediaChannel object.
+ */
+StreamedMediaChannelPtr StreamedMediaStream::channel() const
+{
+ return StreamedMediaChannelPtr(mPriv->channel);
+}
+
+/**
+ * Return the id of this media stream.
+ *
+ * \return An integer representing the media stream id.
+ */
+uint StreamedMediaStream::id() const
+{
+ return mPriv->id;
+}
+
+/**
+ * Return the contact who this media stream is with.
+ *
+ * \return A pointer to the Contact object.
+ */
+ContactPtr StreamedMediaStream::contact() const
+{
+ return mPriv->contact;
+}
+
+/**
+ * Return the state of this media stream.
+ *
+ * \return The state as #MediaStreamState.
+ */
+MediaStreamState StreamedMediaStream::state() const
+{
+ return (MediaStreamState) mPriv->state;
+}
+
+/**
+ * Return the type of this media stream.
+ *
+ * \return The type as #MediaStreamType.
+ */
+MediaStreamType StreamedMediaStream::type() const
+{
+ return (MediaStreamType) mPriv->type;
+}
+
+/**
+ * Return whether media is being sent on this media stream.
+ *
+ * \return \c true if media is being sent, \c false otherwise.
+ * \sa localSendingStateChanged()
+ */
+bool StreamedMediaStream::sending() const
+{
+ return mPriv->direction & MediaStreamDirectionSend;
+}
+
+/**
+ * Return whether media is being received on this media stream.
+ *
+ * \return \c true if media is being received, \c false otherwise.
+ * \sa remoteSendingStateChanged()
+ */
+bool StreamedMediaStream::receiving() const
+{
+ return mPriv->direction & MediaStreamDirectionReceive;
+}
+
+/**
+ * Return whether the local user has been asked to send media by the
+ * remote user on this media stream.
+ *
+ * \return \c true if the local user has been asked to send media by the
+ * remote user, \c false otherwise.
+ * \sa localSendingStateChanged()
+ */
+bool StreamedMediaStream::localSendingRequested() const
+{
+ return mPriv->pendingSend & MediaStreamPendingLocalSend;
+}
+
+/**
+ * Return whether the remote user has been asked to send media by the local
+ * user on this media stream.
+ *
+ * \return \c true if the remote user has been asked to send media by the
+ * local user, \c false otherwise.
+ * \sa remoteSendingStateChanged()
+ */
+bool StreamedMediaStream::remoteSendingRequested() const
+{
+ return mPriv->pendingSend & MediaStreamPendingRemoteSend;
+}
+
+/**
+ * Return the direction of this media stream.
+ *
+ * \return The direction as #MediaStreamDirection.
+ * \sa localSendingState(), remoteSendingState(),
+ * localSendingStateChanged(), remoteSendingStateChanged(),
+ * sending(), receiving()
+ */
+MediaStreamDirection StreamedMediaStream::direction() const
+{
+ return (MediaStreamDirection) mPriv->direction;
+}
+
+/**
+ * Return the pending send flags of this media stream.
+ *
+ * \return The pending send flags as #MediaStreamPendingSend.
+ * \sa localSendingStateChanged()
+ */
+MediaStreamPendingSend StreamedMediaStream::pendingSend() const
+{
+ return (MediaStreamPendingSend) mPriv->pendingSend;
+}
+
+/**
+ * Request a change in the direction of this media stream. In particular, this
+ * might be useful to stop sending media of a particular type, or inform the
+ * peer that you are no longer using media that is being sent to you.
+ *
+ * \param direction The new direction.
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the call has finished.
+ * \sa localSendingStateChanged(), remoteSendingStateChanged()
+ */
+PendingOperation *StreamedMediaStream::requestDirection(
+ MediaStreamDirection direction)
+{
+ StreamedMediaChannelPtr chan(channel());
+ Client::ChannelTypeStreamedMediaInterface *streamedMediaInterface =
+ chan->interface<Client::ChannelTypeStreamedMediaInterface>();
+ return new PendingVoid(
+ streamedMediaInterface->RequestStreamDirection(
+ mPriv->id, direction),
+ StreamedMediaStreamPtr(this));
+}
+
+/**
+ * Start sending a DTMF tone on this media stream.
+ *
+ * Where possible, the tone will continue until stopDTMFTone() is called.
+ * On certain protocols, it may only be possible to send events with a predetermined
+ * length. In this case, the implementation may emit a fixed-length tone,
+ * and the stopDTMFTone() method call should return #TP_QT_ERROR_NOT_AVAILABLE.
+ *
+ * If the channel() does not support the #TP_QT_IFACE_CHANNEL_INTERFACE_DTMF
+ * interface, the resulting PendingOperation will fail with error code
+ * #TP_QT_ERROR_NOT_IMPLEMENTED.
+
+ * \param event A numeric event code from the #DTMFEvent enum.
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the request finishes.
+ * \sa stopDTMFTone()
+ */
+PendingOperation *StreamedMediaStream::startDTMFTone(DTMFEvent event)
+{
+ StreamedMediaChannelPtr chan(channel());
+ if (!chan->interfaces().contains(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_DTMF))) {
+ warning() << "StreamedMediaStream::startDTMFTone() used with no dtmf interface";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("StreamedMediaChannel does not support dtmf interface"),
+ StreamedMediaStreamPtr(this));
+ }
+
+ Client::ChannelInterfaceDTMFInterface *dtmfInterface =
+ chan->interface<Client::ChannelInterfaceDTMFInterface>();
+ return new PendingVoid(
+ dtmfInterface->StartTone(mPriv->id, event),
+ StreamedMediaStreamPtr(this));
+}
+
+/**
+ * Stop sending any DTMF tone which has been started using the startDTMFTone()
+ * method.
+ *
+ * If there is no current tone, the resulting PendingOperation will
+ * finish successfully.
+ *
+ * If continuous tones are not supported by this media stream, the resulting
+ * PendingOperation will fail with error code #TP_QT_ERROR_NOT_AVAILABLE.
+ *
+ * If the channel() does not support the #TP_QT_IFACE_CHANNEL_INTERFACE_DTMF
+ * interface, the resulting PendingOperation will fail with error code
+ * #TP_QT_ERROR_NOT_IMPLEMENTED.
+ *
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the request finishes.
+ * \sa startDTMFTone()
+ */
+PendingOperation *StreamedMediaStream::stopDTMFTone()
+{
+ StreamedMediaChannelPtr chan(channel());
+ if (!chan->interfaces().contains(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_DTMF))) {
+ warning() << "StreamedMediaStream::stopDTMFTone() used with no dtmf interface";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("StreamedMediaChannel does not support dtmf interface"),
+ StreamedMediaStreamPtr(this));
+ }
+
+ Client::ChannelInterfaceDTMFInterface *dtmfInterface =
+ chan->interface<Client::ChannelInterfaceDTMFInterface>();
+ return new PendingVoid(
+ dtmfInterface->StopTone(mPriv->id),
+ StreamedMediaStreamPtr(this));
+}
+
+/**
+ * Request a change in the direction of this media stream.
+ *
+ * In particular, this might be useful to stop sending media of a particular type,
+ * or inform the peer that you are no longer using media that is being sent to you.
+ *
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the call has finished.
+ * \sa requestDirection(Tp::MediaStreamDirection direction),
+ * localSendingStateChanged(), remoteSendingStateChanged()
+ */
+PendingOperation *StreamedMediaStream::requestDirection(bool send, bool receive)
+{
+ uint dir = MediaStreamDirectionNone;
+ if (send) {
+ dir |= MediaStreamDirectionSend;
+ }
+ if (receive) {
+ dir |= MediaStreamDirectionReceive;
+ }
+
+ return requestDirection((MediaStreamDirection) dir);
+}
+
+/**
+ * Return the media stream local sending state.
+ *
+ * \return The local sending state as StreamedMediaStream::SendingState.
+ * \sa localSendingStateChanged()
+ */
+StreamedMediaStream::SendingState StreamedMediaStream::localSendingState() const
+{
+ return mPriv->localSendingStateFromDirection();
+}
+
+/**
+ * Return the media stream remote sending state.
+ *
+ * \return The remote sending state as StreamedMediaStream::SendingState.
+ * \sa remoteSendingStateChanged()
+ */
+StreamedMediaStream::SendingState StreamedMediaStream::remoteSendingState() const
+{
+ return mPriv->remoteSendingStateFromDirection();
+}
+
+/**
+ * Request that media starts or stops being sent on this media stream.
+ *
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the call has finished.
+ * \sa localSendingStateChanged(), requestDirection()
+ */
+PendingOperation *StreamedMediaStream::requestSending(bool send)
+{
+ return mPriv->updateDirection(
+ send,
+ mPriv->direction & MediaStreamDirectionReceive);
+}
+
+/**
+ * Request that the remote contact stops or starts sending on this media stream.
+ *
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the call has finished.
+ * \sa remoteSendingStateChanged(), requestDirection()
+ */
+PendingOperation *StreamedMediaStream::requestReceiving(bool receive)
+{
+ return mPriv->updateDirection(
+ mPriv->direction & MediaStreamDirectionSend,
+ receive);
+}
+
+/**
+ * \fn void StreamedMediaStream::localSendingStateChanged(
+ * Tp::StreamedMediaStream::SendingState localSendingState)
+ *
+ * Emitted when the local sending state of this media stream changes.
+ *
+ * \param localSendingState The new local sending state of this media stream.
+ * \sa localSendingState()
+ */
+
+/**
+ * \fn void MediaStream::remoteSendingStateChanged(
+ * Tp::MediaStream::SendingState &remoteSendingState)
+ *
+ * Emitted when the remote sending state of this media stream changes.
+ *
+ * \param remoteSendingState The new remote sending state of this media stream.
+ * \sa remoteSendingState()
+ */
+
+void StreamedMediaStream::gotContact(PendingOperation *op)
+{
+ PendingContacts *pc = qobject_cast<PendingContacts *>(op);
+ Q_ASSERT(pc->isForHandles());
+
+ if (op->isError()) {
+ warning().nospace() << "Gathering media stream contact failed: "
+ << op->errorName() << ": " << op->errorMessage();
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureCore, false,
+ op->errorName(), op->errorMessage());
+ return;
+ }
+
+ QList<ContactPtr> contacts = pc->contacts();
+ UIntList invalidHandles = pc->invalidHandles();
+ if (contacts.size()) {
+ Q_ASSERT(contacts.size() == 1);
+ Q_ASSERT(invalidHandles.size() == 0);
+ mPriv->contact = contacts.first();
+
+ debug() << "Got stream contact";
+ debug() << "Stream ready";
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureCore, true);
+ } else {
+ Q_ASSERT(invalidHandles.size() == 1);
+ warning().nospace() << "Error retrieving media stream contact (invalid handle)";
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureCore, false,
+ QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT),
+ QLatin1String("Invalid contact handle"));
+ }
+}
+
+void StreamedMediaStream::gotDirection(uint direction, uint pendingSend)
+{
+ if (direction == mPriv->direction &&
+ pendingSend == mPriv->pendingSend) {
+ return;
+ }
+
+ SendingState oldLocalState = mPriv->localSendingStateFromDirection();
+ SendingState oldRemoteState = mPriv->remoteSendingStateFromDirection();
+
+ mPriv->direction = direction;
+ mPriv->pendingSend = pendingSend;
+
+ if (!isReady()) {
+ return;
+ }
+
+ SendingState localSendingState =
+ mPriv->localSendingStateFromDirection();
+ if (localSendingState != oldLocalState) {
+ emit localSendingStateChanged(localSendingState);
+ }
+
+ SendingState remoteSendingState =
+ mPriv->remoteSendingStateFromDirection();
+ if (remoteSendingState != oldRemoteState) {
+ emit remoteSendingStateChanged(remoteSendingState);
+ }
+}
+
+void StreamedMediaStream::gotStreamState(uint state)
+{
+ if (state == mPriv->state) {
+ return;
+ }
+
+ mPriv->state = state;
+}
+
+/* ====== StreamedMediaChannel ====== */
+struct TP_QT_NO_EXPORT StreamedMediaChannel::Private
+{
+ Private(StreamedMediaChannel *parent);
+ ~Private();
+
+ static void introspectStreams(Private *self);
+ static void introspectLocalHoldState(Private *self);
+
+ // Public object
+ StreamedMediaChannel *parent;
+
+ // Mandatory properties interface proxy
+ Client::DBus::PropertiesInterface *properties;
+
+ ReadinessHelper *readinessHelper;
+
+ // Introspection
+ StreamedMediaStreams incompleteStreams;
+ StreamedMediaStreams streams;
+
+ LocalHoldState localHoldState;
+ LocalHoldStateReason localHoldStateReason;
+};
+
+StreamedMediaChannel::Private::Private(StreamedMediaChannel *parent)
+ : parent(parent),
+ properties(parent->interface<Client::DBus::PropertiesInterface>()),
+ readinessHelper(parent->readinessHelper()),
+ localHoldState(LocalHoldStateUnheld),
+ localHoldStateReason(LocalHoldStateReasonNone)
+{
+ ReadinessHelper::Introspectables introspectables;
+
+ ReadinessHelper::Introspectable introspectableStreams(
+ QSet<uint>() << 0, // makesSenseForStatuses
+ Features() << Channel::FeatureCore, // dependsOnFeatures (core)
+ QStringList(), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &Private::introspectStreams,
+ this);
+ introspectables[FeatureStreams] = introspectableStreams;
+
+ ReadinessHelper::Introspectable introspectableLocalHoldState(
+ QSet<uint>() << 0, // makesSenseForStatuses
+ Features() << Channel::FeatureCore, // dependsOnFeatures (core)
+ QStringList() << QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_HOLD), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &Private::introspectLocalHoldState,
+ this);
+ introspectables[FeatureLocalHoldState] = introspectableLocalHoldState;
+
+ readinessHelper->addIntrospectables(introspectables);
+}
+
+StreamedMediaChannel::Private::~Private()
+{
+}
+
+void StreamedMediaChannel::Private::introspectStreams(StreamedMediaChannel::Private *self)
+{
+ StreamedMediaChannel *parent = self->parent;
+ Client::ChannelTypeStreamedMediaInterface *streamedMediaInterface =
+ parent->interface<Client::ChannelTypeStreamedMediaInterface>();
+
+ parent->connect(streamedMediaInterface,
+ SIGNAL(StreamAdded(uint,uint,uint)),
+ SLOT(onStreamAdded(uint,uint,uint)));
+ parent->connect(streamedMediaInterface,
+ SIGNAL(StreamRemoved(uint)),
+ SLOT(onStreamRemoved(uint)));
+ parent->connect(streamedMediaInterface,
+ SIGNAL(StreamDirectionChanged(uint,uint,uint)),
+ SLOT(onStreamDirectionChanged(uint,uint,uint)));
+ parent->connect(streamedMediaInterface,
+ SIGNAL(StreamStateChanged(uint,uint)),
+ SLOT(onStreamStateChanged(uint,uint)));
+ parent->connect(streamedMediaInterface,
+ SIGNAL(StreamError(uint,uint,QString)),
+ SLOT(onStreamError(uint,uint,QString)));
+
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+ streamedMediaInterface->ListStreams(), parent);
+ parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher *)),
+ SLOT(gotStreams(QDBusPendingCallWatcher *)));
+}
+
+void StreamedMediaChannel::Private::introspectLocalHoldState(StreamedMediaChannel::Private *self)
+{
+ StreamedMediaChannel *parent = self->parent;
+ Client::ChannelInterfaceHoldInterface *holdInterface =
+ parent->interface<Client::ChannelInterfaceHoldInterface>();
+
+ parent->connect(holdInterface,
+ SIGNAL(HoldStateChanged(uint,uint)),
+ SLOT(onLocalHoldStateChanged(uint,uint)));
+
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+ holdInterface->GetHoldState(), parent);
+ parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotLocalHoldState(QDBusPendingCallWatcher*)));
+}
+
+/**
+ * \class StreamedMediaChannel
+ * \ingroup clientchannel
+ * \headerfile TelepathyQt/streamed-media-channel.h <TelepathyQt/StreamedMediaChannel>
+ *
+ * \brief The StreamedMediaChannel class represents a Telepathy channel of type StreamedMedia.
+ *
+ * For more details, please refer to \telepathy_spec.
+ *
+ * See \ref async_model, \ref shared_ptr
+ */
+
+/**
+ * Feature representing the core that needs to become ready to make the
+ * StreamedMediaChannel object usable.
+ *
+ * This is currently the same as Channel::FeatureCore, but may change to include more.
+ *
+ * When calling isReady(), becomeReady(), this feature is implicitly added
+ * to the requested features.
+ * \sa awaitingLocalAnswer(), awaitingRemoteAnswer(), acceptCall(), hangupCall(), handlerStreamingRequired()
+ */
+const Feature StreamedMediaChannel::FeatureCore = Feature(QLatin1String(Channel::staticMetaObject.className()), 0, true);
+
+/**
+ * Feature used in order to access media stream specific methods.
+ *
+ * See media stream specific methods' documentation for more details.
+ * \sa streams(), streamsForType(),
+ * requestStream(), requestStreams(), streamAdded()
+ * removeStream(), removeStreams(), streamRemoved(),
+ * streamDirectionChanged(), streamStateChanged(), streamError()
+ */
+const Feature StreamedMediaChannel::FeatureStreams = Feature(QLatin1String(StreamedMediaChannel::staticMetaObject.className()), 0);
+
+/**
+ * Feature used in order to access local hold state info.
+ *
+ * See local hold state specific methods' documentation for more details.
+ * \sa localHoldState(), localHoldStateReason(), requestHold(), localHoldStateChanged()
+ */
+const Feature StreamedMediaChannel::FeatureLocalHoldState = Feature(QLatin1String(StreamedMediaChannel::staticMetaObject.className()), 1);
+
+/**
+ * Create a new StreamedMediaChannel object.
+ *
+ * \param connection Connection owning this channel, and specifying the
+ * service.
+ * \param objectPath The channel object path.
+ * \param immutableProperties The channel immutable properties.
+ * \return A StreamedMediaChannelPtr object pointing to the newly created
+ * StreamedMediaChannel object.
+ */
+StreamedMediaChannelPtr StreamedMediaChannel::create(const ConnectionPtr &connection,
+ const QString &objectPath, const QVariantMap &immutableProperties)
+{
+ return StreamedMediaChannelPtr(new StreamedMediaChannel(connection,
+ objectPath, immutableProperties, StreamedMediaChannel::FeatureCore));
+}
+
+/**
+ * Construct a new StreamedMediaChannel object.
+ *
+ * \param connection Connection owning this channel, and specifying the
+ * service.
+ * \param objectPath The channel object path.
+ * \param immutableProperties The channel immutable properties.
+ * \param coreFeature The core feature of the channel type, if any. The corresponding introspectable should
+ * depend on StreamedMediaChannel::FeatureCore.
+ */
+StreamedMediaChannel::StreamedMediaChannel(const ConnectionPtr &connection,
+ const QString &objectPath,
+ const QVariantMap &immutableProperties,
+ const Feature &coreFeature)
+ : Channel(connection, objectPath, immutableProperties, coreFeature),
+ mPriv(new Private(this))
+{
+}
+
+/**
+ * Class destructor.
+ */
+StreamedMediaChannel::~StreamedMediaChannel()
+{
+ delete mPriv;
+}
+
+/**
+ * Return a list of media streams in this channel.
+ *
+ * This methods requires StreamedMediaChannel::FeatureStreams to be ready.
+ *
+ * \return A list of pointers to StreamedMediaStream objects.
+ * \sa streamAdded(), streamRemoved(), streamsForType(), requestStreams()
+ */
+StreamedMediaStreams StreamedMediaChannel::streams() const
+{
+ return mPriv->streams;
+}
+
+/**
+ * Return a list of media streams in this channel for the given type \a type.
+ *
+ * This methods requires StreamedMediaChannel::FeatureStreams to be ready.
+ *
+ * \param type The interested type.
+ * \return A list of pointers to StreamedMediaStream objects.
+ * \sa streamAdded(), streamRemoved(), streams(), requestStreams()
+ */
+StreamedMediaStreams StreamedMediaChannel::streamsForType(MediaStreamType type) const
+{
+ StreamedMediaStreams ret;
+ foreach (const StreamedMediaStreamPtr &stream, mPriv->streams) {
+ if (stream->type() == type) {
+ ret << stream;
+ }
+ }
+ return ret;
+}
+
+/**
+ * Return whether this channel is awaiting local answer.
+ *
+ * This method requires StreamedMediaChannel::FeatureCore to be ready.
+ *
+ * \return \c true if awaiting local answer, \c false otherwise.
+ * \sa awaitingRemoteAnswer(), acceptCall()
+ */
+bool StreamedMediaChannel::awaitingLocalAnswer() const
+{
+ return groupSelfHandleIsLocalPending();
+}
+
+/**
+ * Return whether this channel is awaiting remote answer.
+ *
+ * This method requires StreamedMediaChannel::FeatureCore to be ready.
+ *
+ * \return \c true if awaiting remote answer, \c false otherwise.
+ * \sa awaitingLocalAnswer()
+ */
+bool StreamedMediaChannel::awaitingRemoteAnswer() const
+{
+ return !groupRemotePendingContacts().isEmpty();
+}
+
+/**
+ * Accept an incoming call.
+ *
+ * This method requires StreamedMediaChannel::FeatureCore to be ready.
+ *
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the call has finished.
+ * \sa awaitingLocalAnswer(), hangupCall()
+ */
+PendingOperation *StreamedMediaChannel::acceptCall()
+{
+ return groupAddSelfHandle();
+}
+
+/**
+ * Remove the specified media stream from this channel.
+ *
+ * This methods requires StreamedMediaChannel::FeatureStreams to be ready.
+ *
+ * \param stream Media stream to remove.
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the call has finished.
+ * \sa streamRemoved(), streams(), streamsForType()
+ */
+PendingOperation *StreamedMediaChannel::removeStream(const StreamedMediaStreamPtr &stream)
+{
+ if (!stream) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT),
+ QLatin1String("Unable to remove a null stream"),
+ StreamedMediaChannelPtr(this));
+ }
+
+ // StreamedMedia.RemoveStreams will trigger StreamedMedia.StreamRemoved
+ // that will proper remove the stream
+ Client::ChannelTypeStreamedMediaInterface *streamedMediaInterface =
+ interface<Client::ChannelTypeStreamedMediaInterface>();
+ return new PendingVoid(
+ streamedMediaInterface->RemoveStreams(
+ UIntList() << stream->id()),
+ StreamedMediaChannelPtr(this));
+}
+
+/**
+ * Remove the specified media streams from this channel.
+ *
+ * This methods requires StreamedMediaChannel::FeatureStreams to be ready.
+ *
+ * \param streams List of media streams to remove.
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the call has finished.
+ * \sa streamRemoved(), streams(), streamsForType()
+ */
+PendingOperation *StreamedMediaChannel::removeStreams(const StreamedMediaStreams &streams)
+{
+ UIntList ids;
+ foreach (const StreamedMediaStreamPtr &stream, streams) {
+ if (!stream) {
+ continue;
+ }
+ ids << stream->id();
+ }
+
+ if (ids.isEmpty()) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT),
+ QLatin1String("Unable to remove invalid streams"),
+ StreamedMediaChannelPtr(this));
+ }
+
+ Client::ChannelTypeStreamedMediaInterface *streamedMediaInterface =
+ interface<Client::ChannelTypeStreamedMediaInterface>();
+ return new PendingVoid(
+ streamedMediaInterface->RemoveStreams(ids),
+ StreamedMediaChannelPtr(this));
+}
+
+/**
+ * Request that media streams be established to exchange the given type \a type
+ * of media with the given contact \a contact.
+ *
+ * This methods requires StreamedMediaChannel::FeatureStreams to be ready.
+ *
+ * \return A PendingStreamedMediaStreams which will emit PendingStreamedMediaStreams::finished
+ * when the call has finished.
+ * \sa streamAdded(), streams(), streamsForType()
+ */
+PendingStreamedMediaStreams *StreamedMediaChannel::requestStream(
+ const ContactPtr &contact,
+ MediaStreamType type)
+{
+ return new PendingStreamedMediaStreams(StreamedMediaChannelPtr(this),
+ contact,
+ QList<MediaStreamType>() << type);
+}
+
+/**
+ * Request that media streams be established to exchange the given types \a
+ * types of media with the given contact \a contact.
+ *
+ * This methods requires StreamedMediaChannel::FeatureStreams to be ready.
+ *
+ * \return A PendingStreamedMediaStreams which will emit PendingStreamedMediaStreams::finished
+ * when the call has finished.
+ * \sa streamAdded(), streams(), streamsForType()
+ */
+PendingStreamedMediaStreams *StreamedMediaChannel::requestStreams(
+ const ContactPtr &contact,
+ QList<MediaStreamType> types)
+{
+ return new PendingStreamedMediaStreams(StreamedMediaChannelPtr(this),
+ contact, types);
+}
+
+/**
+ * Request that the call is ended.
+ *
+ * This method requires StreamedMediaChannel::FeatureCore to be ready.
+ *
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the call has finished.
+ */
+PendingOperation *StreamedMediaChannel::hangupCall()
+{
+ return requestLeave();
+}
+
+/**
+ * Check whether media streaming by the handler is required for this channel.
+ *
+ * For channels with the #TP_QT_IFACE_CHANNEL_INTERFACE_MEDIA_SIGNALLING interface,
+ * the main handler of the channel is responsible for doing the actual streaming, for instance by
+ * calling createFarsightChannel(channel) from TelepathyQt-Farsight library
+ * and using the telepathy-farsight API on the resulting TfChannel.
+ *
+ * This method requires StreamedMediaChannel::FeatureCore to be ready.
+ *
+ * \return \c true if required, \c false otherwise.
+ */
+bool StreamedMediaChannel::handlerStreamingRequired() const
+{
+ return interfaces().contains(
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_MEDIA_SIGNALLING));
+}
+
+/**
+ * Return the local hold state for this channel.
+ *
+ * Whether the local user has placed this channel on hold.
+ *
+ * This method requires StreamedMediaChannel::FeatureHoldState to be ready.
+ *
+ * \return The local hold state as #LocalHoldState.
+ * \sa requestHold(), localHoldStateChanged()
+ */
+LocalHoldState StreamedMediaChannel::localHoldState() const
+{
+ if (!isReady(FeatureLocalHoldState)) {
+ warning() << "StreamedMediaChannel::localHoldState() used with FeatureLocalHoldState not ready";
+ } else if (!interfaces().contains(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_HOLD))) {
+ warning() << "StreamedMediaChannel::localHoldStateReason() used with no hold interface";
+ }
+
+ return mPriv->localHoldState;
+}
+
+/**
+ * Return the reason why localHoldState() changed to its current value.
+ *
+ * This method requires StreamedMediaChannel::FeatureLocalHoldState to be ready.
+ *
+ * \return The local hold state reason as #LocalHoldStateReason.
+ * \sa requestHold(), localHoldStateChanged()
+ */
+LocalHoldStateReason StreamedMediaChannel::localHoldStateReason() const
+{
+ if (!isReady(FeatureLocalHoldState)) {
+ warning() << "StreamedMediaChannel::localHoldStateReason() used with FeatureLocalHoldState not ready";
+ } else if (!interfaces().contains(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_HOLD))) {
+ warning() << "StreamedMediaChannel::localHoldStateReason() used with no hold interface";
+ }
+
+ return mPriv->localHoldStateReason;
+}
+
+/**
+ * Request that the channel be put on hold (be instructed not to send any media
+ * streams to you) or be taken off hold.
+ *
+ * If the CM can immediately tell that the requested state
+ * change could not possibly succeed, the resulting PendingOperation will fail
+ * with error code #TP_QT_ERROR_NOT_AVAILABLE.
+ * If the requested state is the same as the current state, the resulting
+ * PendingOperation will finish successfully.
+ *
+ * Otherwise, the channel's local hold state will change to
+ * #LocalHoldStatePendingHold or #LocalHoldStatePendingUnhold (as
+ * appropriate), then the resulting PendingOperation will finish successfully.
+ *
+ * The eventual success or failure of the request is indicated by a subsequent
+ * localHoldStateChanged() signal, changing the local hold state to
+ * #LocalHoldStateHeld or #LocalHoldStateUnheld.
+ *
+ * If the channel has multiple streams, and the connection manager succeeds in
+ * changing the hold state of one stream but fails to change the hold state of
+ * another, it will attempt to revert all streams to their previous hold
+ * states.
+ *
+ * If the channel does not support the #TP_QT_IFACE_CHANNEL_INTERFACE_HOLD
+ * interface, the PendingOperation will fail with error code
+ * #TP_QT_ERROR_NOT_IMPLEMENTED.
+ *
+ * \param hold A boolean indicating whether or not the channel should be on hold
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the request finishes.
+ * \sa localHoldState(), localHoldStateReason(), localHoldStateChanged()
+ */
+PendingOperation *StreamedMediaChannel::requestHold(bool hold)
+{
+ if (!interfaces().contains(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_HOLD))) {
+ warning() << "StreamedMediaChannel::requestHold() used with no hold interface";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("StreamedMediaChannel does not support hold interface"),
+ StreamedMediaChannelPtr(this));
+ }
+
+ Client::ChannelInterfaceHoldInterface *holdInterface =
+ interface<Client::ChannelInterfaceHoldInterface>();
+ return new PendingVoid(holdInterface->RequestHold(hold),
+ StreamedMediaChannelPtr(this));
+}
+
+/**
+ * \fn void StreamedMediaChannel::streamAdded(const Tp::StreamedMediaStreamPtr &stream)
+ *
+ * Emitted when a media stream is added to this channel.
+ *
+ * \param stream The media stream that was added.
+ * \sa streams(), streamsForType(), streamRemoved()
+ */
+
+/**
+ * \fn void StreamedMediaChannel::streamRemoved(const Tp::StreamedMediaStreamPtr &stream)
+ *
+ * Emitted when a media stream is removed from this channel.
+ *
+ * \param stream The media stream that was removed.
+ * \sa streams(), streamsForType(), streamAdded()
+ */
+
+/**
+ * \fn void StreamedMediaChannel::streamDirectionChanged(
+ * const Tp::StreamedMediaStreamPtr &stream, Tp::MediaStreamDirection direction,
+ * Tp::MediaStreamPendingSend pendingSend)
+ *
+ * Emitted when a media stream direction changes.
+ *
+ * \param stream The media stream which the direction changed.
+ * \param direction The new direction of the stream that changed.
+ * \param pendingSend The new pending send flags of the stream that changed.
+ * \sa StreamedMediaStream::direction()
+ */
+
+/**
+ * \fn void StreamedMediaChannel::streamStateChanged(
+ * const Tp::StreamedMediaStreamPtr &stream, Tp::MediaStreamState state)
+ *
+ * Emitted when a media stream state changes.
+ *
+ * \param stream The media stream which the state changed.
+ * \param state The new state of the stream that changed.
+ * \sa StreamedMediaStream::state()
+ */
+
+/**
+ * \fn void StreamedMediaChannel::streamError(
+ * const Tp::StreamedMediaStreamPtr &stream,
+ * Tp::StreamedMediaStreamError errorCode, const QString &errorMessage)
+ *
+ * Emitted when an error occurs on a media stream.
+ *
+ * \param stream The media stream which the error occurred.
+ * \param errorCode The error code.
+ * \param errorMessage The error message.
+ */
+
+/**
+ * \fn void StreamedMediaChannel::localHoldStateChanged(Tp::LocalHoldState state, Tp::LocalHoldStateReason reason);
+ *
+ * Emitted when the local hold state of this channel changes.
+ *
+ * \param state The new local hold state of this channel.
+ * \param reason The reason why the change occurred.
+ * \sa localHoldState(), localHoldStateReason()
+ */
+
+void StreamedMediaChannel::onStreamReady(PendingOperation *op)
+{
+ PendingReady *pr = qobject_cast<PendingReady*>(op);
+ StreamedMediaStreamPtr stream = StreamedMediaStreamPtr::qObjectCast(pr->proxy());
+
+ if (op->isError()) {
+ mPriv->incompleteStreams.removeOne(stream);
+ if (!isReady(FeatureStreams) && mPriv->incompleteStreams.size() == 0) {
+ // let's not fail because a stream could not become ready
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureStreams, true);
+ }
+ return;
+ }
+
+ // the stream was removed before become ready
+ if (!mPriv->incompleteStreams.contains(stream)) {
+ if (!isReady(FeatureStreams) && mPriv->incompleteStreams.size() == 0) {
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureStreams, true);
+ }
+ return;
+ }
+
+ mPriv->incompleteStreams.removeOne(stream);
+ mPriv->streams.append(stream);
+
+ if (isReady(FeatureStreams)) {
+ emit streamAdded(stream);
+ }
+
+ if (!isReady(FeatureStreams) && mPriv->incompleteStreams.size() == 0) {
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureStreams, true);
+ }
+}
+
+void StreamedMediaChannel::gotStreams(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<MediaStreamInfoList> reply = *watcher;
+ if (reply.isError()) {
+ warning().nospace() << "StreamedMedia.ListStreams failed with" <<
+ reply.error().name() << ": " << reply.error().message();
+
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureStreams,
+ false, reply.error());
+ watcher->deleteLater();
+ return;
+ }
+
+ debug() << "Got reply to StreamedMedia::ListStreams()";
+
+ MediaStreamInfoList streamInfoList = reply.value();
+ if (streamInfoList.size() > 0) {
+ foreach (const MediaStreamInfo &streamInfo, streamInfoList) {
+ StreamedMediaStreamPtr stream = lookupStreamById(
+ streamInfo.identifier);
+ if (!stream) {
+ addStream(streamInfo);
+ } else {
+ onStreamDirectionChanged(streamInfo.identifier,
+ streamInfo.direction, streamInfo.pendingSendFlags);
+ onStreamStateChanged(streamInfo.identifier,
+ streamInfo.state);
+ }
+ }
+ } else {
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureStreams, true);
+ }
+
+ watcher->deleteLater();
+}
+
+void StreamedMediaChannel::onStreamAdded(uint streamId,
+ uint contactHandle, uint streamType)
+{
+ if (lookupStreamById(streamId)) {
+ debug() << "Received StreamedMedia.StreamAdded for an existing "
+ "stream, ignoring";
+ return;
+ }
+
+ MediaStreamInfo streamInfo = {
+ streamId,
+ contactHandle,
+ streamType,
+ MediaStreamStateDisconnected,
+ MediaStreamDirectionReceive,
+ MediaStreamPendingLocalSend
+ };
+ addStream(streamInfo);
+}
+
+void StreamedMediaChannel::onStreamRemoved(uint streamId)
+{
+ debug() << "Received StreamedMedia.StreamRemoved for stream" <<
+ streamId;
+
+ StreamedMediaStreamPtr stream = lookupStreamById(streamId);
+ if (!stream) {
+ return;
+ }
+ bool incomplete = mPriv->incompleteStreams.contains(stream);
+ if (incomplete) {
+ mPriv->incompleteStreams.removeOne(stream);
+ } else {
+ mPriv->streams.removeOne(stream);
+ }
+
+ if (isReady(FeatureStreams) && !incomplete) {
+ emit streamRemoved(stream);
+ }
+
+ // the stream was added/removed before become ready
+ if (!isReady(FeatureStreams) &&
+ mPriv->streams.size() == 0 &&
+ mPriv->incompleteStreams.size() == 0) {
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureStreams, true);
+ }
+}
+
+void StreamedMediaChannel::onStreamDirectionChanged(uint streamId,
+ uint streamDirection, uint streamPendingFlags)
+{
+ debug() << "Received StreamedMedia.StreamDirectionChanged for stream" <<
+ streamId << "with direction changed to" << streamDirection;
+
+ StreamedMediaStreamPtr stream = lookupStreamById(streamId);
+ if (!stream) {
+ return;
+ }
+
+ uint oldDirection = stream->direction();
+ uint oldPendingFlags = stream->pendingSend();
+
+ stream->gotDirection(streamDirection, streamPendingFlags);
+
+ if (oldDirection != streamDirection ||
+ oldPendingFlags != streamPendingFlags) {
+ emit streamDirectionChanged(stream,
+ (MediaStreamDirection) streamDirection,
+ (MediaStreamPendingSend) streamPendingFlags);
+ }
+}
+
+void StreamedMediaChannel::onStreamStateChanged(uint streamId,
+ uint streamState)
+{
+ debug() << "Received StreamedMedia.StreamStateChanged for stream" <<
+ streamId << "with state changed to" << streamState;
+
+ StreamedMediaStreamPtr stream = lookupStreamById(streamId);
+ if (!stream) {
+ return;
+ }
+
+ uint oldState = stream->state();
+
+ stream->gotStreamState(streamState);
+
+ if (oldState != streamState) {
+ emit streamStateChanged(stream, (MediaStreamState) streamState);
+ }
+}
+
+void StreamedMediaChannel::onStreamError(uint streamId,
+ uint errorCode, const QString &errorMessage)
+{
+ debug() << "Received StreamedMedia.StreamError for stream" <<
+ streamId << "with error code" << errorCode <<
+ "and message:" << errorMessage;
+
+ StreamedMediaStreamPtr stream = lookupStreamById(streamId);
+ if (!stream) {
+ return;
+ }
+
+ emit streamError(stream, (MediaStreamError) errorCode, errorMessage);
+}
+
+void StreamedMediaChannel::gotLocalHoldState(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<uint, uint> reply = *watcher;
+ if (reply.isError()) {
+ warning().nospace() << "StreamedMedia::Hold::GetHoldState()"
+ " failed with " << reply.error().name() << ": " <<
+ reply.error().message();
+
+ debug() << "Ignoring error getting hold state and assuming we're not "
+ "on hold";
+ onLocalHoldStateChanged(mPriv->localHoldState,
+ mPriv->localHoldStateReason);
+ watcher->deleteLater();
+ return;
+ }
+
+ debug() << "Got reply to StreamedMedia::Hold::GetHoldState()";
+ onLocalHoldStateChanged(reply.argumentAt<0>(), reply.argumentAt<1>());
+ watcher->deleteLater();
+}
+
+void StreamedMediaChannel::onLocalHoldStateChanged(uint localHoldState,
+ uint localHoldStateReason)
+{
+ bool changed = false;
+ if (mPriv->localHoldState != static_cast<LocalHoldState>(localHoldState) ||
+ mPriv->localHoldStateReason != static_cast<LocalHoldStateReason>(localHoldStateReason)) {
+ changed = true;
+ }
+
+ mPriv->localHoldState = static_cast<LocalHoldState>(localHoldState);
+ mPriv->localHoldStateReason = static_cast<LocalHoldStateReason>(localHoldStateReason);
+
+ if (!isReady(FeatureLocalHoldState)) {
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureLocalHoldState, true);
+ } else {
+ if (changed) {
+ emit localHoldStateChanged(mPriv->localHoldState,
+ mPriv->localHoldStateReason);
+ }
+ }
+}
+
+StreamedMediaStreamPtr StreamedMediaChannel::addStream(const MediaStreamInfo &streamInfo)
+{
+ StreamedMediaStreamPtr stream = StreamedMediaStreamPtr(
+ new StreamedMediaStream(StreamedMediaChannelPtr(this), streamInfo));
+
+ mPriv->incompleteStreams.append(stream);
+ connect(stream->becomeReady(),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onStreamReady(Tp::PendingOperation*)));
+ return stream;
+}
+
+StreamedMediaStreamPtr StreamedMediaChannel::lookupStreamById(uint streamId)
+{
+ foreach (const StreamedMediaStreamPtr &stream, mPriv->streams) {
+ if (stream->id() == streamId) {
+ return stream;
+ }
+ }
+
+ foreach (const StreamedMediaStreamPtr &stream, mPriv->incompleteStreams) {
+ if (stream->id() == streamId) {
+ return stream;
+ }
+ }
+
+ return StreamedMediaStreamPtr();
+}
+
+} // Tp
diff --git a/TelepathyQt/streamed-media-channel.h b/TelepathyQt/streamed-media-channel.h
new file mode 100644
index 00000000..594e3a28
--- /dev/null
+++ b/TelepathyQt/streamed-media-channel.h
@@ -0,0 +1,228 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_streamed_media_channel_h_HEADER_GUARD_
+#define _TelepathyQt_streamed_media_channel_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Channel>
+#include <TelepathyQt/PendingOperation>
+#include <TelepathyQt/Object>
+#include <TelepathyQt/SharedPtr>
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+class StreamedMediaChannel;
+
+typedef QList<StreamedMediaStreamPtr> StreamedMediaStreams;
+
+class TP_QT_EXPORT PendingStreamedMediaStreams : public PendingOperation
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(PendingStreamedMediaStreams)
+
+public:
+ ~PendingStreamedMediaStreams();
+
+ StreamedMediaChannelPtr channel() const;
+
+ StreamedMediaStreams streams() const;
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void gotStreams(QDBusPendingCallWatcher *op);
+
+ TP_QT_NO_EXPORT void onStreamRemoved(const Tp::StreamedMediaStreamPtr &stream);
+ TP_QT_NO_EXPORT void onStreamReady(Tp::PendingOperation *op);
+
+private:
+ friend class StreamedMediaChannel;
+
+ TP_QT_NO_EXPORT PendingStreamedMediaStreams(const StreamedMediaChannelPtr &channel,
+ const ContactPtr &contact,
+ const QList<MediaStreamType> &types);
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+class TP_QT_EXPORT StreamedMediaStream : public Object, private ReadyObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(StreamedMediaStream)
+
+public:
+ enum SendingState {
+ SendingStateNone = 0,
+ SendingStatePendingSend = 1,
+ SendingStateSending = 2
+ };
+
+ ~StreamedMediaStream();
+
+ StreamedMediaChannelPtr channel() const;
+
+ uint id() const;
+
+ ContactPtr contact() const;
+
+ MediaStreamState state() const;
+ MediaStreamType type() const;
+
+ SendingState localSendingState() const;
+ SendingState remoteSendingState() const;
+
+ bool sending() const;
+ bool receiving() const;
+
+ bool localSendingRequested() const;
+ bool remoteSendingRequested() const;
+
+ MediaStreamDirection direction() const;
+ MediaStreamPendingSend pendingSend() const;
+
+ PendingOperation *requestSending(bool send);
+ PendingOperation *requestReceiving(bool receive);
+
+ PendingOperation *requestDirection(MediaStreamDirection direction);
+ PendingOperation *requestDirection(bool send, bool receive);
+
+ PendingOperation *startDTMFTone(DTMFEvent event);
+ PendingOperation *stopDTMFTone();
+
+Q_SIGNALS:
+ void localSendingStateChanged(Tp::StreamedMediaStream::SendingState localSendingState);
+ void remoteSendingStateChanged(Tp::StreamedMediaStream::SendingState remoteSendingState);
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void gotContact(Tp::PendingOperation *op);
+
+private:
+ friend class PendingStreamedMediaStreams;
+ friend class StreamedMediaChannel;
+
+ static const Feature FeatureCore;
+
+ TP_QT_NO_EXPORT StreamedMediaStream(const StreamedMediaChannelPtr &channel, const MediaStreamInfo &info);
+
+ TP_QT_NO_EXPORT void gotDirection(uint direction, uint pendingSend);
+ TP_QT_NO_EXPORT void gotStreamState(uint state);
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+class TP_QT_EXPORT StreamedMediaChannel : public Channel
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(StreamedMediaChannel)
+ Q_ENUMS(StateChangeReason)
+
+public:
+ static const Feature FeatureCore;
+ static const Feature FeatureStreams;
+ static const Feature FeatureLocalHoldState;
+
+ enum StateChangeReason {
+ StateChangeReasonUnknown = 0,
+ StateChangeReasonUserRequested = 1
+ };
+
+ static StreamedMediaChannelPtr create(const ConnectionPtr &connection,
+ const QString &objectPath, const QVariantMap &immutableProperties);
+
+ virtual ~StreamedMediaChannel();
+
+ bool awaitingLocalAnswer() const;
+ bool awaitingRemoteAnswer() const;
+
+ PendingOperation *acceptCall();
+ PendingOperation *hangupCall();
+
+ StreamedMediaStreams streams() const;
+ StreamedMediaStreams streamsForType(MediaStreamType type) const;
+
+ PendingStreamedMediaStreams *requestStream(const ContactPtr &contact, MediaStreamType type);
+ PendingStreamedMediaStreams *requestStreams(const ContactPtr &contact, QList<MediaStreamType> types);
+
+ PendingOperation *removeStream(const StreamedMediaStreamPtr &stream);
+ PendingOperation *removeStreams(const StreamedMediaStreams &streams);
+
+ bool handlerStreamingRequired() const;
+
+ LocalHoldState localHoldState() const;
+ LocalHoldStateReason localHoldStateReason() const;
+ PendingOperation *requestHold(bool hold);
+
+Q_SIGNALS:
+ void streamAdded(const Tp::StreamedMediaStreamPtr &stream);
+ void streamRemoved(const Tp::StreamedMediaStreamPtr &stream);
+ void streamDirectionChanged(const Tp::StreamedMediaStreamPtr &stream,
+ Tp::MediaStreamDirection direction,
+ Tp::MediaStreamPendingSend pendingSend);
+ void streamStateChanged(const Tp::StreamedMediaStreamPtr &stream,
+ Tp::MediaStreamState state);
+ void streamError(const Tp::StreamedMediaStreamPtr &stream,
+ Tp::MediaStreamError errorCode,
+ const QString &errorMessage);
+
+ void localHoldStateChanged(Tp::LocalHoldState state,
+ Tp::LocalHoldStateReason reason);
+
+protected:
+ StreamedMediaChannel(const ConnectionPtr &connection,
+ const QString &objectPath, const QVariantMap &immutableProperties,
+ const Feature &coreFeature = StreamedMediaChannel::FeatureCore);
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onStreamReady(Tp::PendingOperation *op);
+
+ TP_QT_NO_EXPORT void gotStreams(QDBusPendingCallWatcher *);
+ TP_QT_NO_EXPORT void onStreamAdded(uint, uint, uint);
+ TP_QT_NO_EXPORT void onStreamRemoved(uint);
+ TP_QT_NO_EXPORT void onStreamDirectionChanged(uint, uint, uint);
+ TP_QT_NO_EXPORT void onStreamStateChanged(uint streamId, uint streamState);
+ TP_QT_NO_EXPORT void onStreamError(uint, uint, const QString &);
+
+ TP_QT_NO_EXPORT void gotLocalHoldState(QDBusPendingCallWatcher *);
+ TP_QT_NO_EXPORT void onLocalHoldStateChanged(uint, uint);
+
+private:
+ friend class PendingStreamedMediaStreams;
+
+ StreamedMediaStreamPtr addStream(const MediaStreamInfo &streamInfo);
+ StreamedMediaStreamPtr lookupStreamById(uint streamId);
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/test-backdoors.cpp b/TelepathyQt/test-backdoors.cpp
new file mode 100644
index 00000000..bf4b53a8
--- /dev/null
+++ b/TelepathyQt/test-backdoors.cpp
@@ -0,0 +1,50 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/test-backdoors.h>
+
+#include <TelepathyQt/DBusProxy>
+
+namespace Tp
+{
+
+void TestBackdoors::invalidateProxy(DBusProxy *proxy, const QString &reason, const QString &message)
+{
+ Q_ASSERT(proxy != 0);
+ Q_ASSERT(proxy->isValid());
+
+ proxy->invalidate(reason, message);
+}
+
+ConnectionCapabilities TestBackdoors::createConnectionCapabilities(
+ const RequestableChannelClassSpecList &rccSpecs)
+{
+ return ConnectionCapabilities(rccSpecs);
+}
+
+ContactCapabilities TestBackdoors::createContactCapabilities(
+ const RequestableChannelClassSpecList &rccSpecs, bool specificToContact)
+{
+ return ContactCapabilities(rccSpecs, specificToContact);
+}
+
+} // Tp
diff --git a/TelepathyQt/test-backdoors.h b/TelepathyQt/test-backdoors.h
new file mode 100644
index 00000000..a1b84887
--- /dev/null
+++ b/TelepathyQt/test-backdoors.h
@@ -0,0 +1,59 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008-2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008-2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_test_backdoors_h_HEADER_GUARD_
+#define _TelepathyQt_test_backdoors_h_HEADER_GUARD_
+
+#ifdef IN_TP_QT_HEADER
+#error "This file is an internal header and should never be included by a public one"
+#endif
+
+#include <TelepathyQt/Global>
+#include <TelepathyQt/ConnectionCapabilities>
+#include <TelepathyQt/ContactCapabilities>
+
+#include <QString>
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+namespace Tp
+{
+
+class DBusProxy;
+
+// Exported so the tests can use it even if they link dynamically
+// The header is not installed though, so this should be considered private API
+struct TP_QT_EXPORT TestBackdoors
+{
+ static void invalidateProxy(DBusProxy *proxy, const QString &reason, const QString &message);
+
+ static ConnectionCapabilities createConnectionCapabilities(
+ const RequestableChannelClassSpecList &rccSpecs);
+ static ContactCapabilities createContactCapabilities(
+ const RequestableChannelClassSpecList &rccSpecs, bool specificToContact);
+};
+
+} // Tp
+
+#endif /* DOXYGEN_SHOULD_SKIP_THIS */
+
+#endif
diff --git a/TelepathyQt/text-channel.cpp b/TelepathyQt/text-channel.cpp
new file mode 100644
index 00000000..83eab19b
--- /dev/null
+++ b/TelepathyQt/text-channel.cpp
@@ -0,0 +1,1277 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/TextChannel>
+
+#include "TelepathyQt/_gen/text-channel.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/Connection>
+#include <TelepathyQt/ConnectionLowlevel>
+#include <TelepathyQt/ContactManager>
+#include <TelepathyQt/Message>
+#include <TelepathyQt/PendingContacts>
+#include <TelepathyQt/PendingFailure>
+#include <TelepathyQt/PendingReady>
+#include <TelepathyQt/ReceivedMessage>
+#include <TelepathyQt/ReferencedHandles>
+
+#include <QDateTime>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT TextChannel::Private
+{
+ Private(TextChannel *parent);
+ ~Private();
+
+ static void introspectMessageQueue(Private *self);
+ static void introspectMessageCapabilities(Private *self);
+ static void introspectMessageSentSignal(Private *self);
+ static void enableChatStateNotifications(Private *self);
+
+ void updateInitialMessages();
+ void updateCapabilities();
+
+ void processMessageQueue();
+ void processChatStateQueue();
+
+ void contactLost(uint handle);
+ void contactFound(ContactPtr contact);
+
+ // Public object
+ TextChannel *parent;
+
+ Client::ChannelTypeTextInterface *textInterface;
+ Client::DBus::PropertiesInterface *properties;
+
+ ReadinessHelper *readinessHelper;
+
+ // FeatureMessageCapabilities and FeatureMessageQueue
+ QVariantMap props;
+ bool getAllInFlight;
+ bool gotProperties;
+
+ // requires FeatureMessageCapabilities
+ QStringList supportedContentTypes;
+ MessagePartSupportFlags messagePartSupport;
+ DeliveryReportingSupportFlags deliveryReportingSupport;
+
+ // FeatureMessageQueue
+ bool initialMessagesReceived;
+ struct MessageEvent
+ {
+ MessageEvent(const ReceivedMessage &message)
+ : isMessage(true), message(message),
+ removed(0)
+ { }
+ MessageEvent(uint removed)
+ : isMessage(false), message(), removed(removed)
+ { }
+
+ bool isMessage;
+ ReceivedMessage message;
+ uint removed;
+ };
+ QList<ReceivedMessage> messages;
+ QList<MessageEvent *> incompleteMessages;
+ QHash<QDBusPendingCallWatcher *, UIntList> acknowledgeBatches;
+
+ // FeatureChatState
+ struct ChatStateEvent
+ {
+ ChatStateEvent(uint contactHandle, uint state)
+ : contactHandle(contactHandle), state(state)
+ { }
+
+ ContactPtr contact;
+ uint contactHandle;
+ uint state;
+ };
+ QList<ChatStateEvent *> chatStateQueue;
+ QHash<ContactPtr, ChannelChatState> chatStates;
+
+ QSet<uint> awaitingContacts;
+};
+
+TextChannel::Private::Private(TextChannel *parent)
+ : parent(parent),
+ textInterface(parent->interface<Client::ChannelTypeTextInterface>()),
+ properties(parent->interface<Client::DBus::PropertiesInterface>()),
+ readinessHelper(parent->readinessHelper()),
+ getAllInFlight(false),
+ gotProperties(false),
+ messagePartSupport(0),
+ deliveryReportingSupport(0),
+ initialMessagesReceived(false)
+{
+ ReadinessHelper::Introspectables introspectables;
+
+ ReadinessHelper::Introspectable introspectableMessageQueue(
+ QSet<uint>() << 0, // makesSenseForStatuses
+ Features() << Channel::FeatureCore, // dependsOnFeatures (core)
+ QStringList(), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &Private::introspectMessageQueue,
+ this);
+ introspectables[FeatureMessageQueue] = introspectableMessageQueue;
+
+ ReadinessHelper::Introspectable introspectableMessageCapabilities(
+ QSet<uint>() << 0, // makesSenseForStatuses
+ Features() << Channel::FeatureCore, // dependsOnFeatures (core)
+ QStringList(), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &Private::introspectMessageCapabilities,
+ this);
+ introspectables[FeatureMessageCapabilities] = introspectableMessageCapabilities;
+
+ ReadinessHelper::Introspectable introspectableMessageSentSignal(
+ QSet<uint>() << 0, // makesSenseForStatuses
+ Features() << Channel::FeatureCore, // dependsOnFeatures (core)
+ QStringList(), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &Private::introspectMessageSentSignal,
+ this);
+ introspectables[FeatureMessageSentSignal] = introspectableMessageSentSignal;
+
+ ReadinessHelper::Introspectable introspectableChatState(
+ QSet<uint>() << 0, // makesSenseForStatuses
+ Features() << Channel::FeatureCore, // dependsOnFeatures (core)
+ QStringList() << QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_CHAT_STATE), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &Private::enableChatStateNotifications,
+ this);
+ introspectables[FeatureChatState] = introspectableChatState;
+
+ readinessHelper->addIntrospectables(introspectables);
+}
+
+TextChannel::Private::~Private()
+{
+ foreach (MessageEvent *e, incompleteMessages) {
+ delete e;
+ }
+
+ foreach (ChatStateEvent *e, chatStateQueue) {
+ delete e;
+ }
+}
+
+void TextChannel::Private::introspectMessageQueue(
+ TextChannel::Private *self)
+{
+ TextChannel *parent = self->parent;
+
+ if (parent->hasMessagesInterface()) {
+ Client::ChannelInterfaceMessagesInterface *messagesInterface =
+ parent->interface<Client::ChannelInterfaceMessagesInterface>();
+
+ // FeatureMessageQueue needs signal connections + Get (but we
+ // might as well do GetAll and reduce the number of code paths)
+ parent->connect(messagesInterface,
+ SIGNAL(MessageReceived(Tp::MessagePartList)),
+ SLOT(onMessageReceived(Tp::MessagePartList)));
+ parent->connect(messagesInterface,
+ SIGNAL(PendingMessagesRemoved(Tp::UIntList)),
+ SLOT(onPendingMessagesRemoved(Tp::UIntList)));
+
+ if (!self->gotProperties && !self->getAllInFlight) {
+ self->getAllInFlight = true;
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+ self->properties->GetAll(
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_MESSAGES)),
+ parent);
+ parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotProperties(QDBusPendingCallWatcher*)));
+ } else if (self->gotProperties) {
+ self->updateInitialMessages();
+ }
+ } else {
+ // FeatureMessageQueue needs signal connections + ListPendingMessages
+ parent->connect(self->textInterface,
+ SIGNAL(Received(uint,uint,uint,uint,uint,QString)),
+ SLOT(onTextReceived(uint,uint,uint,uint,uint,const QString)));
+
+ // we present SendError signals as if they were incoming
+ // messages, to be consistent with Messages
+ parent->connect(self->textInterface,
+ SIGNAL(SendError(uint,uint,uint,QString)),
+ SLOT(onTextSendError(uint,uint,uint,QString)));
+
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+ self->textInterface->ListPendingMessages(false), parent);
+ parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotPendingMessages(QDBusPendingCallWatcher*)));
+ }
+}
+
+void TextChannel::Private::introspectMessageCapabilities(
+ TextChannel::Private *self)
+{
+ TextChannel *parent = self->parent;
+
+ if (parent->hasMessagesInterface()) {
+ if (!self->gotProperties && !self->getAllInFlight) {
+ self->getAllInFlight = true;
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+ self->properties->GetAll(
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_MESSAGES)),
+ parent);
+ parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotProperties(QDBusPendingCallWatcher*)));
+ } else if (self->gotProperties) {
+ self->updateCapabilities();
+ }
+ } else {
+ self->supportedContentTypes =
+ (QStringList(QLatin1String("text/plain")));
+ parent->readinessHelper()->setIntrospectCompleted(
+ FeatureMessageCapabilities, true);
+ }
+}
+
+void TextChannel::Private::introspectMessageSentSignal(
+ TextChannel::Private *self)
+{
+ TextChannel *parent = self->parent;
+
+ if (parent->hasMessagesInterface()) {
+ Client::ChannelInterfaceMessagesInterface *messagesInterface =
+ parent->interface<Client::ChannelInterfaceMessagesInterface>();
+
+ parent->connect(messagesInterface,
+ SIGNAL(MessageSent(Tp::MessagePartList,uint,QString)),
+ SLOT(onMessageSent(Tp::MessagePartList,uint,QString)));
+ } else {
+ parent->connect(self->textInterface,
+ SIGNAL(Sent(uint,uint,QString)),
+ SLOT(onTextSent(uint,uint,QString)));
+ }
+
+ self->readinessHelper->setIntrospectCompleted(FeatureMessageSentSignal, true);
+}
+
+void TextChannel::Private::enableChatStateNotifications(
+ TextChannel::Private *self)
+{
+ TextChannel *parent = self->parent;
+ Client::ChannelInterfaceChatStateInterface *chatStateInterface =
+ parent->interface<Client::ChannelInterfaceChatStateInterface>();
+
+ parent->connect(chatStateInterface,
+ SIGNAL(ChatStateChanged(uint,uint)),
+ SLOT(onChatStateChanged(uint,uint)));
+
+ // FIXME fd.o#24882 - Download contacts' initial chat states
+
+ self->readinessHelper->setIntrospectCompleted(FeatureChatState, true);
+}
+
+void TextChannel::Private::updateInitialMessages()
+{
+ if (!readinessHelper->requestedFeatures().contains(FeatureMessageQueue) ||
+ readinessHelper->isReady(Features() << FeatureMessageQueue)) {
+ return;
+ }
+
+ Q_ASSERT(!initialMessagesReceived);
+ initialMessagesReceived = true;
+
+ MessagePartListList messages = qdbus_cast<MessagePartListList>(
+ props[QLatin1String("PendingMessages")]);
+ if (messages.isEmpty()) {
+ debug() << "Message queue empty: FeatureMessageQueue is now ready";
+ readinessHelper->setIntrospectCompleted(FeatureMessageQueue, true);
+ } else {
+ foreach (const MessagePartList &message, messages) {
+ parent->onMessageReceived(message);
+ }
+ }
+}
+
+void TextChannel::Private::updateCapabilities()
+{
+ if (!readinessHelper->requestedFeatures().contains(FeatureMessageCapabilities) ||
+ readinessHelper->isReady(Features() << FeatureMessageCapabilities)) {
+ return;
+ }
+
+ supportedContentTypes = qdbus_cast<QStringList>(
+ props[QLatin1String("SupportedContentTypes")]);
+ if (supportedContentTypes.isEmpty()) {
+ supportedContentTypes << QLatin1String("text/plain");
+ }
+ messagePartSupport = MessagePartSupportFlags(qdbus_cast<uint>(
+ props[QLatin1String("MessagePartSupportFlags")]));
+ deliveryReportingSupport = DeliveryReportingSupportFlags(
+ qdbus_cast<uint>(props[QLatin1String("DeliveryReportingSupport")]));
+ readinessHelper->setIntrospectCompleted(FeatureMessageCapabilities, true);
+}
+
+void TextChannel::Private::processMessageQueue()
+{
+ // Proceed as far as we can with the processing of incoming messages
+ // and message-removal events; message IDs aren't necessarily globally
+ // unique, so we need to process them in the correct order relative
+ // to incoming messages
+ while (!incompleteMessages.isEmpty()) {
+ const MessageEvent *e = incompleteMessages.first();
+ debug() << "MessageEvent:" << reinterpret_cast<const void *>(e);
+
+ if (e->isMessage) {
+ if (e->message.senderHandle() != 0 &&
+ !e->message.sender()) {
+ // the message doesn't have a sender Contact, but needs one.
+ // We'll have to stop processing here, and come back to it
+ // when we have more Contact objects
+ break;
+ }
+
+ // if we reach here, the message is ready
+ debug() << "Message is usable, copying to main queue";
+ messages << e->message;
+ emit parent->messageReceived(e->message);
+ } else {
+ // forget about the message(s) with ID e->removed (there should be
+ // at most one under normal circumstances)
+ int i = 0;
+ while (i < messages.size()) {
+ if (messages.at(i).pendingId() == e->removed) {
+ emit parent->pendingMessageRemoved(messages.at(i));
+ messages.removeAt(i);
+ } else {
+ i++;
+ }
+ }
+ }
+
+ debug() << "Dropping first event";
+ delete incompleteMessages.takeFirst();
+ }
+
+ if (incompleteMessages.isEmpty()) {
+ if (readinessHelper->requestedFeatures().contains(FeatureMessageQueue) &&
+ !readinessHelper->isReady(Features() << FeatureMessageQueue)) {
+ debug() << "incompleteMessages empty for the first time: "
+ "FeatureMessageQueue is now ready";
+ readinessHelper->setIntrospectCompleted(FeatureMessageQueue, true);
+ }
+ return;
+ }
+
+ // What Contact objects do we need in order to proceed, ignoring those
+ // for which we've already sent a request?
+ HandleIdentifierMap contactsRequired;
+ foreach (const MessageEvent *e, incompleteMessages) {
+ if (e->isMessage) {
+ uint handle = e->message.senderHandle();
+ if (handle != 0 && !e->message.sender()
+ && !awaitingContacts.contains(handle)) {
+ contactsRequired.insert(handle, e->message.senderId());
+ }
+ }
+ }
+
+ if (contactsRequired.isEmpty()) {
+ return;
+ }
+
+ ConnectionPtr conn = parent->connection();
+ conn->lowlevel()->injectContactIds(contactsRequired);
+
+ parent->connect(conn->contactManager()->contactsForHandles(
+ contactsRequired.keys()),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onContactsFinished(Tp::PendingOperation*)));
+
+ awaitingContacts |= contactsRequired.keys().toSet();
+}
+
+void TextChannel::Private::processChatStateQueue()
+{
+ while (!chatStateQueue.isEmpty()) {
+ const ChatStateEvent *e = chatStateQueue.first();
+ debug() << "ChatStateEvent:" << reinterpret_cast<const void *>(e);
+
+ if (e->contact.isNull()) {
+ // the chat state Contact object wasn't retrieved yet, but needs
+ // one. We'll have to stop processing here, and come back to it
+ // when we have more Contact objects
+ break;
+ }
+
+ chatStates.insert(e->contact, (ChannelChatState) e->state);
+
+ // if we reach here, the Contact object is ready
+ emit parent->chatStateChanged(e->contact, (ChannelChatState) e->state);
+
+ debug() << "Dropping first event";
+ delete chatStateQueue.takeFirst();
+ }
+
+ // What Contact objects do we need in order to proceed, ignoring those
+ // for which we've already sent a request?
+ QSet<uint> contactsRequired;
+ foreach (const ChatStateEvent *e, chatStateQueue) {
+ if (!e->contact &&
+ !awaitingContacts.contains(e->contactHandle)) {
+ contactsRequired << e->contactHandle;
+ }
+ }
+
+ if (contactsRequired.isEmpty()) {
+ return;
+ }
+
+ // TODO: pass id hints to ContactManager if we ever gain support to retrieve contact ids
+ // from ChatState.
+ parent->connect(parent->connection()->contactManager()->contactsForHandles(
+ contactsRequired.toList()),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onContactsFinished(Tp::PendingOperation*)));
+
+ awaitingContacts |= contactsRequired;
+}
+
+void TextChannel::Private::contactLost(uint handle)
+{
+ // we're not going to get a Contact object for this handle, so mark the
+ // messages from that handle as "unknown sender"
+ foreach (MessageEvent *e, incompleteMessages) {
+ if (e->isMessage && e->message.senderHandle() == handle
+ && !e->message.sender()) {
+ e->message.clearSenderHandle();
+ }
+ }
+
+ // there is no point in sending chat state notifications for unknown
+ // contacts, removing chat state events from queue that refer to this handle
+ foreach (ChatStateEvent *e, chatStateQueue) {
+ if (e->contactHandle == handle) {
+ chatStateQueue.removeOne(e);
+ delete e;
+ }
+ }
+}
+
+void TextChannel::Private::contactFound(ContactPtr contact)
+{
+ uint handle = contact->handle().at(0);
+
+ foreach (MessageEvent *e, incompleteMessages) {
+ if (e->isMessage && e->message.senderHandle() == handle
+ && !e->message.sender()) {
+ e->message.setSender(contact);
+ }
+ }
+
+ foreach (ChatStateEvent *e, chatStateQueue) {
+ if (e->contactHandle == handle) {
+ e->contact = contact;
+ }
+ }
+}
+
+/**
+ * \class TextChannel
+ * \ingroup clientchannel
+ * \headerfile TelepathyQt/text-channel.h <TelepathyQt/TextChannel>
+ *
+ * \brief The TextChannel class represents a Telepathy channel of type Text.
+ *
+ * For more details, please refer to \telepathy_spec.
+ *
+ * See \ref async_model, \ref shared_ptr
+ */
+
+/**
+ * Feature representing the core that needs to become ready to make the
+ * TextChannel object usable.
+ *
+ * This is currently the same as Channel::FeatureCore, but may change to include more.
+ *
+ * When calling isReady(), becomeReady(), this feature is implicitly added
+ * to the requested features.
+ */
+const Feature TextChannel::FeatureCore = Feature(QLatin1String(Channel::staticMetaObject.className()), 0, true);
+
+/**
+ * Feature used in order to access the message queue info.
+ *
+ * See message queue methods' documentation for more details.
+ *
+ * \sa messageQueue(), messageReceived(), pendingMessageRemoved(), forget(), acknowledge()
+ */
+const Feature TextChannel::FeatureMessageQueue = Feature(QLatin1String(TextChannel::staticMetaObject.className()), 0);
+
+/**
+ * Feature used in order to access message capabilities info.
+ *
+ * See message capabilities methods' documentation for more details.
+ *
+ * \sa supportedContentTypes(), messagePartSupport(), deliveryReportingSupport()
+ */
+const Feature TextChannel::FeatureMessageCapabilities = Feature(QLatin1String(TextChannel::staticMetaObject.className()), 1);
+
+/**
+ * Feature used in order to receive notification when a message is sent.
+ *
+ * \sa messageSent()
+ */
+const Feature TextChannel::FeatureMessageSentSignal = Feature(QLatin1String(TextChannel::staticMetaObject.className()), 2);
+
+/**
+ * Feature used in order to keep track of chat state changes.
+ *
+ * See chat state methods' documentation for more details.
+ *
+ * \sa chatState(), chatStateChanged()
+ */
+const Feature TextChannel::FeatureChatState = Feature(QLatin1String(TextChannel::staticMetaObject.className()), 3);
+
+/**
+ * \fn void TextChannel::messageSent(const Tp::Message &message,
+ * Tp::MessageSendingFlags flags,
+ * const QString &sentMessageToken)
+ *
+ * Emitted when a message is sent, if the TextChannel::FeatureMessageSentSignal
+ * has been enabled.
+ *
+ * This signal is emitted regardless of whether the message is sent by this
+ * client, or another client using the same channel via D-Bus.
+ *
+ * \param message A message. This may differ slightly from what the client
+ * requested to send, for instance if it has been altered due
+ * to limitations of the instant messaging protocol used.
+ * \param flags #MessageSendingFlags that were in effect when the message was
+ * sent. Clients can use these in conjunction with
+ * deliveryReportingSupport() to determine whether delivery
+ * reporting can be expected.
+ * \param sentMessageToken Either an empty QString, or an opaque token used
+ * to match the message to any delivery reports.
+ */
+
+/**
+ * \fn void TextChannel::messageReceived(const Tp::ReceivedMessage &message)
+ *
+ * Emitted when a message is added to messageQueue(), if the
+ * TextChannel::FeatureMessageQueue Feature has been enabled.
+ *
+ * This occurs slightly later than the message being received over D-Bus;
+ * see messageQueue() for details.
+ *
+ * \param message The message received.
+ * \sa messageQueue(), acknowledge(), forget()
+ */
+
+/**
+ * \fn void TextChannel::pendingMessageRemoved(
+ * const Tp::ReceivedMessage &message)
+ *
+ * Emitted when a message is removed from messageQueue(), if the
+ * TextChannel::FeatureMessageQueue Feature has been enabled. See messageQueue() for the
+ * circumstances in which this happens.
+ *
+ * \param message The message removed.
+ * \sa messageQueue(), acknowledge(), forget()
+ */
+
+/**
+ * \fn void TextChannel::chatStateChanged(const Tp::ContactPtr &contact,
+ * ChannelChatState state)
+ *
+ * Emitted when the state of a member of the channel has changed, if the
+ * TextChannel::FeatureChatState feature has been enabled.
+ *
+ * Local state changes are also emitted here.
+ *
+ * \param contact The contact whose chat state changed.
+ * \param state The new chat state for \a contact.
+ * \sa chatState()
+ */
+
+/**
+ * Create a new TextChannel object.
+ *
+ * \param connection Connection owning this channel, and specifying the
+ * service.
+ * \param objectPath The channel object path.
+ * \param immutableProperties The channel immutable properties.
+ * \return A TextChannelPtr object pointing to the newly created
+ * TextChannel object.
+ */
+TextChannelPtr TextChannel::create(const ConnectionPtr &connection,
+ const QString &objectPath, const QVariantMap &immutableProperties)
+{
+ return TextChannelPtr(new TextChannel(connection, objectPath,
+ immutableProperties, TextChannel::FeatureCore));
+}
+
+/**
+ * Construct a new TextChannel object.
+ *
+ * \param connection Connection owning this channel, and specifying the
+ * service.
+ * \param objectPath The channel object path.
+ * \param immutableProperties The channel immutable properties.
+ * \param coreFeature The core feature of the channel type, if any. The corresponding introspectable should
+ * depend on TextChannel::FeatureCore.
+ */
+TextChannel::TextChannel(const ConnectionPtr &connection,
+ const QString &objectPath,
+ const QVariantMap &immutableProperties,
+ const Feature &coreFeature)
+ : Channel(connection, objectPath, immutableProperties, coreFeature),
+ mPriv(new Private(this))
+{
+}
+
+/**
+ * Class destructor.
+ */
+TextChannel::~TextChannel()
+{
+ delete mPriv;
+}
+
+/**
+ * Return whether this channel supports the Messages interface.
+ *
+ * If the interface is not supported, some advanced functionality will be unavailable.
+ *
+ * This method requires TextChannel::FeatureCore to be ready.
+ *
+ * \return \c true if the Messages interface is supported, \c false otherwise.
+ */
+bool TextChannel::hasMessagesInterface() const
+{
+ return interfaces().contains(QLatin1String(
+ TELEPATHY_INTERFACE_CHANNEL_INTERFACE_MESSAGES));
+}
+
+/**
+ * Return whether this channel supports the ChatState interface.
+ *
+ * If the interface is not supported, requestChatState() will fail and all contacts' chat states
+ * will appear to be #ChannelChatStateInactive.
+ *
+ * This method requires TextChannel::FeatureCore to be ready.
+ *
+ * \return \c true if the ChatState interface is supported, \c false otherwise.
+ * \sa requestChatState(), chatStateChanged()
+ */
+bool TextChannel::hasChatStateInterface() const
+{
+ return interfaces().contains(QLatin1String(
+ TELEPATHY_INTERFACE_CHANNEL_INTERFACE_CHAT_STATE));
+}
+
+/**
+ * Return whether contacts can be invited into this channel using
+ * inviteContacts() (which is equivalent to Channel::groupAddContacts()).
+ *
+ * Whether this is the case depends on the underlying protocol, the type of channel,
+ * and the user's privileges (in some chatrooms, only a privileged user
+ * can invite other contacts).
+ *
+ * This is an alias for Channel::groupCanAddContacts(), to indicate its meaning more
+ * clearly for Text channels.
+ *
+ * This method requires Channel::FeatureCore to be ready.
+ *
+ * \return \c true if contacts can be invited, \c false otherwise.
+ * \sa inviteContacts(), Channel::groupCanAddContacts(), Channel::groupAddContacts()
+ */
+bool TextChannel::canInviteContacts() const
+{
+ return groupCanAddContacts();
+}
+
+/* <!--x--> in the block below is used to escape the star-slash sequence */
+/**
+ * Return a list of supported MIME content types for messages on this channel.
+ *
+ * For a simple text channel this will be a list containing one item,
+ * "text/plain".
+ *
+ * This list may contain the special value "*<!--x-->/<!--x-->*", which
+ * indicates that any content type is supported.
+ *
+ * This method requires TextChannel::FeatureMessageCapabilities to be ready.
+ *
+ * \return The list of MIME content types.
+ */
+QStringList TextChannel::supportedContentTypes() const
+{
+ return mPriv->supportedContentTypes;
+}
+
+/**
+ * Return a set of flags indicating support for multi-part messages on this
+ * channel.
+ *
+ * This is zero on simple text channels, or greater than zero if
+ * there is partial or full support for multi-part messages.
+ *
+ * This method requires TextChannel::FeatureMessageCapabilities to be ready.
+ *
+ * \return The flags as #MessagePartSupportFlags.
+ */
+MessagePartSupportFlags TextChannel::messagePartSupport() const
+{
+ return mPriv->messagePartSupport;
+}
+
+/**
+ * Return a set of flags indicating support for delivery reporting on this
+ * channel.
+ *
+ * This is zero if there are no particular guarantees, or greater
+ * than zero if delivery reports can be expected under certain circumstances.
+ *
+ * This method requires TextChannel::FeatureMessageCapabilities to be ready.
+ *
+ * \return The flags as #DeliveryReportingSupportFlags.
+ */
+DeliveryReportingSupportFlags TextChannel::deliveryReportingSupport() const
+{
+ return mPriv->deliveryReportingSupport;
+}
+
+/**
+ * Return a list of messages received in this channel.
+ *
+ * Messages are added to this list when they are received from the instant
+ * messaging service; the messageReceived() signal is emitted.
+ *
+ * There is a small delay between the message being received over D-Bus and
+ * becoming available to users of this C++ API, since a small amount of
+ * additional information needs to be fetched. However, the relative ordering
+ * of all the messages in a channel is preserved.
+ *
+ * Messages are removed from this list when they are acknowledged with the
+ * acknowledge() or forget() methods. On channels where hasMessagesInterface()
+ * returns \c true, they will also be removed when acknowledged by a different
+ * client. In either case, the pendingMessageRemoved() signal is emitted.
+ *
+ * This method requires TextChannel::FeatureMessageQueue to be ready.
+ *
+ * \return A list of ReceivedMessage objects.
+ * \sa messageReceived()
+ */
+QList<ReceivedMessage> TextChannel::messageQueue() const
+{
+ return mPriv->messages;
+}
+
+/**
+ * Return the current chat state for \a contact.
+ *
+ * If hasChatStateInterface() returns \c false, this method will always return
+ * #ChannelChatStateInactive.
+ *
+ * This method requires TextChannel::FeatureChatState to be ready.
+ *
+ * \return The contact chat state as #ChannelChatState.
+ */
+ChannelChatState TextChannel::chatState(const ContactPtr &contact) const
+{
+ if (!isReady(FeatureChatState)) {
+ warning() << "TextChannel::chatState() used with "
+ "FeatureChatState not ready";
+ return ChannelChatStateInactive;
+ }
+
+ if (mPriv->chatStates.contains(contact)) {
+ return mPriv->chatStates.value(contact);
+ }
+ return ChannelChatStateInactive;
+}
+
+void TextChannel::onAcknowledgePendingMessagesReply(
+ QDBusPendingCallWatcher *watcher)
+{
+ UIntList ids = mPriv->acknowledgeBatches.value(watcher);
+ QDBusPendingReply<> reply = *watcher;
+
+ if (reply.isError()) {
+ // One of the IDs was bad, and we can't know which one. Recover by
+ // doing as much as possible, and hope for the best...
+ debug() << "Recovering from AcknowledgePendingMessages failure for: "
+ << ids;
+ foreach (uint id, ids) {
+ mPriv->textInterface->AcknowledgePendingMessages(UIntList() << id);
+ }
+ }
+
+ mPriv->acknowledgeBatches.remove(watcher);
+ watcher->deleteLater();
+}
+
+/**
+ * Acknowledge that received messages have been displayed to the user.
+ *
+ * Note that this method should only be called by the main handler of a channel, usually
+ * meaning the user interface process that displays the channel to the user
+ * (when a channel dispatcher is used, the handler must acknowledge messages,
+ * and other approvers or observers must not acknowledge messages).
+ *
+ * Processes other than the main handler of a channel can free memory used
+ * by the library by calling forget() instead.
+ *
+ * This method requires TextChannel::FeatureMessageQueue to be ready.
+ *
+ * \param messages A list of received messages that have now been displayed.
+ * \sa forget(), messageQueue(), messageReceived(), pendingMessageRemoved()
+ */
+void TextChannel::acknowledge(const QList<ReceivedMessage> &messages)
+{
+ UIntList ids;
+
+ foreach (const ReceivedMessage &m, messages) {
+ if (m.isFromChannel(TextChannelPtr(this))) {
+ ids << m.pendingId();
+ } else {
+ warning() << "message did not come from this channel, ignoring";
+ }
+ }
+
+ if (ids.isEmpty()) {
+ return;
+ }
+
+ // we're going to acknowledge these messages (or as many as possible, if
+ // we lose a race with another acknowledging process), so let's remove
+ // them from the list immediately
+ forget(messages);
+
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+ mPriv->textInterface->AcknowledgePendingMessages(ids),
+ this);
+ connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(onAcknowledgePendingMessagesReply(QDBusPendingCallWatcher*)));
+ mPriv->acknowledgeBatches[watcher] = ids;
+}
+
+/**
+ * Remove messages from the message queue without acknowledging them.
+ *
+ * Note that this method frees memory used by the library, but
+ * does not free the corresponding memory in the CM process.
+ * It should be used by clients that are not the main handler for a channel;
+ * the main handler for a channel should use acknowledge() instead.
+ *
+ * This method requires TextChannel::FeatureMessageQueue to be ready.
+ *
+ * \param messages A list of received messages that have now been processed.
+ * \sa acknowledge(), messageQueue(), messageReceived(), pendingMessageRemoved()
+ */
+void TextChannel::forget(const QList<ReceivedMessage> &messages)
+{
+ foreach (const ReceivedMessage &m, messages) {
+ if (!m.isFromChannel(TextChannelPtr(this))) {
+ warning() << "message did not come from this channel, ignoring";
+ } else if (mPriv->messages.removeOne(m)) {
+ emit pendingMessageRemoved(m);
+ }
+ }
+}
+
+/**
+ * Request that a message be sent on this channel.
+ *
+ * When the message has been submitted for delivery,
+ * this method will return and the messageSent() signal will be emitted.
+ *
+ * If the message cannot be submitted for delivery, the returned pending operation will fail and no
+ * signal is emitted.
+ *
+ * This method requires TextChannel::FeatureCore to be ready.
+ *
+ * \param text The message body.
+ * \param type The message type.
+ * \param flags Flags affecting how the message is sent.
+ * Note that the channel may ignore some or all flags, depending on
+ * deliveryReportingSupport(); the flags that were handled by the CM are provided in
+ * messageSent().
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the message has been submitted for delivery.
+ * \sa messageSent()
+ */
+PendingSendMessage *TextChannel::send(const QString &text,
+ ChannelTextMessageType type, MessageSendingFlags flags)
+{
+ Message m(type, text);
+ PendingSendMessage *op = new PendingSendMessage(TextChannelPtr(this), m);
+
+ if (hasMessagesInterface()) {
+ Client::ChannelInterfaceMessagesInterface *messagesInterface =
+ interface<Client::ChannelInterfaceMessagesInterface>();
+
+ connect(new QDBusPendingCallWatcher(
+ messagesInterface->SendMessage(m.parts(),
+ (uint) flags)),
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ op,
+ SLOT(onMessageSent(QDBusPendingCallWatcher*)));
+ } else {
+ connect(new QDBusPendingCallWatcher(mPriv->textInterface->Send(type, text)),
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ op,
+ SLOT(onTextSent(QDBusPendingCallWatcher*)));
+ }
+ return op;
+}
+
+/**
+ * Request that a message be sent on this channel.
+ *
+ * When the message has been submitted for delivery,
+ * this method will return and the messageSent() signal will be emitted.
+ *
+ * If the message cannot be submitted for delivery, the returned pending operation will fail and no
+ * signal is emitted.
+ *
+ * This method requires TextChannel::FeatureCore to be ready.
+ *
+ * \param part The message parts.
+ * \param flags Flags affecting how the message is sent.
+ * Note that the channel may ignore some or all flags, depending on
+ * deliveryReportingSupport(); the flags that were handled by the CM are provided in
+ * messageSent().
+ * \return A PendingOperation which will emit PendingOperation::finished
+ * when the message has been submitted for delivery.
+ * \sa messageSent()
+ */
+PendingSendMessage *TextChannel::send(const MessagePartList &parts,
+ MessageSendingFlags flags)
+{
+ Message m(parts);
+ PendingSendMessage *op = new PendingSendMessage(TextChannelPtr(this), m);
+
+ if (hasMessagesInterface()) {
+ Client::ChannelInterfaceMessagesInterface *messagesInterface =
+ interface<Client::ChannelInterfaceMessagesInterface>();
+
+ connect(new QDBusPendingCallWatcher(
+ messagesInterface->SendMessage(m.parts(),
+ (uint) flags)),
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ op,
+ SLOT(onMessageSent(QDBusPendingCallWatcher*)));
+ } else {
+ connect(new QDBusPendingCallWatcher(mPriv->textInterface->Send(
+ m.messageType(), m.text())),
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ op,
+ SLOT(onTextSent(QDBusPendingCallWatcher*)));
+ }
+ return op;
+}
+
+/**
+ * Set the local chat state and notify other members of the channel that it has
+ * changed.
+ *
+ * Note that only the primary handler of the channel should set its chat
+ * state.
+ *
+ * This method requires TextChannel::FeatureCore to be ready.
+ *
+ * \param state The new state.
+ * \sa chatStateChanged()
+ */
+PendingOperation *TextChannel::requestChatState(ChannelChatState state)
+{
+ if (!interfaces().contains(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_CHAT_STATE))) {
+ warning() << "TextChannel::requestChatState() used with no chat "
+ "state interface";
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("TextChannel does not support chat state interface"),
+ TextChannelPtr(this));
+ }
+
+ Client::ChannelInterfaceChatStateInterface *chatStateInterface =
+ interface<Client::ChannelInterfaceChatStateInterface>();
+ return new PendingVoid(chatStateInterface->SetChatState(
+ (uint) state), TextChannelPtr(this));
+}
+
+void TextChannel::onMessageSent(const MessagePartList &parts,
+ uint flags,
+ const QString &sentMessageToken)
+{
+ emit messageSent(Message(parts), MessageSendingFlag(flags),
+ sentMessageToken);
+}
+
+void TextChannel::onContactsFinished(PendingOperation *op)
+{
+ PendingContacts *pc = qobject_cast<PendingContacts *>(op);
+ UIntList failed;
+
+ Q_ASSERT(pc->isForHandles());
+
+ foreach (uint handle, pc->handles()) {
+ mPriv->awaitingContacts -= handle;
+ }
+
+ if (pc->isError()) {
+ warning().nospace() << "Gathering contacts failed: "
+ << pc->errorName() << ": " << pc->errorMessage();
+ foreach (uint handle, pc->handles()) {
+ mPriv->contactLost(handle);
+ }
+ } else {
+ foreach (const ContactPtr &contact, pc->contacts()) {
+ mPriv->contactFound(contact);
+ }
+ foreach (uint handle, pc->invalidHandles()) {
+ mPriv->contactLost(handle);
+ }
+ }
+
+ // all contacts for messages and chat state events we were asking about
+ // should now be ready
+ mPriv->processMessageQueue();
+ mPriv->processChatStateQueue();
+}
+
+void TextChannel::onMessageReceived(const MessagePartList &parts)
+{
+ if (!mPriv->initialMessagesReceived) {
+ return;
+ }
+
+ mPriv->incompleteMessages << new Private::MessageEvent(
+ ReceivedMessage(parts, TextChannelPtr(this)));
+ mPriv->processMessageQueue();
+}
+
+void TextChannel::onPendingMessagesRemoved(const UIntList &ids)
+{
+ if (!mPriv->initialMessagesReceived) {
+ return;
+ }
+ foreach (uint id, ids) {
+ mPriv->incompleteMessages << new Private::MessageEvent(id);
+ }
+ mPriv->processMessageQueue();
+}
+
+void TextChannel::onTextSent(uint timestamp, uint type, const QString &text)
+{
+ emit messageSent(Message(timestamp, type, text), 0,
+ QLatin1String(""));
+}
+
+void TextChannel::onTextReceived(uint id, uint timestamp, uint sender,
+ uint type, uint flags, const QString &text)
+{
+ if (!mPriv->initialMessagesReceived) {
+ return;
+ }
+
+ MessagePart header;
+
+ if (timestamp == 0) {
+ timestamp = QDateTime::currentDateTime().toTime_t();
+ }
+ header.insert(QLatin1String("message-received"),
+ QDBusVariant(static_cast<qlonglong>(timestamp)));
+
+ header.insert(QLatin1String("pending-message-id"), QDBusVariant(id));
+ header.insert(QLatin1String("message-sender"), QDBusVariant(sender));
+ header.insert(QLatin1String("message-type"), QDBusVariant(type));
+
+ if (flags & ChannelTextMessageFlagScrollback) {
+ header.insert(QLatin1String("scrollback"), QDBusVariant(true));
+ }
+ if (flags & ChannelTextMessageFlagRescued) {
+ header.insert(QLatin1String("rescued"), QDBusVariant(true));
+ }
+
+ MessagePart body;
+
+ body.insert(QLatin1String("content-type"),
+ QDBusVariant(QLatin1String("text/plain")));
+ body.insert(QLatin1String("content"), QDBusVariant(text));
+
+ if (flags & ChannelTextMessageFlagTruncated) {
+ header.insert(QLatin1String("truncated"), QDBusVariant(true));
+ }
+
+ MessagePartList parts;
+ parts << header;
+ parts << body;
+
+ ReceivedMessage m(parts, TextChannelPtr(this));
+
+ if (flags & ChannelTextMessageFlagNonTextContent) {
+ // set the "you are not expected to understand this" flag
+ m.setForceNonText();
+ }
+
+ mPriv->incompleteMessages << new Private::MessageEvent(m);
+ mPriv->processMessageQueue();
+}
+
+void TextChannel::onTextSendError(uint error, uint timestamp, uint type,
+ const QString &text)
+{
+ if (!mPriv->initialMessagesReceived) {
+ return;
+ }
+
+ MessagePart header;
+
+ header.insert(QLatin1String("message-received"),
+ QDBusVariant(static_cast<qlonglong>(
+ QDateTime::currentDateTime().toTime_t())));
+ header.insert(QLatin1String("message-type"),
+ QDBusVariant(static_cast<uint>(
+ ChannelTextMessageTypeDeliveryReport)));
+
+ // we can't tell whether it's a temporary or permanent failure here,
+ // so guess based on the delivery-error
+ uint deliveryStatus;
+ switch (error) {
+ case ChannelTextSendErrorOffline:
+ case ChannelTextSendErrorPermissionDenied:
+ deliveryStatus = DeliveryStatusTemporarilyFailed;
+ break;
+
+ case ChannelTextSendErrorInvalidContact:
+ case ChannelTextSendErrorTooLong:
+ case ChannelTextSendErrorNotImplemented:
+ deliveryStatus = DeliveryStatusPermanentlyFailed;
+ break;
+
+ case ChannelTextSendErrorUnknown:
+ default:
+ deliveryStatus = DeliveryStatusTemporarilyFailed;
+ break;
+ }
+
+ header.insert(QLatin1String("delivery-status"),
+ QDBusVariant(deliveryStatus));
+ header.insert(QLatin1String("delivery-error"), QDBusVariant(error));
+
+ MessagePart echoHeader;
+ echoHeader.insert(QLatin1String("message-sent"),
+ QDBusVariant(timestamp));
+ echoHeader.insert(QLatin1String("message-type"),
+ QDBusVariant(type));
+
+ MessagePart echoBody;
+ echoBody.insert(QLatin1String("content-type"),
+ QDBusVariant(QLatin1String("text/plain")));
+ echoBody.insert(QLatin1String("content"), QDBusVariant(text));
+
+ MessagePartList echo;
+ echo << echoHeader;
+ echo << echoBody;
+ header.insert(QLatin1String("delivery-echo"),
+ QDBusVariant(QVariant::fromValue(echo)));
+
+ MessagePartList parts;
+ parts << header;
+}
+
+void TextChannel::gotProperties(QDBusPendingCallWatcher *watcher)
+{
+ Q_ASSERT(mPriv->getAllInFlight);
+ mPriv->getAllInFlight = false;
+ mPriv->gotProperties = true;
+
+ QDBusPendingReply<QVariantMap> reply = *watcher;
+ if (reply.isError()) {
+ warning().nospace() << "Properties::GetAll(Channel.Interface.Messages)"
+ " failed with " << reply.error().name() << ": " <<
+ reply.error().message();
+
+ ReadinessHelper *readinessHelper = mPriv->readinessHelper;
+ if (readinessHelper->requestedFeatures().contains(FeatureMessageQueue) &&
+ !readinessHelper->isReady(Features() << FeatureMessageQueue)) {
+ readinessHelper->setIntrospectCompleted(FeatureMessageQueue, false, reply.error());
+ }
+
+ if (readinessHelper->requestedFeatures().contains(FeatureMessageCapabilities) &&
+ !readinessHelper->isReady(Features() << FeatureMessageCapabilities)) {
+ readinessHelper->setIntrospectCompleted(FeatureMessageCapabilities, false, reply.error());
+ }
+ return;
+ }
+
+ debug() << "Properties::GetAll(Channel.Interface.Messages) returned";
+ mPriv->props = reply.value();
+
+ mPriv->updateInitialMessages();
+ mPriv->updateCapabilities();
+
+ watcher->deleteLater();
+}
+
+void TextChannel::gotPendingMessages(QDBusPendingCallWatcher *watcher)
+{
+ Q_ASSERT(!mPriv->initialMessagesReceived);
+ mPriv->initialMessagesReceived = true;
+
+ QDBusPendingReply<PendingTextMessageList> reply = *watcher;
+ if (reply.isError()) {
+ warning().nospace() << "Properties::GetAll(Channel.Interface.Messages)"
+ " failed with " << reply.error().name() << ": " <<
+ reply.error().message();
+
+ // TODO should we fail here?
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureMessageQueue, false, reply.error());
+ return;
+ }
+
+ debug() << "Text::ListPendingMessages returned";
+ PendingTextMessageList list = reply.value();
+
+ if (!list.isEmpty()) {
+ foreach (const PendingTextMessage &message, list) {
+ onTextReceived(message.identifier, message.unixTimestamp,
+ message.sender, message.messageType, message.flags,
+ message.text);
+ }
+ // processMessageQueue sets FeatureMessageQueue ready when the queue is empty for the first
+ // time
+ } else {
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureMessageQueue, true);
+ }
+
+ watcher->deleteLater();
+}
+
+void TextChannel::onChatStateChanged(uint contactHandle, uint state)
+{
+ mPriv->chatStateQueue.append(new Private::ChatStateEvent(
+ contactHandle, state));
+ mPriv->processChatStateQueue();
+}
+
+} // Tp
diff --git a/TelepathyQt/text-channel.h b/TelepathyQt/text-channel.h
new file mode 100644
index 00000000..4cf4241e
--- /dev/null
+++ b/TelepathyQt/text-channel.h
@@ -0,0 +1,139 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2009 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_text_channel_h_HEADER_GUARD_
+#define _TelepathyQt_text_channel_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Channel>
+#include <TelepathyQt/PendingOperation>
+#include <TelepathyQt/PendingSendMessage>
+
+namespace Tp
+{
+
+class Message;
+class ReceivedMessage;
+
+class TP_QT_EXPORT TextChannel : public Channel
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(TextChannel)
+
+public:
+ static const Feature FeatureCore;
+ static const Feature FeatureMessageQueue;
+ static const Feature FeatureMessageCapabilities;
+ static const Feature FeatureMessageSentSignal;
+ static const Feature FeatureChatState;
+
+ static TextChannelPtr create(const ConnectionPtr &connection,
+ const QString &objectPath, const QVariantMap &immutableProperties);
+
+ virtual ~TextChannel();
+
+ bool hasMessagesInterface() const;
+ bool hasChatStateInterface() const;
+ bool canInviteContacts() const;
+
+ // requires FeatureMessageCapabilities
+ QStringList supportedContentTypes() const;
+ MessagePartSupportFlags messagePartSupport() const;
+ DeliveryReportingSupportFlags deliveryReportingSupport() const;
+
+ // requires FeatureMessageQueue
+ QList<ReceivedMessage> messageQueue() const;
+
+ // requires FeatureChatState
+ ChannelChatState chatState(const ContactPtr &contact) const;
+
+public Q_SLOTS:
+ void acknowledge(const QList<ReceivedMessage> &messages);
+
+ void forget(const QList<ReceivedMessage> &messages);
+
+ PendingSendMessage *send(const QString &text,
+ ChannelTextMessageType type = ChannelTextMessageTypeNormal,
+ MessageSendingFlags flags = 0);
+
+ PendingSendMessage *send(const MessagePartList &parts,
+ MessageSendingFlags flags = 0);
+
+ inline PendingOperation *inviteContacts(
+ const QList<ContactPtr> &contacts,
+ const QString &message = QString())
+ {
+ return groupAddContacts(contacts, message);
+ }
+
+ PendingOperation *requestChatState(ChannelChatState state);
+
+Q_SIGNALS:
+ // FeatureMessageSentSignal
+ void messageSent(const Tp::Message &message,
+ Tp::MessageSendingFlags flags,
+ const QString &sentMessageToken);
+
+ // FeatureMessageQueue
+ void messageReceived(const Tp::ReceivedMessage &message);
+ void pendingMessageRemoved(
+ const Tp::ReceivedMessage &message);
+
+ // FeatureChatState
+ void chatStateChanged(const Tp::ContactPtr &contact,
+ Tp::ChannelChatState state);
+
+protected:
+ TextChannel(const ConnectionPtr &connection, const QString &objectPath,
+ const QVariantMap &immutableProperties,
+ const Feature &coreFeature = TextChannel::FeatureCore);
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onContactsFinished(Tp::PendingOperation *);
+ TP_QT_NO_EXPORT void onAcknowledgePendingMessagesReply(QDBusPendingCallWatcher *);
+
+ TP_QT_NO_EXPORT void onMessageSent(const Tp::MessagePartList &, uint,
+ const QString &);
+ TP_QT_NO_EXPORT void onMessageReceived(const Tp::MessagePartList &);
+ TP_QT_NO_EXPORT void onPendingMessagesRemoved(const Tp::UIntList &);
+
+ TP_QT_NO_EXPORT void onTextSent(uint, uint, const QString &);
+ TP_QT_NO_EXPORT void onTextReceived(uint, uint, uint, uint, uint, const QString &);
+ TP_QT_NO_EXPORT void onTextSendError(uint, uint, uint, const QString &);
+
+ TP_QT_NO_EXPORT void gotProperties(QDBusPendingCallWatcher *);
+ TP_QT_NO_EXPORT void gotPendingMessages(QDBusPendingCallWatcher *);
+
+ TP_QT_NO_EXPORT void onChatStateChanged(uint, uint);
+
+private:
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/tls-certificate.cpp b/TelepathyQt/tls-certificate.cpp
new file mode 100644
index 00000000..8ba4f1b5
--- /dev/null
+++ b/TelepathyQt/tls-certificate.cpp
@@ -0,0 +1,26 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define IN_TP_QT_HEADER
+#include <TelepathyQt/tls-certificate.h>
+
+#include "TelepathyQt/_gen/cli-tls-certificate-body.hpp"
+#include "TelepathyQt/_gen/cli-tls-certificate.moc.hpp"
diff --git a/TelepathyQt/tls-certificate.h b/TelepathyQt/tls-certificate.h
new file mode 100644
index 00000000..5b2c1c78
--- /dev/null
+++ b/TelepathyQt/tls-certificate.h
@@ -0,0 +1,31 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_tls_certificate_h_HEADER_GUARD_
+#define _TelepathyQt_tls_certificate_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/_gen/cli-tls-certificate.h>
+
+#endif
diff --git a/TelepathyQt/tls-certificate.xml b/TelepathyQt/tls-certificate.xml
new file mode 100644
index 00000000..0ba3d2d0
--- /dev/null
+++ b/TelepathyQt/tls-certificate.xml
@@ -0,0 +1,9 @@
+<tp:spec
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<tp:title>TLSCertificate</tp:title>
+
+<xi:include href="../spec/Authentication_TLS_Certificate.xml"/>
+
+</tp:spec>
diff --git a/TelepathyQt/tube-channel.cpp b/TelepathyQt/tube-channel.cpp
new file mode 100644
index 00000000..0e53b770
--- /dev/null
+++ b/TelepathyQt/tube-channel.cpp
@@ -0,0 +1,281 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010-2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/TubeChannel>
+
+#include "TelepathyQt/_gen/tube-channel.moc.hpp"
+
+#include "TelepathyQt/debug-internal.h"
+
+#include <TelepathyQt/PendingVariantMap>
+
+namespace Tp
+{
+
+struct TP_QT_NO_EXPORT TubeChannel::Private
+{
+ Private(TubeChannel *parent);
+
+ static void introspectTube(TubeChannel::Private *self);
+
+ void extractTubeProperties(const QVariantMap &props);
+
+ // Public object
+ TubeChannel *parent;
+
+ ReadinessHelper *readinessHelper;
+
+ // Introspection
+ TubeChannelState state;
+ QVariantMap parameters;
+};
+
+TubeChannel::Private::Private(TubeChannel *parent)
+ : parent(parent),
+ readinessHelper(parent->readinessHelper()),
+ state((TubeChannelState) -1)
+{
+ ReadinessHelper::Introspectables introspectables;
+
+ ReadinessHelper::Introspectable introspectableTube(
+ QSet<uint>() << 0, // makesSenseForStatuses
+ Features() << Channel::FeatureCore, // dependsOnFeatures (core)
+ QStringList() << QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_TUBE), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &TubeChannel::Private::introspectTube,
+ this);
+ introspectables[TubeChannel::FeatureCore] = introspectableTube;
+
+ readinessHelper->addIntrospectables(introspectables);
+}
+
+void TubeChannel::Private::introspectTube(TubeChannel::Private *self)
+{
+ TubeChannel *parent = self->parent;
+
+ debug() << "Introspecting tube properties";
+ Client::ChannelInterfaceTubeInterface *tubeInterface =
+ parent->interface<Client::ChannelInterfaceTubeInterface>();
+
+ parent->connect(tubeInterface,
+ SIGNAL(TubeChannelStateChanged(uint)),
+ SLOT(onTubeChannelStateChanged(uint)));
+
+ PendingVariantMap *pvm = tubeInterface->requestAllProperties();
+ parent->connect(pvm,
+ SIGNAL(finished(Tp::PendingOperation *)),
+ SLOT(gotTubeProperties(Tp::PendingOperation *)));
+}
+
+void TubeChannel::Private::extractTubeProperties(const QVariantMap &props)
+{
+ state = (Tp::TubeChannelState) qdbus_cast<uint>(props[QLatin1String("State")]);
+ parameters = qdbus_cast<QVariantMap>(props[QLatin1String("Parameters")]);
+}
+
+/**
+ * \class TubeChannel
+ * \ingroup clientchannel
+ * \headerfile TelepathyQt/tube-channel.h <TelepathyQt/TubeChannel>
+ *
+ * \brief The TubeChannel class is a base class for all tube types.
+ *
+ * A tube is a mechanism for arbitrary data transfer between two or more IM users,
+ * used to allow applications on the users' systems to communicate without having
+ * to establish network connections themselves.
+ *
+ * Note that TubeChannel should never be instantiated directly, instead one of its
+ * subclasses (e.g. IncomingStreamTubeChannel or OutgoingStreamTubeChannel) should be used.
+ *
+ * See \ref async_model, \ref shared_ptr
+ */
+
+/**
+ * Feature representing the core that needs to become ready to make the
+ * TubeChannel object usable.
+ *
+ * Note that this feature must be enabled in order to use most
+ * TubeChannel methods.
+ * See specific methods documentation for more details.
+ */
+const Feature TubeChannel::FeatureCore = Feature(QLatin1String(TubeChannel::staticMetaObject.className()), 0);
+
+/**
+ * \deprecated Use TubeChannel::FeatureCore instead.
+ */
+const Feature TubeChannel::FeatureTube = TubeChannel::FeatureCore;
+
+/**
+ * Create a new TubeChannel channel.
+ *
+ * \param connection Connection owning this channel, and specifying the
+ * service.
+ * \param objectPath The channel object path.
+ * \param immutableProperties The channel immutable properties.
+ * \return A TubeChannelPtr object pointing to the newly created
+ * TubeChannel object.
+ */
+TubeChannelPtr TubeChannel::create(const ConnectionPtr &connection,
+ const QString &objectPath, const QVariantMap &immutableProperties)
+{
+ return TubeChannelPtr(new TubeChannel(connection, objectPath,
+ immutableProperties));
+}
+
+/**
+ * Construct a new TubeChannel object.
+ *
+ * \param connection Connection owning this channel, and specifying the
+ * service.
+ * \param objectPath The channel object path.
+ * \param immutableProperties The channel immutable properties.
+ * \param coreFeature The core feature of the channel type, if any. The corresponding introspectable should
+ * depend on TubeChannel::FeatureCore.
+ */
+TubeChannel::TubeChannel(const ConnectionPtr &connection,
+ const QString &objectPath,
+ const QVariantMap &immutableProperties,
+ const Feature &coreFeature)
+ : Channel(connection, objectPath, immutableProperties, coreFeature),
+ mPriv(new Private(this))
+{
+}
+
+/**
+ * Class destructor.
+ */
+TubeChannel::~TubeChannel()
+{
+ delete mPriv;
+}
+
+/**
+ * Return the parameters associated with this tube, if any.
+ *
+ * The parameters are populated when an outgoing tube is offered, but they are most useful in the
+ * receiving end, where the parameters passed to the offer can be extracted for the tube's entire
+ * lifetime to bootstrap legacy protocols. All parameters are passed unchanged.
+ *
+ * This method requires TubeChannel::FeatureCore to be ready.
+ *
+ * \return The parameters as QVariantMap.
+ */
+QVariantMap TubeChannel::parameters() const
+{
+ if (!isReady(FeatureCore)) {
+ warning() << "TubeChannel::parameters() used with FeatureCore not ready";
+ return QVariantMap();
+ }
+
+ return mPriv->parameters;
+}
+
+/**
+ * Return the state of this tube.
+ *
+ * Change notification is via the stateChanged() signal.
+ *
+ * This method requires TubeChannel::FeatureCore to be ready.
+ *
+ * \return The state as #TubeChannelState.
+ * \sa stateChanged()
+ */
+TubeChannelState TubeChannel::state() const
+{
+ if (!isReady(FeatureCore)) {
+ warning() << "TubeChannel::state() used with FeatureCore not ready";
+ return TubeChannelStateNotOffered;
+ }
+
+ return mPriv->state;
+}
+
+/**
+ * \deprecated Use state() instead.
+ */
+TubeChannelState TubeChannel::tubeState() const
+{
+ return state();
+}
+
+void TubeChannel::setParameters(const QVariantMap &parameters)
+{
+ mPriv->parameters = parameters;
+}
+
+void TubeChannel::onTubeChannelStateChanged(uint newState)
+{
+ if (newState == mPriv->state) {
+ return;
+ }
+
+ uint oldState = mPriv->state;
+
+ debug() << "Tube state changed to" << newState;
+ mPriv->state = (Tp::TubeChannelState) newState;
+
+ /* only emit stateChanged if we already received the state from initial introspection */
+ if (oldState != (uint) -1) {
+ emit stateChanged((Tp::TubeChannelState) newState);
+ // FIXME (API/ABI break) Remove tubeStateChanged call
+ emit tubeStateChanged((Tp::TubeChannelState) newState);
+ }
+}
+
+void TubeChannel::gotTubeProperties(PendingOperation *op)
+{
+ if (!op->isError()) {
+ PendingVariantMap *pvm = qobject_cast<PendingVariantMap *>(op);
+
+ mPriv->extractTubeProperties(pvm->result());
+
+ debug() << "Got reply to Properties::GetAll(TubeChannel)";
+ mPriv->readinessHelper->setIntrospectCompleted(TubeChannel::FeatureCore, true);
+ } else {
+ warning().nospace() << "Properties::GetAll(TubeChannel) failed "
+ "with " << op->errorName() << ": " << op->errorMessage();
+ mPriv->readinessHelper->setIntrospectCompleted(TubeChannel::FeatureCore, false,
+ op->errorName(), op->errorMessage());
+ }
+}
+
+/**
+ * \fn void TubeChannel::stateChanged(Tp::TubeChannelState state)
+ *
+ * Emitted when the value of state() changes.
+ *
+ * \sa state The new state of this tube.
+ */
+
+/**
+ * \fn void TubeChannel::tubeStateChanged(Tp::TubeChannelState state)
+ *
+ * \deprecated Use stateChanged() instead.
+ */
+
+void TubeChannel::connectNotify(const char *signalName)
+{
+ if (qstrcmp(signalName, SIGNAL(tubeStateChanged(Tp::TubeChannelState))) == 0) {
+ warning() << "Connecting to deprecated signal tubeStateChanged(Tp::TubeChannelState)";
+ }
+}
+
+
+} // Tp
diff --git a/TelepathyQt/tube-channel.h b/TelepathyQt/tube-channel.h
new file mode 100644
index 00000000..42379203
--- /dev/null
+++ b/TelepathyQt/tube-channel.h
@@ -0,0 +1,80 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010-2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_tube_channel_h_HEADER_GUARD_
+#define _TelepathyQt_tube_channel_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Channel>
+
+namespace Tp
+{
+
+class TP_QT_EXPORT TubeChannel : public Channel
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(TubeChannel)
+
+public:
+ static const Feature FeatureCore;
+ // FIXME (API/ABI break) Remove FeatureTube in favour of FeatureCore
+ static const Feature FeatureTube;
+
+ static TubeChannelPtr create(const ConnectionPtr &connection,
+ const QString &objectPath, const QVariantMap &immutableProperties);
+
+ virtual ~TubeChannel();
+
+ TubeChannelState state() const;
+ TP_QT_DEPRECATED TubeChannelState tubeState() const;
+
+ QVariantMap parameters() const;
+
+Q_SIGNALS:
+ void stateChanged(Tp::TubeChannelState state);
+ void tubeStateChanged(Tp::TubeChannelState state);
+
+protected:
+ TubeChannel(const ConnectionPtr &connection, const QString &objectPath,
+ const QVariantMap &immutableProperties,
+ const Feature &coreFeature = TubeChannel::FeatureCore);
+
+ void setParameters(const QVariantMap &parameters);
+
+ // FIXME (API/ABI break) Remove connectNotify
+ void connectNotify(const char *);
+
+private Q_SLOTS:
+ TP_QT_NO_EXPORT void onTubeChannelStateChanged(uint newstate);
+ TP_QT_NO_EXPORT void gotTubeProperties(Tp::PendingOperation *op);
+
+private:
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+}
+
+#endif
diff --git a/TelepathyQt/types-internal.h b/TelepathyQt/types-internal.h
new file mode 100644
index 00000000..f6ed06b5
--- /dev/null
+++ b/TelepathyQt/types-internal.h
@@ -0,0 +1,156 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_types_internal_h_HEADER_GUARD_
+#define _TelepathyQt_types_internal_h_HEADER_GUARD_
+
+#include <QDBusVariant>
+#include <QDBusArgument>
+#include <TelepathyQt/Types>
+
+namespace Tp
+{
+
+/**
+ * Private structure used to circumvent QtDBus' strict demarshalling
+ */
+struct TP_QT_EXPORT SUSocketAddress
+{
+ /**
+ * A dotted-quad IPv4 address literal: four ASCII decimal numbers, each
+ * between 0 and 255 inclusive, e.g. &quot;192.168.0.1&quot;.
+ */
+ QString address;
+ /**
+ * The TCP or UDP port number.
+ */
+ uint port;
+};
+
+TP_QT_EXPORT bool operator==(const SUSocketAddress& v1, const SUSocketAddress& v2);
+TP_QT_EXPORT inline bool operator!=(const SUSocketAddress& v1, const SUSocketAddress& v2)
+{
+ return !operator==(v1, v2);
+}
+TP_QT_EXPORT QDBusArgument& operator<<(QDBusArgument& arg, const SUSocketAddress& val);
+TP_QT_EXPORT const QDBusArgument& operator>>(const QDBusArgument& arg, SUSocketAddress& val);
+
+} // Tp
+
+// specialise for Tp::SocketAddressIPv4, allowing it to be used in place of QDBusVariant
+template<> inline Tp::SocketAddressIPv4 qdbus_cast<Tp::SocketAddressIPv4>(const QDBusArgument &arg,
+Tp::SocketAddressIPv4 *)
+{
+ if (arg.currentSignature() == QLatin1String("(su)")) {
+ // Use Tp::SUSocketAddress
+ Tp::SUSocketAddress saddr = qdbus_cast<Tp::SUSocketAddress>(arg);
+ Tp::SocketAddressIPv4 addr;
+ addr.address = saddr.address;
+ addr.port = saddr.port;
+ return addr;
+ } else if (arg.currentSignature() == QLatin1String("(sq)")) {
+ // Keep it standard
+ Tp::SocketAddressIPv4 item;
+ arg >> item;
+ return item;
+ } else {
+ // This should never happen...
+ return Tp::SocketAddressIPv4();
+ }
+}
+
+template<> inline Tp::SocketAddressIPv4 qdbus_cast<Tp::SocketAddressIPv4>(const QVariant &v, Tp::SocketAddressIPv4 *)
+{
+ int id = v.userType();
+ if (id == qMetaTypeId<QDBusArgument>()) {
+ QDBusArgument arg = qvariant_cast<QDBusArgument>(v);
+
+ if (arg.currentSignature() == QLatin1String("(su)")) {
+ // Use Tp::SUSocketAddress
+ Tp::SUSocketAddress saddr = qdbus_cast<Tp::SUSocketAddress>(arg);
+ Tp::SocketAddressIPv4 addr;
+ addr.address = saddr.address;
+ addr.port = saddr.port;
+ return addr;
+ } else if (arg.currentSignature() == QLatin1String("(sq)")) {
+ // Keep it standard
+ Tp::SocketAddressIPv4 item;
+ arg >> item;
+ return item;
+ } else {
+ // This should never happen...
+ return Tp::SocketAddressIPv4();
+ }
+ } else
+ return qvariant_cast<Tp::SocketAddressIPv4>(v);
+}
+
+// specialise for Tp::SocketAddressIPv6, allowing it to be used in place of QDBusVariant
+template<> inline Tp::SocketAddressIPv6 qdbus_cast<Tp::SocketAddressIPv6>(const QDBusArgument &arg,
+Tp::SocketAddressIPv6 *)
+{
+ if (arg.currentSignature() == QLatin1String("(su)")) {
+ // Use Tp::SUSocketAddress
+ Tp::SUSocketAddress saddr = qdbus_cast<Tp::SUSocketAddress>(arg);
+ Tp::SocketAddressIPv6 addr;
+ addr.address = saddr.address;
+ addr.port = saddr.port;
+ return addr;
+ } else if (arg.currentSignature() == QLatin1String("(sq)")) {
+ // Keep it standard
+ Tp::SocketAddressIPv6 item;
+ arg >> item;
+ return item;
+ } else {
+ // This should never happen...
+ return Tp::SocketAddressIPv6();
+ }
+}
+
+template<> inline Tp::SocketAddressIPv6 qdbus_cast<Tp::SocketAddressIPv6>(const QVariant &v, Tp::SocketAddressIPv6 *)
+{
+ int id = v.userType();
+ if (id == qMetaTypeId<QDBusArgument>()) {
+ QDBusArgument arg = qvariant_cast<QDBusArgument>(v);
+
+ if (arg.currentSignature() == QLatin1String("(su)")) {
+ // Use Tp::SUSocketAddress
+ Tp::SUSocketAddress saddr = qdbus_cast<Tp::SUSocketAddress>(arg);
+ Tp::SocketAddressIPv6 addr;
+ addr.address = saddr.address;
+ addr.port = saddr.port;
+ return addr;
+ } else if (arg.currentSignature() == QLatin1String("(sq)")) {
+ // Keep it standard
+ Tp::SocketAddressIPv6 item;
+ arg >> item;
+ return item;
+ } else {
+ // This should never happen...
+ return Tp::SocketAddressIPv6();
+ }
+ } else
+ return qvariant_cast<Tp::SocketAddressIPv6>(v);
+}
+
+Q_DECLARE_METATYPE(Tp::SUSocketAddress)
+
+#endif
diff --git a/TelepathyQt/types.cpp b/TelepathyQt/types.cpp
new file mode 100644
index 00000000..e9a606a0
--- /dev/null
+++ b/TelepathyQt/types.cpp
@@ -0,0 +1,73 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/Types>
+
+#include <TelepathyQt/types-internal.h>
+
+#include "TelepathyQt/_gen/types-body.hpp"
+
+#include "TelepathyQt/future-internal.h"
+#include "TelepathyQt/_gen/future-types-body.hpp"
+
+namespace Tp {
+/**
+ * \\ingroup types
+ * \headerfile TelepathyQt/types.h <TelepathyQt/Types>
+ *
+ * Register the types used by the library with the QtDBus type system.
+ *
+ * Call this function to register the types used before using anything else in
+ * the library.
+ */
+void registerTypes()
+{
+ qDBusRegisterMetaType<Tp::SUSocketAddress>();
+
+ Tp::_registerTypes();
+ TpFuture::_registerTypes();
+}
+
+bool operator==(const SUSocketAddress& v1, const SUSocketAddress& v2)
+{
+ return ((v1.address == v2.address)
+ && (v1.port == v2.port)
+ );
+}
+
+QDBusArgument& operator<<(QDBusArgument& arg, const SUSocketAddress& val)
+{
+ arg.beginStructure();
+ arg << val.address << val.port;
+ arg.endStructure();
+ return arg;
+}
+
+const QDBusArgument& operator>>(const QDBusArgument& arg, SUSocketAddress& val)
+{
+ arg.beginStructure();
+ arg >> val.address >> val.port;
+ arg.endStructure();
+ return arg;
+}
+
+}
diff --git a/TelepathyQt/types.h b/TelepathyQt/types.h
new file mode 100644
index 00000000..ff502402
--- /dev/null
+++ b/TelepathyQt/types.h
@@ -0,0 +1,171 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2008 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_types_h_HEADER_GUARD_
+#define _TelepathyQt_types_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/_gen/types.h>
+
+#include <TelepathyQt/Global>
+#include <TelepathyQt/SharedPtr>
+#include <TelepathyQt/MethodInvocationContext>
+
+#include <QDBusVariant>
+
+namespace Tp
+{
+
+TP_QT_EXPORT void registerTypes();
+
+template <typename T> class Filter;
+template <typename T> class GenericCapabilityFilter;
+template <typename T> class GenericPropertyFilter;
+
+class AbstractClient;
+class AbstractClientApprover;
+class AbstractClientHandler;
+class AbstractClientObserver;
+class Account;
+typedef GenericCapabilityFilter<Account> AccountCapabilityFilter;
+class AccountFactory;
+typedef Filter<Account> AccountFilter;
+class AccountManager;
+class AccountPropertyFilter;
+class AccountSet;
+class Channel;
+class ChannelDispatchOperation;
+class ChannelFactory;
+class ChannelRequest;
+class ClientObject;
+class ClientRegistrar;
+class Connection;
+class ConnectionFactory;
+class ConnectionLowlevel;
+class ConnectionManager;
+class ConnectionManagerLowlevel;
+class Contact;
+class ContactFactory;
+class ContactManager;
+class ContactMessenger;
+class ContactSearchChannel;
+class DBusProxy;
+class FileTransferChannel;
+class IncomingFileTransferChannel;
+class IncomingStreamTubeChannel;
+class OutgoingFileTransferChannel;
+class OutgoingStreamTubeChannel;
+class Profile;
+class ProfileManager;
+class RoomListChannel;
+class SimpleObserver;
+class SimpleCallObserver;
+class SimpleTextObserver;
+class StreamedMediaChannel;
+class StreamedMediaStream;
+class StreamTubeChannel;
+class StreamTubeClient;
+class StreamTubeServer;
+class TextChannel;
+class TubeChannel;
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+typedef SharedPtr<AbstractClient> AbstractClientPtr;
+typedef SharedPtr<AbstractClientApprover> AbstractClientApproverPtr;
+typedef SharedPtr<AbstractClientHandler> AbstractClientHandlerPtr;
+typedef SharedPtr<AbstractClientObserver> AbstractClientObserverPtr;
+typedef SharedPtr<Account> AccountPtr;
+typedef SharedPtr<AccountCapabilityFilter> AccountCapabilityFilterPtr;
+typedef SharedPtr<const AccountCapabilityFilter> AccountCapabilityFilterConstPtr;
+typedef SharedPtr<AccountFactory> AccountFactoryPtr;
+typedef SharedPtr<const AccountFactory> AccountFactoryConstPtr;
+typedef SharedPtr<AccountFilter> AccountFilterPtr;
+typedef SharedPtr<const AccountFilter> AccountFilterConstPtr;
+typedef SharedPtr<AccountManager> AccountManagerPtr;
+typedef SharedPtr<AccountPropertyFilter> AccountPropertyFilterPtr;
+typedef SharedPtr<const AccountPropertyFilter> AccountPropertyFilterConstPtr;
+typedef SharedPtr<AccountSet> AccountSetPtr;
+typedef SharedPtr<Channel> ChannelPtr;
+typedef SharedPtr<ChannelDispatchOperation> ChannelDispatchOperationPtr;
+typedef SharedPtr<ChannelFactory> ChannelFactoryPtr;
+typedef SharedPtr<const ChannelFactory> ChannelFactoryConstPtr;
+typedef SharedPtr<ChannelRequest> ChannelRequestPtr;
+typedef SharedPtr<ClientObject> ClientObjectPtr;
+typedef SharedPtr<ClientRegistrar> ClientRegistrarPtr;
+typedef SharedPtr<Connection> ConnectionPtr;
+typedef SharedPtr<ConnectionFactory> ConnectionFactoryPtr;
+typedef SharedPtr<const ConnectionFactory> ConnectionFactoryConstPtr;
+typedef SharedPtr<ConnectionLowlevel> ConnectionLowlevelPtr;
+typedef SharedPtr<const ConnectionLowlevel> ConnectionLowlevelConstPtr;
+typedef SharedPtr<ConnectionManager> ConnectionManagerPtr;
+typedef SharedPtr<ConnectionManagerLowlevel> ConnectionManagerLowlevelPtr;
+typedef SharedPtr<const ConnectionManagerLowlevel> ConnectionManagerLowlevelConstPtr;
+typedef SharedPtr<Contact> ContactPtr;
+typedef QSet<ContactPtr> Contacts;
+typedef SharedPtr<ContactFactory> ContactFactoryPtr;
+typedef SharedPtr<const ContactFactory> ContactFactoryConstPtr;
+typedef SharedPtr<ContactManager> ContactManagerPtr;
+typedef SharedPtr<ContactMessenger> ContactMessengerPtr;
+typedef SharedPtr<ContactSearchChannel> ContactSearchChannelPtr;
+typedef SharedPtr<DBusProxy> DBusProxyPtr;
+typedef SharedPtr<FileTransferChannel> FileTransferChannelPtr;
+typedef SharedPtr<IncomingFileTransferChannel> IncomingFileTransferChannelPtr;
+typedef SharedPtr<IncomingStreamTubeChannel> IncomingStreamTubeChannelPtr;
+typedef SharedPtr<OutgoingFileTransferChannel> OutgoingFileTransferChannelPtr;
+typedef SharedPtr<OutgoingStreamTubeChannel> OutgoingStreamTubeChannelPtr;
+typedef SharedPtr<Profile> ProfilePtr;
+typedef SharedPtr<ProfileManager> ProfileManagerPtr;
+typedef SharedPtr<RoomListChannel> RoomListChannelPtr;
+typedef SharedPtr<SimpleObserver> SimpleObserverPtr;
+typedef SharedPtr<SimpleCallObserver> SimpleCallObserverPtr;
+typedef SharedPtr<SimpleTextObserver> SimpleTextObserverPtr;
+typedef SharedPtr<StreamedMediaChannel> StreamedMediaChannelPtr;
+typedef SharedPtr<StreamedMediaStream> StreamedMediaStreamPtr;
+typedef SharedPtr<StreamTubeChannel> StreamTubeChannelPtr;
+typedef SharedPtr<StreamTubeClient> StreamTubeClientPtr;
+typedef SharedPtr<StreamTubeServer> StreamTubeServerPtr;
+typedef SharedPtr<TextChannel> TextChannelPtr;
+typedef SharedPtr<TubeChannel> TubeChannelPtr;
+
+template<typename T1 = MethodInvocationContextTypes::Nil, typename T2 = MethodInvocationContextTypes::Nil,
+ typename T3 = MethodInvocationContextTypes::Nil, typename T4 = MethodInvocationContextTypes::Nil,
+ typename T5 = MethodInvocationContextTypes::Nil, typename T6 = MethodInvocationContextTypes::Nil,
+ typename T7 = MethodInvocationContextTypes::Nil, typename T8 = MethodInvocationContextTypes::Nil>
+class MethodInvocationContextPtr : public SharedPtr<MethodInvocationContext<T1, T2, T3, T4, T5, T6, T7, T8> >
+{
+public:
+ inline MethodInvocationContextPtr() { }
+ explicit inline MethodInvocationContextPtr(MethodInvocationContext<T1, T2, T3, T4, T5, T6, T7, T8> *d)
+ : SharedPtr<MethodInvocationContext<T1, T2, T3, T4, T5, T6, T7, T8> >(d) { }
+ inline MethodInvocationContextPtr(const SharedPtr<MethodInvocationContext<T1, T2, T3, T4, T5, T6, T7, T8> > &o)
+ : SharedPtr<MethodInvocationContext<T1, T2, T3, T4, T5, T6, T7, T8> >(o) { }
+};
+
+#endif /* DOXYGEN_SHOULD_SKIP_THIS */
+
+} // Tp
+
+#endif
diff --git a/TelepathyQt/utils.cpp b/TelepathyQt/utils.cpp
new file mode 100644
index 00000000..d3874770
--- /dev/null
+++ b/TelepathyQt/utils.cpp
@@ -0,0 +1,120 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 <TelepathyQt/Utils>
+
+#include <QByteArray>
+#include <QString>
+
+/**
+ * \defgroup utility functions
+ */
+
+namespace Tp
+{
+
+static inline bool isBad(char c, bool isFirst)
+{
+ return ((c < 'a' || c > 'z') &&
+ (c < 'A' || c > 'Z') &&
+ (c < '0' || c > '9' || isFirst));
+}
+
+/**
+ * Escape an arbitrary string so it follows the rules for a C identifier,
+ * and hence an object path component, interface element component,
+ * bus name component or member name in D-Bus.
+ *
+ * This is a reversible encoding, so it preserves distinctness.
+ *
+ * The escaping consists of replacing all non-alphanumerics, and the first
+ * character if it's a digit, with an underscore and two lower-case hex
+ * digits:
+ *
+ * "0123abc_xyz\x01\xff" -> _30123abc_5fxyz_01_ff
+ *
+ * i.e. similar to URI encoding, but with _ taking the role of %, and a
+ * smaller allowed set. As a special case, "" is escaped to "_" (just for
+ * completeness, really).
+ *
+ * \param string The string to be escaped.
+ * \return the escaped string.
+ */
+QString escapeAsIdentifier(const QString &string)
+{
+ bool bad = false;
+ QByteArray op;
+ QByteArray utf8;
+ const char *name;
+ const char *ptr, *firstOk;
+
+ /* This function is copy/pasted from tp_escape_as_identified from
+ * telepathy-glib. */
+
+ /* fast path for empty name */
+ if (string.isEmpty()) {
+ return QString::fromLatin1("_");
+ }
+
+ utf8 = string.toUtf8();
+ name = utf8.constData();
+ for (ptr = name; *ptr; ptr++) {
+ if (isBad(*ptr, ptr == name)) {
+ bad = true;
+ break;
+ }
+ }
+
+ /* fast path if it's clean */
+ if (!bad) {
+ return string;
+ }
+
+ /* If strictly less than ptr, firstOk is the first uncopied safe character.
+ */
+ firstOk = name;
+ for (ptr = name; *ptr; ptr++) {
+ if (isBad(*ptr, ptr == name)) {
+ char buf[4] = { 0, };
+
+ /* copy preceding safe characters if any */
+ if (firstOk < ptr) {
+ op.append(firstOk, ptr - firstOk);
+ }
+
+ /* escape the unsafe character */
+ qsnprintf(buf, sizeof (buf), "_%02x", (unsigned char)(*ptr));
+ op.append(buf);
+
+ /* restart after it */
+ firstOk = ptr + 1;
+ }
+ }
+ /* copy trailing safe characters if any */
+ if (firstOk < ptr) {
+ op.append(firstOk, ptr - firstOk);
+ }
+
+ return QString::fromLatin1(op.constData());
+}
+
+} // Tp
diff --git a/TelepathyQt/utils.h b/TelepathyQt/utils.h
new file mode 100644
index 00000000..02c2b35d
--- /dev/null
+++ b/TelepathyQt/utils.h
@@ -0,0 +1,41 @@
+/**
+ * This file is part of TelepathyQt
+ *
+ * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @copyright Copyright (C) 2010 Nokia Corporation
+ * @license LGPL 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 _TelepathyQt_utils_h_HEADER_GUARD_
+#define _TelepathyQt_utils_h_HEADER_GUARD_
+
+#ifndef IN_TP_QT_HEADER
+#error IN_TP_QT_HEADER
+#endif
+
+#include <TelepathyQt/Global>
+
+#include <QString>
+
+namespace Tp
+{
+
+TP_QT_EXPORT QString escapeAsIdentifier(const QString &string);
+
+} // Tp
+
+#endif