OLD | NEW |
1 #!/usr/bin/python2.4 | 1 #!/usr/bin/python2.4 |
2 # Copyright (c) 2010 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2010 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """A bare-bones and non-compliant XMPP server. | 6 """A bare-bones and non-compliant XMPP server. |
7 | 7 |
8 Just enough of the protocol is implemented to get it to work with | 8 Just enough of the protocol is implemented to get it to work with |
9 Chrome's sync notification system. | 9 Chrome's sync notification system. |
10 """ | 10 """ |
(...skipping 470 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
481 The XMPP server starts accepting connections on the given address | 481 The XMPP server starts accepting connections on the given address |
482 and spawns off XmppConnection objects for each one. | 482 and spawns off XmppConnection objects for each one. |
483 | 483 |
484 Use like so: | 484 Use like so: |
485 | 485 |
486 socket_map = {} | 486 socket_map = {} |
487 xmpp_server = xmppserver.XmppServer(socket_map, ('127.0.0.1', 5222)) | 487 xmpp_server = xmppserver.XmppServer(socket_map, ('127.0.0.1', 5222)) |
488 asyncore.loop(30.0, False, socket_map) | 488 asyncore.loop(30.0, False, socket_map) |
489 """ | 489 """ |
490 | 490 |
| 491 # Used when sending a notification. |
| 492 _NOTIFICATION_STANZA = ParseXml( |
| 493 '<message>' |
| 494 ' <push xmlns="google:push">' |
| 495 ' <data/>' |
| 496 ' </push>' |
| 497 '</message>') |
| 498 |
491 def __init__(self, socket_map, addr): | 499 def __init__(self, socket_map, addr): |
492 asyncore.dispatcher.__init__(self, None, socket_map) | 500 asyncore.dispatcher.__init__(self, None, socket_map) |
493 self.create_socket(socket.AF_INET, socket.SOCK_STREAM) | 501 self.create_socket(socket.AF_INET, socket.SOCK_STREAM) |
494 self.set_reuse_addr() | 502 self.set_reuse_addr() |
495 self.bind(addr) | 503 self.bind(addr) |
496 self.listen(5) | 504 self.listen(5) |
497 self._socket_map = socket_map | 505 self._socket_map = socket_map |
498 self._connections = set() | 506 self._connections = set() |
499 self._handshake_done_connections = set() | 507 self._handshake_done_connections = set() |
| 508 self._notifications_enabled = True |
500 | 509 |
501 def handle_accept(self): | 510 def handle_accept(self): |
502 (sock, addr) = self.accept() | 511 (sock, addr) = self.accept() |
503 xmpp_connection = XmppConnection(sock, self._socket_map, self, addr) | 512 xmpp_connection = XmppConnection(sock, self._socket_map, self, addr) |
504 self._connections.add(xmpp_connection) | 513 self._connections.add(xmpp_connection) |
| 514 # Return the new XmppConnection for testing. |
| 515 return xmpp_connection |
505 | 516 |
506 def close(self): | 517 def close(self): |
507 # A copy is necessary since calling close on each connection | 518 # A copy is necessary since calling close on each connection |
508 # removes it from self._connections. | 519 # removes it from self._connections. |
509 for connection in self._connections.copy(): | 520 for connection in self._connections.copy(): |
510 connection.close() | 521 connection.close() |
511 asyncore.dispatcher.close(self) | 522 asyncore.dispatcher.close(self) |
512 | 523 |
| 524 def EnableNotifications(self): |
| 525 self._notifications_enabled = True |
| 526 |
| 527 def DisableNotifications(self): |
| 528 self._notifications_enabled = False |
| 529 |
| 530 def MakeNotification(self, channel, data): |
| 531 """Makes a notification from the given channel and encoded data. |
| 532 |
| 533 Args: |
| 534 channel: The channel on which to send the notification. |
| 535 data: The notification payload. |
| 536 """ |
| 537 notification_stanza = CloneXml(self._NOTIFICATION_STANZA) |
| 538 push_element = notification_stanza.getElementsByTagName('push')[0] |
| 539 push_element.setAttribute('channel', channel) |
| 540 data_element = push_element.getElementsByTagName('data')[0] |
| 541 encoded_data = base64.b64encode(data) |
| 542 data_text = notification_stanza.parentNode.createTextNode(encoded_data) |
| 543 data_element.appendChild(data_text) |
| 544 return notification_stanza |
| 545 |
| 546 def SendNotification(self, channel, data): |
| 547 """Sends a notification to all connections. |
| 548 |
| 549 Args: |
| 550 channel: The channel on which to send the notification. |
| 551 data: The notification payload. |
| 552 """ |
| 553 notification_stanza = self.MakeNotification(channel, data) |
| 554 self.ForwardNotification(None, notification_stanza) |
| 555 notification_stanza.unlink() |
| 556 |
513 # XmppConnection delegate methods. | 557 # XmppConnection delegate methods. |
514 def OnXmppHandshakeDone(self, xmpp_connection): | 558 def OnXmppHandshakeDone(self, xmpp_connection): |
515 self._handshake_done_connections.add(xmpp_connection) | 559 self._handshake_done_connections.add(xmpp_connection) |
516 | 560 |
517 def OnXmppConnectionClosed(self, xmpp_connection): | 561 def OnXmppConnectionClosed(self, xmpp_connection): |
518 self._connections.discard(xmpp_connection) | 562 self._connections.discard(xmpp_connection) |
519 self._handshake_done_connections.discard(xmpp_connection) | 563 self._handshake_done_connections.discard(xmpp_connection) |
520 | 564 |
521 def ForwardNotification(self, unused_xmpp_connection, notification_stanza): | 565 def ForwardNotification(self, unused_xmpp_connection, notification_stanza): |
522 for connection in self._handshake_done_connections: | 566 if self._notifications_enabled: |
523 print 'Sending notification to %s' % connection | 567 for connection in self._handshake_done_connections: |
524 connection.ForwardNotification(notification_stanza) | 568 print 'Sending notification to %s' % connection |
| 569 connection.ForwardNotification(notification_stanza) |
| 570 else: |
| 571 print 'Notifications disabled; dropping notification' |
OLD | NEW |