Merge lp://qastaging/~cmiller/desktopcouch/kill-asyncore-use-twisted into lp://qastaging/desktopcouch
- kill-asyncore-use-twisted
- Merge into trunk
Proposed by
Chad Miller
Status: | Merged |
---|---|
Approved by: | Elliot Murphy |
Approved revision: | 3 |
Merged at revision: | not available |
Proposed branch: | lp://qastaging/~cmiller/desktopcouch/kill-asyncore-use-twisted |
Merge into: | lp://qastaging/desktopcouch |
Diff against target: | None lines |
To merge this branch: | bzr merge lp://qastaging/~cmiller/desktopcouch/kill-asyncore-use-twisted |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Elliot Murphy (community) | Approve | ||
Review via email:
|
Commit message
[r=statik] Change pairing tool to twisted instead of asyncore.
Description of the change
To post a comment you must log in.
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Chad Miller (cmiller) wrote : | # |
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Elliot Murphy (statik) wrote : | # |
Looks good! please file a bug for the couple of FIXME spots.
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'desktopcouch/pair/couchdb_pairing/dbus_io.py' | |||
2 | --- desktopcouch/pair/couchdb_pairing/dbus_io.py 2009-07-08 17:48:11 +0000 | |||
3 | +++ desktopcouch/pair/couchdb_pairing/dbus_io.py 2009-07-10 22:13:01 +0000 | |||
4 | @@ -26,7 +26,7 @@ | |||
5 | 26 | 26 | ||
6 | 27 | 27 | ||
7 | 28 | def get_local_hostname(): | 28 | def get_local_hostname(): |
9 | 29 | """Get the name of this host, an ASCII-encoded string.""" | 29 | """Get the name of this host, as Unicode host and domain parts.""" |
10 | 30 | bus, server = get_dbus_bus_server() | 30 | bus, server = get_dbus_bus_server() |
11 | 31 | return server.GetHostName(), server.GetDomainName() | 31 | return server.GetHostName(), server.GetDomainName() |
12 | 32 | 32 | ||
13 | 33 | 33 | ||
14 | === modified file 'desktopcouch/pair/couchdb_pairing/network_io.py' | |||
15 | --- desktopcouch/pair/couchdb_pairing/network_io.py 2009-07-08 17:48:11 +0000 | |||
16 | +++ desktopcouch/pair/couchdb_pairing/network_io.py 2009-07-10 22:13:01 +0000 | |||
17 | @@ -15,11 +15,14 @@ | |||
18 | 15 | # along with desktopcouch. If not, see <http://www.gnu.org/licenses/>. | 15 | # along with desktopcouch. If not, see <http://www.gnu.org/licenses/>. |
19 | 16 | """All inter-tool communication.""" | 16 | """All inter-tool communication.""" |
20 | 17 | 17 | ||
21 | 18 | import asyncore | ||
22 | 19 | import socket | ||
23 | 20 | import logging | 18 | import logging |
24 | 21 | import hashlib | 19 | import hashlib |
25 | 22 | 20 | ||
26 | 21 | from twisted.internet import reactor | ||
27 | 22 | from twisted.internet.protocol import ServerFactory, ReconnectingClientFactory | ||
28 | 23 | from twisted.protocols import basic | ||
29 | 24 | from twisted.internet import ssl | ||
30 | 25 | |||
31 | 23 | import dbus | 26 | import dbus |
32 | 24 | 27 | ||
33 | 25 | try: | 28 | try: |
34 | @@ -29,132 +32,81 @@ | |||
35 | 29 | get_remote_hostname = lambda addr: None | 32 | get_remote_hostname = lambda addr: None |
36 | 30 | 33 | ||
37 | 31 | 34 | ||
38 | 32 | # asyncore is ancient. pylint: disable-msg=W0233 | ||
39 | 33 | # asyncore overridden. pylint: disable-msg=C0111 | ||
40 | 34 | |||
41 | 35 | message_terminator = "\n" | ||
42 | 36 | hash = hashlib.sha512 | 35 | hash = hashlib.sha512 |
43 | 37 | 36 | ||
62 | 38 | def receive_up_to_char(s, buffer_list, sentinel=message_terminator): | 37 | |
63 | 39 | message = s.recv(4098) | 38 | class ListenForInvitations(): |
46 | 40 | if len(message) == 0: | ||
47 | 41 | return None | ||
48 | 42 | |||
49 | 43 | buffer_list.append(message) | ||
50 | 44 | total_so_far = "".join(buffer_list) | ||
51 | 45 | if sentinel not in total_so_far: | ||
52 | 46 | return None | ||
53 | 47 | |||
54 | 48 | message, extra = total_so_far.split(sentinel, 1) | ||
55 | 49 | buffer_list[:] = [ extra ] | ||
56 | 50 | |||
57 | 51 | return message | ||
58 | 52 | |||
59 | 53 | |||
60 | 54 | |||
61 | 55 | class ListenForInvitations(asyncore.dispatcher): | ||
64 | 56 | """Narrative "Alice". | 39 | """Narrative "Alice". |
65 | 57 | 40 | ||
66 | 58 | This is the first half of a TCP listening socket. We spawn off | 41 | This is the first half of a TCP listening socket. We spawn off |
67 | 59 | processors when we accept invitation-connections.""" | 42 | processors when we accept invitation-connections.""" |
68 | 60 | 43 | ||
92 | 61 | def __init__(self, get_secret_from_user, on_close, local_map=False): | 44 | def __init__(self, get_secret_from_user, on_close): |
93 | 62 | self.logging = logging.getLogger("ListenForInvitations") | 45 | """Initialize.""" |
94 | 63 | 46 | self.logging = logging.getLogger(self.__class__.__name__) | |
95 | 64 | if local_map: | 47 | |
96 | 65 | if not hasattr(local_map, "get"): | 48 | self.factory = ProcessAnInvitationFactory(get_secret_from_user, |
97 | 66 | local_map = dict() | 49 | on_close) |
98 | 67 | self.__map = local_map | 50 | self.listening_port = reactor.listenTCP(0, self.factory) |
76 | 68 | asyncore.dispatcher.__init__(self, map=self.__map) | ||
77 | 69 | else: | ||
78 | 70 | self.__map = None | ||
79 | 71 | asyncore.dispatcher.__init__(self) | ||
80 | 72 | |||
81 | 73 | self.get_secret_from_user = get_secret_from_user | ||
82 | 74 | self.on_close = on_close | ||
83 | 75 | |||
84 | 76 | self.create_socket(socket.AF_INET, socket.SOCK_STREAM) | ||
85 | 77 | self.bind(("", 0)) | ||
86 | 78 | self.listen(5) | ||
87 | 79 | |||
88 | 80 | def process(self): | ||
89 | 81 | """How we jump into asyncore processing. Call this occasionally.""" | ||
90 | 82 | asyncore.loop(0.01, False, self.__map, 3) | ||
91 | 83 | return True | ||
99 | 84 | 51 | ||
100 | 85 | def get_local_port(self): | 52 | def get_local_port(self): |
101 | 86 | """We created a socket, and the caller needs to know what our port | 53 | """We created a socket, and the caller needs to know what our port |
102 | 87 | number is, so it can advertise it.""" | 54 | number is, so it can advertise it.""" |
103 | 88 | 55 | ||
105 | 89 | bogus_address, port = self.getsockname() | 56 | port = self.listening_port.getHost().port |
106 | 90 | self.logging.info("local port to receive invitations is %s", port) | 57 | self.logging.info("local port to receive invitations is %s", port) |
107 | 91 | return port | 58 | return port |
108 | 92 | 59 | ||
117 | 93 | def handle_accept(self): | 60 | def close(self): |
118 | 94 | s, remote_addr = self.accept() | 61 | """Called from the UI when a window is destroyed and we do not need |
119 | 95 | self.logging.debug("accepted connection from %s", s) | 62 | this connection any more.""" |
120 | 96 | ProcessAnInvitation(self.get_secret_from_user, s, self.on_close, | 63 | self.listening_port.stopListening() |
121 | 97 | local_map=self.__map) | 64 | |
122 | 98 | 65 | ||
123 | 99 | 66 | class ProcessAnInvitationProtocol(basic.LineReceiver): | |
116 | 100 | class ProcessAnInvitation(asyncore.dispatcher_with_send): | ||
124 | 101 | """Narrative "Alice". | 67 | """Narrative "Alice". |
125 | 102 | 68 | ||
126 | 103 | Listen for messages, and when we receive one, call the display callback | 69 | Listen for messages, and when we receive one, call the display callback |
127 | 104 | function with the inviter details plus a key.""" | 70 | function with the inviter details plus a key.""" |
128 | 105 | 71 | ||
138 | 106 | def __init__(self, get_secret_from_user, sock, on_close, local_map=False): | 72 | def __init__(self): |
139 | 107 | self.logging = logging.getLogger("ProcessAnInvitation") | 73 | """Initialize.""" |
140 | 108 | 74 | self.logging = logging.getLogger(self.__class__.__name__) | |
132 | 109 | if local_map: | ||
133 | 110 | assert hasattr(local_map, "get") | ||
134 | 111 | asyncore.dispatcher_with_send.__init__(self, sock, map=local_map) | ||
135 | 112 | else: | ||
136 | 113 | asyncore.dispatcher_with_send.__init__(self, sock) | ||
137 | 114 | |||
141 | 115 | self.expected_hash = None | 75 | self.expected_hash = None |
142 | 116 | self.public_seed = None | 76 | self.public_seed = None |
143 | 117 | 77 | ||
162 | 118 | self.get_secret_from_user = get_secret_from_user | 78 | def connectionMade(self): |
163 | 119 | self.on_close = on_close | 79 | """Called when a connection is made. No obligation here.""" |
164 | 120 | self.recv_buffers = [] | 80 | basic.LineReceiver.connectionMade(self) |
165 | 121 | self.logging.debug("initialized") | 81 | |
166 | 122 | 82 | def connectionLost(self): | |
167 | 123 | def handle_connect(self): | 83 | """Called when a connection is lost.""" |
168 | 124 | self.logging.debug("connecting") | 84 | self.logging.debug("connection lost") |
169 | 125 | 85 | basic.LineReceiver.connectionLost(self) | |
170 | 126 | def handle_close(self): | 86 | |
171 | 127 | self.logging.debug("closing") | 87 | def lineReceived(self, message): |
172 | 128 | self.on_close() | 88 | """Handler for receipt of a message from the Bob end.""" |
155 | 129 | self.close() | ||
156 | 130 | |||
157 | 131 | def handle_read(self): | ||
158 | 132 | message = receive_up_to_char(self, self.recv_buffers) | ||
159 | 133 | if message is None: | ||
160 | 134 | return | ||
161 | 135 | |||
173 | 136 | h = hash() | 89 | h = hash() |
174 | 137 | digest_nybble_count = h.digest_size * 2 | 90 | digest_nybble_count = h.digest_size * 2 |
175 | 138 | self.expected_hash = message[0:digest_nybble_count] | 91 | self.expected_hash = message[0:digest_nybble_count] |
176 | 139 | self.public_seed = message[digest_nybble_count:] | 92 | self.public_seed = message[digest_nybble_count:] |
178 | 140 | self.get_secret_from_user(self.addr[0], self.check_secret_from_user, | 93 | self.factory.get_secret_from_user(self.transport.getPeer().host, |
179 | 94 | self.check_secret_from_user, | ||
180 | 141 | self.send_secret_to_remote) | 95 | self.send_secret_to_remote) |
181 | 142 | 96 | ||
182 | 143 | def send_secret_to_remote(self, secret_message): | 97 | def send_secret_to_remote(self, secret_message): |
183 | 144 | """A callback for the invitation protocol to start a new phase | 98 | """A callback for the invitation protocol to start a new phase |
184 | 145 | involving the other end getting the hash-digest of the public | 99 | involving the other end getting the hash-digest of the public |
185 | 146 | seed and a secret we receive as a parameter.""" | 100 | seed and a secret we receive as a parameter.""" |
186 | 147 | |||
187 | 148 | h = hash() | 101 | h = hash() |
188 | 149 | h.update(self.public_seed) | 102 | h.update(self.public_seed) |
189 | 150 | h.update(secret_message) | 103 | h.update(secret_message) |
191 | 151 | self.send(h.hexdigest() + message_terminator) | 104 | self.sendLine(h.hexdigest()) |
192 | 152 | 105 | ||
193 | 153 | def check_secret_from_user(self, secret_message): | 106 | def check_secret_from_user(self, secret_message): |
194 | 154 | """A callback for the invitation protocol to verify the secret | 107 | """A callback for the invitation protocol to verify the secret |
195 | 155 | that the user gives, against the hash we received over the | 108 | that the user gives, against the hash we received over the |
196 | 156 | network.""" | 109 | network.""" |
197 | 157 | |||
198 | 158 | h = hash() | 110 | h = hash() |
199 | 159 | h.update(secret_message) | 111 | h.update(secret_message) |
200 | 160 | digest = h.hexdigest() | 112 | digest = h.hexdigest() |
201 | @@ -163,89 +115,111 @@ | |||
202 | 163 | h = hash() | 115 | h = hash() |
203 | 164 | h.update(self.public_seed) | 116 | h.update(self.public_seed) |
204 | 165 | h.update(secret_message) | 117 | h.update(secret_message) |
206 | 166 | self.send(h.hexdigest() + message_terminator) | 118 | self.sendLine(h.hexdigest()) |
207 | 167 | 119 | ||
208 | 168 | self.logging.debug("User knew secret!") | 120 | self.logging.debug("User knew secret!") |
209 | 121 | |||
210 | 122 | self.transport.loseConnection() | ||
211 | 169 | return True | 123 | return True |
212 | 170 | 124 | ||
213 | 171 | self.logging.info("User secret %r is wrong.", secret_message) | 125 | self.logging.info("User secret %r is wrong.", secret_message) |
214 | 172 | return False | 126 | return False |
215 | 173 | 127 | ||
238 | 174 | # def handle_error(self, *args): | 128 | |
239 | 175 | # self.logging.error("%s", args) | 129 | class ProcessAnInvitationFactory(ServerFactory): |
240 | 176 | 130 | """Hold configuration values for all the connections, and fire off a | |
241 | 177 | 131 | protocol to handle the data sent and received.""" | |
242 | 178 | class SendInvitation(asyncore.dispatcher_with_send): | 132 | |
243 | 179 | """Narrative "Bob". | 133 | protocol = ProcessAnInvitationProtocol |
244 | 180 | """ | 134 | |
245 | 181 | 135 | def __init__(self, get_secret_from_user, on_close): | |
246 | 182 | def __init__(self, host, port, auth_complete_cb, secret_message, | 136 | self.logging = logging.getLogger(self.__class__.__name__) |
247 | 183 | public_seed, on_close, local_map=False): | 137 | self.get_secret_from_user = get_secret_from_user |
226 | 184 | self.logging = logging.getLogger("SendInvitation") | ||
227 | 185 | |||
228 | 186 | if local_map: | ||
229 | 187 | if not hasattr(local_map, "get"): | ||
230 | 188 | local_map = dict() | ||
231 | 189 | self.__map = local_map | ||
232 | 190 | asyncore.dispatcher_with_send.__init__(self, map=self.__map) | ||
233 | 191 | else: | ||
234 | 192 | self.__map = None | ||
235 | 193 | asyncore.dispatcher_with_send.__init__(self) | ||
236 | 194 | |||
237 | 195 | self.auth_complete_cb = auth_complete_cb | ||
248 | 196 | self.on_close = on_close | 138 | self.on_close = on_close |
264 | 197 | 139 | ||
265 | 198 | self.create_socket(socket.AF_INET, socket.SOCK_STREAM) | 140 | |
266 | 199 | self.connect((host, port)) | 141 | class SendInvitationProtocol(basic.LineReceiver): |
267 | 200 | 142 | """Narrative "Bob".""" | |
268 | 201 | self.recv_buffers = [] | 143 | |
269 | 202 | 144 | def __init__(self): | |
270 | 203 | self.logging.debug("secret is %r", secret_message) | 145 | """Initialize.""" |
271 | 204 | 146 | self.logging = logging.getLogger(self.__class__.__name__) | |
272 | 205 | h = hash() | 147 | self.logging.debug("initialized") |
273 | 206 | h.update(secret_message) | 148 | |
274 | 207 | self.send(h.hexdigest() + public_seed + message_terminator) | 149 | def connectionMade(self): |
275 | 208 | 150 | """Fire when a connection is made to the listener. No obligation | |
276 | 209 | h = hash() | 151 | here.""" |
277 | 210 | h.update(public_seed) | 152 | self.logging.debug("connection made") |
278 | 211 | h.update(secret_message) | 153 | |
279 | 154 | h = hash() | ||
280 | 155 | h.update(self.factory.secret_message) | ||
281 | 156 | self.sendLine(h.hexdigest() + self.factory.public_seed) | ||
282 | 157 | |||
283 | 158 | h = hash() | ||
284 | 159 | h.update(self.factory.public_seed) | ||
285 | 160 | h.update(self.factory.secret_message) | ||
286 | 212 | self.expected_hash_of_secret = h.hexdigest() | 161 | self.expected_hash_of_secret = h.hexdigest() |
287 | 213 | 162 | ||
301 | 214 | def process(self): | 163 | |
302 | 215 | """How we jump into asyncore processing. Call this occasionally.""" | 164 | def lineReceived(self, message): |
303 | 216 | asyncore.loop(0.01, False, self.__map, 3) | 165 | """Handler for receipt of a message from the Alice end.""" |
291 | 217 | return True | ||
292 | 218 | |||
293 | 219 | def handle_connect(self): | ||
294 | 220 | self.logging.debug("connecting") | ||
295 | 221 | |||
296 | 222 | def handle_read(self): | ||
297 | 223 | message = receive_up_to_char(self, self.recv_buffers) | ||
298 | 224 | if message is None: | ||
299 | 225 | return | ||
300 | 226 | self.logging.debug("received %r", message) | ||
304 | 227 | if message == self.expected_hash_of_secret: | 166 | if message == self.expected_hash_of_secret: |
306 | 228 | remote_host = self.socket.getpeername()[0] | 167 | remote_host = self.transport.getPeer().host |
307 | 229 | try: | 168 | try: |
308 | 230 | remote_hostname = get_remote_hostname(remote_host) | 169 | remote_hostname = get_remote_hostname(remote_host) |
309 | 231 | except dbus.exceptions.DBusException: | 170 | except dbus.exceptions.DBusException: |
310 | 232 | remote_hostname = None | 171 | remote_hostname = None |
312 | 233 | self.auth_complete_cb(remote_hostname, "(port)", "(info)") | 172 | self.factory.auth_complete_cb(remote_hostname, "(port)", "(info)") |
313 | 173 | self.transport.loseConnection() | ||
314 | 234 | else: | 174 | else: |
315 | 235 | self.logging.warn("Expected %r from invitation.", | 175 | self.logging.warn("Expected %r from invitation.", |
316 | 236 | self.expected_hash_of_secret) | 176 | self.expected_hash_of_secret) |
317 | 237 | 177 | ||
332 | 238 | def handle_close(self): | 178 | def connectionLost(self): |
333 | 239 | self.logging.debug("closing") | 179 | """When a connected socked is broken, this is fired.""" |
334 | 240 | self.on_close() | 180 | self.logging.info("connection lost.") |
335 | 241 | self.close() | 181 | basic.LineReceiver.connectionLost(self) |
336 | 242 | 182 | ||
337 | 243 | # def handle_error(self, *args): | 183 | |
338 | 244 | # self.logging.error("%s", args) | 184 | class SendInvitationFactory(ReconnectingClientFactory): |
339 | 245 | 185 | """Hold configuration values for all the connections, and fire off a | |
340 | 246 | 186 | protocol to handle the data sent and received.""" | |
341 | 247 | 187 | ||
342 | 248 | if __name__ == "__main__": | 188 | protocol = SendInvitationProtocol |
343 | 249 | logging.basicConfig(level=logging.DEBUG, format= | 189 | |
344 | 250 | "%(asctime)s [%(process)d] %(levelname)s - %(name)s %(message)s") | 190 | def __init__(self, auth_complete_cb, secret_message, public_seed, |
345 | 251 | 191 | on_close): | |
346 | 192 | self.logging = logging.getLogger(self.__class__.__name__) | ||
347 | 193 | self.auth_complete_cb = auth_complete_cb | ||
348 | 194 | self.secret_message = secret_message | ||
349 | 195 | self.public_seed = public_seed | ||
350 | 196 | self.on_close = on_close | ||
351 | 197 | self.logging.debug("initialized") | ||
352 | 198 | |||
353 | 199 | def close(self): | ||
354 | 200 | """Called from the UI when a window is destroyed and we do not need | ||
355 | 201 | this connection any more.""" | ||
356 | 202 | self.logging.warn("close not handled properly") # FIXME | ||
357 | 203 | |||
358 | 204 | def clientConnectionFailed(self, connector, reason): | ||
359 | 205 | """When we fail to connect to the listener, this is fired.""" | ||
360 | 206 | self.logging.warn("connect failed. %s", reason) | ||
361 | 207 | ReconnectingClientFactory.clientConnectionFailed(self, connector, | ||
362 | 208 | reason) | ||
363 | 209 | |||
364 | 210 | def clientConnectionLost(self, connector, reason): | ||
365 | 211 | """When a connected socked is broken, this is fired.""" | ||
366 | 212 | self.logging.info("connection lost. %s", reason) | ||
367 | 213 | ReconnectingClientFactory.clientConnectionLost(self, connector, reason) | ||
368 | 214 | |||
369 | 215 | |||
370 | 216 | def start_send_invitation(host, port, auth_complete_cb, secret_message, | ||
371 | 217 | public_seed, on_close): | ||
372 | 218 | """Instantiate the factory to hold configuration data about sending an | ||
373 | 219 | invitation and let the reactor add it to its event-handling loop by way of | ||
374 | 220 | starting a TCP connection.""" | ||
375 | 221 | factory = SendInvitationFactory(auth_complete_cb, secret_message, | ||
376 | 222 | public_seed, on_close) | ||
377 | 223 | reactor.connectTCP(host, port, factory) | ||
378 | 224 | |||
379 | 225 | return factory | ||
380 | 252 | 226 | ||
381 | === modified file 'desktopcouch/pair/pair.py' | |||
382 | --- desktopcouch/pair/pair.py 2009-07-08 17:48:11 +0000 | |||
383 | +++ desktopcouch/pair/pair.py 2009-07-10 22:13:01 +0000 | |||
384 | @@ -52,6 +52,11 @@ | |||
385 | 52 | import random | 52 | import random |
386 | 53 | import cgi | 53 | import cgi |
387 | 54 | 54 | ||
388 | 55 | from twisted.internet import gtk2reactor | ||
389 | 56 | gtk2reactor.install() | ||
390 | 57 | from twisted.internet.reactor import run as run_program | ||
391 | 58 | from twisted.internet.reactor import stop as stop_program | ||
392 | 59 | |||
393 | 55 | import pygtk | 60 | import pygtk |
394 | 56 | pygtk.require('2.0') | 61 | pygtk.require('2.0') |
395 | 57 | import gtk | 62 | import gtk |
396 | @@ -64,7 +69,6 @@ | |||
397 | 64 | discovery_tool_version = "1" | 69 | discovery_tool_version = "1" |
398 | 65 | 70 | ||
399 | 66 | 71 | ||
400 | 67 | |||
401 | 68 | def generate_secret(length=7): | 72 | def generate_secret(length=7): |
402 | 69 | """Create a secret that is easy to write and read. We hate ambiguity and | 73 | """Create a secret that is easy to write and read. We hate ambiguity and |
403 | 70 | errors.""" | 74 | errors.""" |
404 | @@ -124,7 +128,6 @@ | |||
405 | 124 | 128 | ||
406 | 125 | def destroy(self, widget, data=None): | 129 | def destroy(self, widget, data=None): |
407 | 126 | """The window is destroyed.""" | 130 | """The window is destroyed.""" |
408 | 127 | gobject.source_remove(self.inviter_loop) | ||
409 | 128 | self.inviter.close() | 131 | self.inviter.close() |
410 | 129 | 132 | ||
411 | 130 | def auth_completed(self, remote_host, remote_port, remote_info): | 133 | def auth_completed(self, remote_host, remote_port, remote_info): |
412 | @@ -154,10 +157,9 @@ | |||
413 | 154 | self.secret_message = secret_message | 157 | self.secret_message = secret_message |
414 | 155 | self.public_seed = generate_secret() | 158 | self.public_seed = generate_secret() |
415 | 156 | 159 | ||
417 | 157 | self.inviter = network_io.SendInvitation(hostname, port, | 160 | self.inviter = network_io.start_send_invitation(hostname, port, |
418 | 158 | self.auth_completed, self.secret_message, self.public_seed, | 161 | self.auth_completed, self.secret_message, self.public_seed, |
421 | 159 | self.on_close, local_map=False) | 162 | self.on_close) |
420 | 160 | self.inviter_loop = gobject.idle_add(self.inviter.process) | ||
422 | 161 | 163 | ||
423 | 162 | top_vbox = gtk.VBox() | 164 | top_vbox = gtk.VBox() |
424 | 163 | self.window.add(top_vbox) | 165 | self.window.add(top_vbox) |
425 | @@ -173,6 +175,12 @@ | |||
426 | 173 | top_vbox.pack_start(text, False, False, 10) | 175 | top_vbox.pack_start(text, False, False, 10) |
427 | 174 | text.show() | 176 | text.show() |
428 | 175 | 177 | ||
429 | 178 | cancel_button = gtk.Button(stock=gtk.STOCK_CANCEL) | ||
430 | 179 | cancel_button.set_border_width(3) | ||
431 | 180 | cancel_button.connect("clicked", lambda widget: self.window.destroy()) | ||
432 | 181 | top_vbox.pack_end(cancel_button, False, False, 10) | ||
433 | 182 | cancel_button.show() | ||
434 | 183 | |||
435 | 176 | self.window.show_all() | 184 | self.window.show_all() |
436 | 177 | 185 | ||
437 | 178 | 186 | ||
438 | @@ -293,8 +301,6 @@ | |||
439 | 293 | if self.advertisement is not None: | 301 | if self.advertisement is not None: |
440 | 294 | self.advertisement.die() | 302 | self.advertisement.die() |
441 | 295 | self.listener.close() | 303 | self.listener.close() |
442 | 296 | if self.listener_loop is not None: | ||
443 | 297 | gobject.source_remove(self.listener_loop) | ||
444 | 298 | 304 | ||
445 | 299 | def receive_invitation_challenge(self, remote_address, is_secret_valid, | 305 | def receive_invitation_challenge(self, remote_address, is_secret_valid, |
446 | 300 | send_secret): | 306 | send_secret): |
447 | @@ -317,9 +323,7 @@ | |||
448 | 317 | 323 | ||
449 | 318 | self.listener = network_io.ListenForInvitations( | 324 | self.listener = network_io.ListenForInvitations( |
450 | 319 | self.receive_invitation_challenge, | 325 | self.receive_invitation_challenge, |
454 | 320 | lambda: self.window.destroy(), | 326 | lambda: self.window.destroy()) |
452 | 321 | local_map=False) | ||
453 | 322 | self.listener_loop = gobject.idle_add(self.listener.process) | ||
455 | 323 | 327 | ||
456 | 324 | listen_port = self.listener.get_local_port() | 328 | listen_port = self.listener.get_local_port() |
457 | 325 | 329 | ||
458 | @@ -433,7 +437,7 @@ | |||
459 | 433 | 437 | ||
460 | 434 | def destroy(self, widget, data=None): | 438 | def destroy(self, widget, data=None): |
461 | 435 | """The window was destroyed.""" | 439 | """The window was destroyed.""" |
463 | 436 | gtk.main_quit() | 440 | stop_program() |
464 | 437 | 441 | ||
465 | 438 | def create_pick_pane(self, container): | 442 | def create_pick_pane(self, container): |
466 | 439 | """Set up the pane that contains what's necessary to choose an | 443 | """Set up the pane that contains what's necessary to choose an |
467 | @@ -633,6 +637,7 @@ | |||
468 | 633 | def main(args): | 637 | def main(args): |
469 | 634 | """Start execution.""" | 638 | """Start execution.""" |
470 | 635 | global pick_or_listen # pylint: disable-msg=W0601 | 639 | global pick_or_listen # pylint: disable-msg=W0601 |
471 | 640 | |||
472 | 636 | logging.basicConfig(level=logging.DEBUG, format= | 641 | logging.basicConfig(level=logging.DEBUG, format= |
473 | 637 | "%(asctime)s [%(process)d] %(name)s:%(levelname)s: %(message)s") | 642 | "%(asctime)s [%(process)d] %(name)s:%(levelname)s: %(message)s") |
474 | 638 | 643 | ||
475 | @@ -641,7 +646,7 @@ | |||
476 | 641 | try: | 646 | try: |
477 | 642 | logging.debug("starting couchdb pairing tool") | 647 | logging.debug("starting couchdb pairing tool") |
478 | 643 | pick_or_listen = PickOrListen() | 648 | pick_or_listen = PickOrListen() |
480 | 644 | return gtk.main() | 649 | return run_program() |
481 | 645 | finally: | 650 | finally: |
482 | 646 | logging.debug("exiting couchdb pairing tool") | 651 | logging.debug("exiting couchdb pairing tool") |
483 | 647 | 652 | ||
484 | 648 | 653 | ||
485 | === modified file 'desktopcouch/pair/tests/test_network_io.py' | |||
486 | --- desktopcouch/pair/tests/test_network_io.py 2009-07-08 17:48:11 +0000 | |||
487 | +++ desktopcouch/pair/tests/test_network_io.py 2009-07-10 22:13:01 +0000 | |||
488 | @@ -14,15 +14,30 @@ | |||
489 | 14 | # You should have received a copy of the GNU Lesser General Public License | 14 | # You should have received a copy of the GNU Lesser General Public License |
490 | 15 | # along with desktopcouch. If not, see <http://www.gnu.org/licenses/>. | 15 | # along with desktopcouch. If not, see <http://www.gnu.org/licenses/>. |
491 | 16 | 16 | ||
492 | 17 | |||
493 | 18 | import pygtk | ||
494 | 19 | pygtk.require('2.0') | ||
495 | 20 | import gtk | ||
496 | 21 | |||
497 | 22 | from twisted.internet import gtk2reactor | ||
498 | 23 | gtk2reactor.install() | ||
499 | 24 | from twisted.internet import reactor, task | ||
500 | 25 | |||
501 | 26 | |||
502 | 17 | if __name__ == "__main__": | 27 | if __name__ == "__main__": |
503 | 18 | import sys, os | 28 | import sys, os |
504 | 19 | sys.path.append(os.pardir) | 29 | sys.path.append(os.pardir) |
506 | 20 | from couchdb_pairing.network_io import SendInvitation, ListenForInvitations | 30 | from couchdb_pairing.network_io import start_send_invitation, ListenForInvitations |
507 | 31 | from couchdb_pairing.dbus_io import get_local_hostname | ||
508 | 21 | else: | 32 | else: |
510 | 22 | from ..couchdb_pairing.network_io import SendInvitation, ListenForInvitations | 33 | from ..couchdb_pairing.network_io import start_send_invitation, ListenForInvitations |
511 | 34 | from ..couchdb_pairing.dbus_io import get_local_hostname | ||
512 | 23 | 35 | ||
513 | 24 | import unittest | 36 | import unittest |
514 | 25 | 37 | ||
515 | 38 | local_hostname = ".".join(get_local_hostname()) | ||
516 | 39 | |||
517 | 40 | |||
518 | 26 | class TestNetworkIO(unittest.TestCase): | 41 | class TestNetworkIO(unittest.TestCase): |
519 | 27 | 42 | ||
520 | 28 | def setUp(self): | 43 | def setUp(self): |
521 | @@ -47,11 +62,9 @@ | |||
522 | 47 | listener_complete_auth() | 62 | listener_complete_auth() |
523 | 48 | 63 | ||
524 | 49 | def listener_close_socket(): | 64 | def listener_close_socket(): |
525 | 50 | print "LISTNER!\n\n" | ||
526 | 51 | self._listener_socket_state = "closed" | 65 | self._listener_socket_state = "closed" |
527 | 52 | 66 | ||
528 | 53 | def inviter_close_socket(): | 67 | def inviter_close_socket(): |
529 | 54 | print "INVITER!\n\n" | ||
530 | 55 | self._inviter_socket_state = "closed" | 68 | self._inviter_socket_state = "closed" |
531 | 56 | 69 | ||
532 | 57 | def inviter_complete_auth(a, b, c): | 70 | def inviter_complete_auth(a, b, c): |
533 | @@ -67,16 +80,21 @@ | |||
534 | 67 | 80 | ||
535 | 68 | def inviter_display_message(*args): | 81 | def inviter_display_message(*args): |
536 | 69 | """Show message to user.""" | 82 | """Show message to user.""" |
538 | 70 | print "display message from inviter:", args | 83 | logging.info("display message from inviter: %s", args) |
539 | 71 | 84 | ||
541 | 72 | self.inviter = SendInvitation("localhost", listener_port, | 85 | self.inviter = start_send_invitation(local_hostname, listener_port, |
542 | 73 | inviter_complete_auth, secret, "seed", inviter_close_socket) | 86 | inviter_complete_auth, secret, "seed", inviter_close_socket) |
543 | 74 | 87 | ||
545 | 75 | for i in xrange(50): | 88 | def exit_on_success(): |
546 | 76 | if self._listener_auth_completed and self._inviter_auth_completed: | 89 | if self._listener_auth_completed and self._inviter_auth_completed: |
550 | 77 | break | 90 | reactor.stop() |
551 | 78 | self.listener.process() | 91 | task.LoopingCall(exit_on_success).start(1.0) |
552 | 79 | self.inviter.process() | 92 | |
553 | 93 | def exit_on_timeout(): | ||
554 | 94 | reactor.stop() | ||
555 | 95 | reactor.callLater(30, exit_on_timeout) | ||
556 | 96 | |||
557 | 97 | reactor.run() | ||
558 | 80 | 98 | ||
559 | 81 | self.assertTrue(self._listener_auth_completed) | 99 | self.assertTrue(self._listener_auth_completed) |
560 | 82 | self.assertTrue(self._inviter_auth_completed) | 100 | self.assertTrue(self._inviter_auth_completed) |
561 | @@ -117,14 +135,12 @@ | |||
562 | 117 | 135 | ||
563 | 118 | def inviter_display_message(*args): | 136 | def inviter_display_message(*args): |
564 | 119 | """Show message to user.""" | 137 | """Show message to user.""" |
566 | 120 | print "display message from inviter:", args | 138 | logging.info("display message from inviter: %s", args) |
567 | 121 | 139 | ||
569 | 122 | self.inviter = SendInvitation("localhost", listener_port, | 140 | self.inviter = start_send_invitation(local_hostname, listener_port, |
570 | 123 | inviter_complete_auth, secret, "seed", inviter_close_socket) | 141 | inviter_complete_auth, secret, "seed", inviter_close_socket) |
571 | 124 | 142 | ||
575 | 125 | for i in xrange(50): | 143 | # FIXME |
573 | 126 | self.listener.process() | ||
574 | 127 | self.inviter.process() | ||
576 | 128 | 144 | ||
577 | 129 | self.assertFalse(self._listener_auth_completed) | 145 | self.assertFalse(self._listener_auth_completed) |
578 | 130 | self.assertFalse(self._inviter_auth_completed) | 146 | self.assertFalse(self._inviter_auth_completed) |
579 | @@ -147,11 +163,9 @@ | |||
580 | 147 | 163 | ||
581 | 148 | 164 | ||
582 | 149 | def listener_close_socket(): | 165 | def listener_close_socket(): |
583 | 150 | print "closed listener" | ||
584 | 151 | self._listener_socket_state = "closed" | 166 | self._listener_socket_state = "closed" |
585 | 152 | 167 | ||
586 | 153 | def inviter_close_socket(): | 168 | def inviter_close_socket(): |
587 | 154 | print "closed inviter" | ||
588 | 155 | self._inviter_socket_state = "closed" | 169 | self._inviter_socket_state = "closed" |
589 | 156 | 170 | ||
590 | 157 | def inviter_complete_auth(a, b, c): | 171 | def inviter_complete_auth(a, b, c): |
591 | @@ -167,16 +181,12 @@ | |||
592 | 167 | 181 | ||
593 | 168 | def inviter_display_message(*args): | 182 | def inviter_display_message(*args): |
594 | 169 | """Show message to user.""" | 183 | """Show message to user.""" |
596 | 170 | print "display message from inviter:", args | 184 | logging.info("display message from inviter: %s", args) |
597 | 171 | 185 | ||
599 | 172 | self.inviter = SendInvitation("localhost", listener_port, | 186 | self.inviter = start_send_invitation(local_hostname, listener_port, |
600 | 173 | inviter_complete_auth, secret, "seed", inviter_close_socket) | 187 | inviter_complete_auth, secret, "seed", inviter_close_socket) |
601 | 174 | 188 | ||
607 | 175 | for i in xrange(500): | 189 | # FIXME |
603 | 176 | if self._inviter_socket_state == "closed" and self._listener_socket_state == "closed": | ||
604 | 177 | break | ||
605 | 178 | self.listener.process() | ||
606 | 179 | self.inviter.process() | ||
608 | 180 | 190 | ||
609 | 181 | self.assertFalse(self._listener_auth_completed) | 191 | self.assertFalse(self._listener_auth_completed) |
610 | 182 | self.assertFalse(self._inviter_auth_completed) | 192 | self.assertFalse(self._inviter_auth_completed) |
611 | @@ -185,8 +195,6 @@ | |||
612 | 185 | self.assertEqual(self._inviter_socket_state, "closed") | 195 | self.assertEqual(self._inviter_socket_state, "closed") |
613 | 186 | 196 | ||
614 | 187 | 197 | ||
615 | 188 | |||
616 | 189 | |||
617 | 190 | if __name__ == "__main__": | 198 | if __name__ == "__main__": |
618 | 191 | import logging | 199 | import logging |
619 | 192 | logging.basicConfig(level=logging.ERROR) | 200 | logging.basicConfig(level=logging.ERROR) |
I, for one, welcome our new twisted overlords.