5 # Copyright (C) 2009 Olivier Le Thanh Duong, Guillaume Desmottes
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 2 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
22 logging
.basicConfig(level
=logging
.DEBUG
)
25 from dbus
.service
import method
, signal
, Object
28 from connection_watcher
import ConnectionWatcher
30 from telepathy
.interfaces
import CHANNEL_TYPE_CONTACT_LIST
, CONNECTION_INTERFACE_REQUESTS
,\
31 CONNECTION_INTERFACE_SIMPLE_PRESENCE
, CHANNEL_INTERFACE_GROUP
, CHANNEL_INTERFACE
32 from telepathy
.constants
import HANDLE_TYPE_LIST
, HANDLE_TYPE_CONTACT
, CONNECTION_PRESENCE_TYPE_AVAILABLE
,\
33 CONNECTION_PRESENCE_TYPE_AWAY
, CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY
, CONNECTION_PRESENCE_TYPE_BUSY
,\
35 from telepathy
.client
.channel
import Channel
36 from telepathy
.client
.conn
import Connection
39 TUBE_SERVICE
= "be.staz.DTubetest"
40 dbus
.mainloop
.glib
.DBusGMainLoop(set_as_default
=True)
42 # TODO: import when tube API is stable
43 CHANNEL_INTERFACE_TUBE
= CHANNEL_INTERFACE
+ ".Interface.Tube.DRAFT"
44 CHANNEL_TYPE_DBUS_TUBE
= CHANNEL_INTERFACE
+ ".Type.DBusTube.DRAFT"
46 TUBE_CHANNEL_STATE_LOCAL_PENDING
= 0
47 TUBE_CHANNEL_STATE_REMOTE_PENDING
= 1
48 TUBE_CHANNEL_STATE_OPEN
= 2
49 TUBE_CHANNEL_STATE_NOT_OFFERED
= 3
51 # List of presences types we consider as online
52 ONLINE_TYPES
= [CONNECTION_PRESENCE_TYPE_AVAILABLE
,
53 CONNECTION_PRESENCE_TYPE_AWAY
,
54 CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY
,
55 CONNECTION_PRESENCE_TYPE_BUSY
]
57 def search_contact_from_id(contact
, conn
):
58 """Return the handle that match the id in given connexion"""
60 if CONNECTION_INTERFACE_REQUESTS
not in conn
:
61 logging
.warning("CONNECTION_INTERFACE_REQUESTS not implemented in %s" % (conn
.service_name
))
65 yours
, path
, props
= conn
[CONNECTION_INTERFACE_REQUESTS
].EnsureChannel({
66 'org.freedesktop.Telepathy.Channel.ChannelType': CHANNEL_TYPE_CONTACT_LIST
,
67 'org.freedesktop.Telepathy.Channel.TargetHandleType': HANDLE_TYPE_LIST
,
68 'org.freedesktop.Telepathy.Channel.TargetID': 'subscribe'})
70 chan
= Channel(conn
.service_name
, path
)
71 contacts
= chan
[CHANNEL_INTERFACE_GROUP
].GetMembers()
72 ids
= conn
.InspectHandles(HANDLE_TYPE_CONTACT
, contacts
)
74 for handle
, id in zip(contacts
, ids
):
78 def contact_is_online(handle
, conn
):
79 """Is contact online?"""
80 presences
= conn
[CONNECTION_INTERFACE_SIMPLE_PRESENCE
].GetPresences([handle
])
81 type, status
, msg
= presences
[handle
]
82 return type in ONLINE_TYPES
84 def offer_tube(conn
, handle
, tube_service
):
85 """Offer a dtube to contact designed by @handle"""
86 # FIXME: check if contact supports our tube
87 yours
, path
, props
= conn
[CONNECTION_INTERFACE_REQUESTS
].EnsureChannel({
88 'org.freedesktop.Telepathy.Channel.ChannelType': CHANNEL_TYPE_DBUS_TUBE
,
89 'org.freedesktop.Telepathy.Channel.TargetHandleType': HANDLE_TYPE_CONTACT
,
90 'org.freedesktop.Telepathy.Channel.TargetHandle': handle
,
91 CHANNEL_TYPE_DBUS_TUBE
+ '.ServiceName': tube_service
})
93 def tube_state_changed_cb(state
):
94 if state
== TUBE_STATE_OPEN
:
95 print "Tube accepted "
97 tube_conn
= dbus
.connection
.Connection(address
)
98 # Create and export the object
99 obj
= ExempleObject(tube_conn
, "/Example")
101 elif state
== TUBE_CHANNEL_STATE_REMOTE_PENDING
:
102 print "Other player is invited, waiting for response..."
104 print "Tube state changed", state
106 def tube_closed_cb():
109 chan
= Channel(conn
.service_name
, path
)
110 chan
[CHANNEL_INTERFACE_TUBE
].connect_to_signal('TubeChannelStateChanged', tube_state_changed_cb
)
111 chan
[CHANNEL_INTERFACE
].connect_to_signal('Closed', tube_closed_cb
)
112 print "Invited contact on %s" % tube_service
113 address
= chan
[CHANNEL_TYPE_DBUS_TUBE
].OfferDBusTube({})
115 def offer_tube_to_contact_cb(watcher
, conn
, contact
, tube_service
):
116 """When a new connection is added, check if @contact is present on and online on it, if found offer him a tube of @tube_service"""
117 #TODO : Remove itself when contact is found.
118 handle
= search_contact_from_id(contact
, conn
)
120 print "Contact %s found in %s" % (contact
, conn
.service_name
)
121 if contact_is_online(handle
, conn
):
122 print "contact online"
123 offer_tube(conn
, handle
, tube_service
)
125 print "Contact %s not found in %s" % (contact
, conn
.service_name
)
127 class Handler(Object
):
128 """Create an Handler service which wait for a connection"""
129 @dbus.service
.method("org.gnome.Empathy.TubeHandler",
130 in_signature
='soouu', out_signature
='')
131 def HandleTube(self
, bus_name
, connection
, channel
, handle_type
, handle
):
132 #We need a try here cause strangly it doesn't display exceptions
134 logging
.info("Handling a new tube!")
135 # TODO: ask to the user if he wants to accept or decline the tube
136 conn
= Connection(bus_name
, connection
)
137 chan
= Channel(bus_name
, channel
)
139 address
= chan
[CHANNEL_TYPE_DBUS_TUBE
].AcceptDBusTube()
140 tube_conn
= dbus
.connection
.Connection(address
)
141 # FIXME This should be in a callback
142 # Get the object from the remote end of the tube
143 obj
= tube_conn
.get_object(object_path
="/Example")
145 # Call a methode on it
146 ret
= obj
.HelloWorld("Hello world")
153 def create_handler(tube_service
):
154 """create a D-Bus handler service,
155 which wait for someone to offer us a tube"""
156 logging
.info("Creating the handler service")
157 # Open the local session bus
158 bus
= dbus
.SessionBus()
159 handler_name
= "org.gnome.Empathy.DTubeHandler.%s" % tube_service
160 handler_path
= "/org/gnome/Empathy/DTubeHandler/%s" % tube_service
.replace('.','/')
161 logging
.info("Bus name : %s" % handler_name
)
162 logging
.info("Bus path : %s" % handler_path
)
163 name
= dbus
.service
.BusName(handler_name
, bus
)
164 handler
= Handler(bus
, handler_path
)
167 def invite_contact(contact
, tube_service
):
168 watcher
= ConnectionWatcher()
169 print "Looking for contact..."
170 watcher
.connect('connection-added', offer_tube_to_contact_cb
, contact
, tube_service
)
172 class ExempleObject(dbus
.service
.Object
):
173 @dbus.service
.method("com.example.SampleInterface",
174 in_signature
='s', out_signature
='as')
175 def HelloWorld(self
, hello_message
):
176 print (str(hello_message
))
177 return ["Hello", " from example-service.py"]
179 if __name__
== "__main__":
181 if sys
.argv
[1] == '--handler':
182 # We must keep a pointer on those otherwhise they get destroyed
183 name
, handler
= create_handler(TUBE_SERVICE
)
185 contact
= sys
.argv
[1]
186 invite_contact(contact
, TUBE_SERVICE
)
188 loop
= gobject
.MainLoop()
191 # vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: