divmod-dev team mailing list archive
-
divmod-dev team
-
Mailing list archive
-
Message #00428
[Merge] lp:~cyli/divmod.org/remove-vertex into lp:divmod.org
cyli has proposed merging lp:~cyli/divmod.org/remove-vertex into lp:divmod.org.
Requested reviews:
Divmod-dev (divmod-dev)
For more details, see:
https://code.launchpad.net/~cyli/divmod.org/remove-vertex/+merge/171717
Proposing to remove Vertex from the divmod repos. I found only one branch with a merge proposal for it:
https://code.launchpad.net/~alfred-54/divmod.org/divmod.org/+merge/158833
I've ported trunk as well as alfred-54's branch to https://github.com/cyli/vertex, which I will then transfer to the twistedmatrix organization after/if this branch is merged into divmod.org repo.
--
https://code.launchpad.net/~cyli/divmod.org/remove-vertex/+merge/171717
Your team Divmod-dev is requested to review the proposed merge of lp:~cyli/divmod.org/remove-vertex into lp:divmod.org.
=== modified file 'Divmod.pth'
--- Divmod.pth 2013-01-02 10:08:46 +0000
+++ Divmod.pth 2013-06-27 06:07:28 +0000
@@ -1,4 +1,4 @@
-# -*- test-case-name: axiom,combinator,epsilon,xmantissa,nevow,formless,xquotient,reverend,sine,vertex,hyperbola,imaginary,examplegame -*-
+# -*- test-case-name: axiom,combinator,epsilon,xmantissa,nevow,formless,xquotient,reverend,sine,hyperbola,imaginary,examplegame -*-
Axiom
Combinator
Epsilon
@@ -7,7 +7,6 @@
Quotient
Reverend
Sine
-Vertex
Hyperbola
Imaginary
Imaginary/ExampleGame
=== removed directory 'Vertex'
=== removed file 'Vertex/DEPS.txt'
--- Vertex/DEPS.txt 2006-06-14 11:54:41 +0000
+++ Vertex/DEPS.txt 1970-01-01 00:00:00 +0000
@@ -1,5 +0,0 @@
-Python 2.4
-Twisted 2.4.0
-PyOpenSSL 0.6
-OpenSSL 0.9.7
-Epsilon 0.5.0
=== removed file 'Vertex/LICENSE'
--- Vertex/LICENSE 2005-12-10 22:31:51 +0000
+++ Vertex/LICENSE 1970-01-01 00:00:00 +0000
@@ -1,20 +0,0 @@
-Copyright (c) 2005 Divmod Inc.
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
=== removed file 'Vertex/MANIFEST.in'
--- Vertex/MANIFEST.in 2006-06-14 11:54:41 +0000
+++ Vertex/MANIFEST.in 1970-01-01 00:00:00 +0000
@@ -1,4 +0,0 @@
-include LICENSE
-include NAME.txt
-include DEPS.txt
-include doc/q2q-standalone.tac
=== removed file 'Vertex/NAME.txt'
--- Vertex/NAME.txt 2005-08-27 23:09:07 +0000
+++ Vertex/NAME.txt 1970-01-01 00:00:00 +0000
@@ -1,10 +0,0 @@
-
-See: http://mathworld.wolfram.com/Vertex.html
-
-A vertex in mathematics is a location where two or more lines or edges meet.
-
-Divmod Vertex is an implementation of and interface to the Q2Q protocol. It is
-named for a vertext because it provides a way for peers on the internet to
-establish connections with each other, e.g. to make their connection lines
-meet, regardless of intermediary interference such as network address
-translators and lack of naming information.
=== removed file 'Vertex/NEWS.txt'
--- Vertex/NEWS.txt 2009-11-30 01:08:55 +0000
+++ Vertex/NEWS.txt 1970-01-01 00:00:00 +0000
@@ -1,20 +0,0 @@
-0.3.0 (2009-11-25):
- - Remove use of deprecated Twisted APIs from the test suite and improve
- some error handling as a necessary consequence.
- - Use twisted.internet.ssl instead of epsilon.sslverify.
- - Remove an implementation of deferLater.
-
-0.2.0 (2006-06-12):
- - Moved JUICE implementation into Epsilon.
- - Removed dependency on Nevow's formless.
- - Clarify licensing terms.
- - Fix bugs on 64-bit platforms.
- - removed buggy legacy non-TLS options which would break negotiation with
- OpenSSL 0.9.8a.
- - Deprecated twisted test APIs removed.
- - First phase of integration with twisted.cred; vertex endpoints can now be
- authenticated against a Twisted UsernamePassword cred authenticator.
-
-0.1.0 (2005-10-10):
-
- - Initial release.
=== removed file 'Vertex/README.txt'
--- Vertex/README.txt 2006-06-14 11:54:41 +0000
+++ Vertex/README.txt 1970-01-01 00:00:00 +0000
@@ -1,12 +0,0 @@
-
-Divmod Vertex
-=============
-
-Divmod Vertex is the first implementation of the Q2Q protocol, which is a
-peer-to-peer communication protocol for establishing stream-based communication
-between named endpoints.
-
-It is also a P2P application client and server platform in the early stages of
-development. It is currently quite usable for knocking holes in firewalls, but
-requires some polish to really be usable.
-
=== removed directory 'Vertex/bin'
=== removed file 'Vertex/bin/gvertex'
--- Vertex/bin/gvertex 2006-03-08 04:10:37 +0000
+++ Vertex/bin/gvertex 1970-01-01 00:00:00 +0000
@@ -1,7 +0,0 @@
-#!/usr/bin/python
-# Copyright 2005 Divmod, Inc. See LICENSE file for details
-
-from vertex.gtk2hack import main
-
-if __name__ == '__main__':
- main()
=== removed file 'Vertex/bin/vertex'
--- Vertex/bin/vertex 2005-08-10 22:20:03 +0000
+++ Vertex/bin/vertex 1970-01-01 00:00:00 +0000
@@ -1,7 +0,0 @@
-#!/usr/bin/python
-# Copyright 2005 Divmod, Inc. See LICENSE file for details
-
-from vertex.q2qclient import Q2QClientProgram
-
-if __name__ == '__main__':
- Q2QClientProgram().parseOptions()
=== removed directory 'Vertex/doc'
=== removed file 'Vertex/doc/notes'
--- Vertex/doc/notes 2005-08-05 03:13:02 +0000
+++ Vertex/doc/notes 1970-01-01 00:00:00 +0000
@@ -1,61 +0,0 @@
-
-Gin
-===
-
-TCP-alike over UDP
-
-Packet Format
-=============
-
-Connection ID - 32 bits
-Sequence Number - 32 bits
-Ack number - 32 bits
-Window - 32 bits
-Flags (SYN, ACK, FIN, IGN) - 8 bits
-Checksum - 32 bits
-Data length in bytes - 16 bits
-Data - See previous
-
-SEMANTICS
-=========
-Different types of packets:
-
-just SYN:
-SYN+ACK:
-just ACK: normal tcp packet meanings
-
-SYN+NAT: Request for information about the sender's public address
-
-ACK+NAT: Response with information about the recipient's public address. The
-data is the IP address and port number to which the packet is being sent,
-formatted as a dotted-quad formatted IP address followed by a colon followed by
-the base-10 string representation of the port number..
-
-STB: Size Too Big - a packet with a dlen greater than the length of its data
-was received.
-
-Other Stuff:
-
-Connection IDs uniquely identify a stream for a protocol. All bytes
-associated with a particular connectionID will be delivered to the
-same protocol instance.
-
-Sequence Numbers indicate the senders notion of how far into its
-outgoing stream this packet is. Sequence numbers start from a
-pseudo-random value within the allowed range and are incremented by
-the number of bytes in each packet transmitted (re-transmits
-discounted). This indicates to the peer where the bytes in each
-packet lie in the stream, allowing ordered delivery.
-
-Ack numbers indicate the senders notion of how far into its incoming
-stream all data has been received. This value is always what the
-sender expects the receiver to use as its next sequence number.
-
-Window indicates the number of bytes in advance of the senders ack
-number the receiver may proceed in sending. This receiver's sequence
-numbers must never be greater than the sender's last ack number plus
-their window number.
-
-Checksum is a CRC 32 of the data (and only the data - wait maybe this
-should be the header less the checksum, too, in case things get
-corrupted there)
=== removed file 'Vertex/doc/q2q-standalone.tac'
--- Vertex/doc/q2q-standalone.tac 2005-08-10 22:20:03 +0000
+++ Vertex/doc/q2q-standalone.tac 1970-01-01 00:00:00 +0000
@@ -1,5 +0,0 @@
-# Copyright 2005 Divmod, Inc. See LICENSE file for details
-from vertex.q2qstandalone import defaultConfig
-
-application = defaultConfig()
-
=== removed directory 'Vertex/prime'
=== removed directory 'Vertex/prime/plugins'
=== removed file 'Vertex/prime/plugins/vertex_client.py'
--- Vertex/prime/plugins/vertex_client.py 2006-06-01 04:57:02 +0000
+++ Vertex/prime/plugins/vertex_client.py 1970-01-01 00:00:00 +0000
@@ -1,4 +0,0 @@
-
-from vertex.gtk2hack import PlugEntry
-
-pe = PlugEntry()
=== removed file 'Vertex/setup.py'
--- Vertex/setup.py 2009-11-30 01:08:55 +0000
+++ Vertex/setup.py 1970-01-01 00:00:00 +0000
@@ -1,31 +0,0 @@
-from epsilon import setuphelper
-
-from vertex import version
-
-setuphelper.autosetup(
- name="Vertex",
- version=version.short(),
- maintainer="Divmod, Inc.",
- maintainer_email="support@xxxxxxxxxx",
- url="http://divmod.org/trac/wiki/DivmodVertex";,
- license="MIT",
- platforms=["any"],
- description=
- """
- Divmod Vertex is the first implementation of the Q2Q protocol, which
- is a peer-to-peer communication protocol for establishing
- stream-based communication between named endpoints.
- """,
- classifiers=[
- "Development Status :: 2 - Pre-Alpha",
- "Framework :: Twisted",
- "Intended Audience :: Developers",
- "License :: OSI Approved :: MIT License",
- "Programming Language :: Python",
- "Topic :: Communications",
- "Topic :: Internet",
- "Topic :: Internet :: File Transfer Protocol (FTP)",
- "Topic :: Internet :: Name Service (DNS)",
- "Topic :: Software Development :: Libraries :: Python Modules",
- ],
- )
=== removed directory 'Vertex/vertex'
=== removed file 'Vertex/vertex/__init__.py'
--- Vertex/vertex/__init__.py 2008-08-11 11:19:59 +0000
+++ Vertex/vertex/__init__.py 1970-01-01 00:00:00 +0000
@@ -1,3 +0,0 @@
-# -*- test-case-name: vertex.test -*-
-
-from vertex._version import version
=== removed file 'Vertex/vertex/_version.py'
--- Vertex/vertex/_version.py 2009-11-30 01:08:55 +0000
+++ Vertex/vertex/_version.py 1970-01-01 00:00:00 +0000
@@ -1,3 +0,0 @@
-# This is an auto-generated file. Use admin/change-versions to update.
-from twisted.python import versions
-version = versions.Version(__name__[:__name__.rfind('.')], 0, 3, 0)
=== removed file 'Vertex/vertex/bits.py'
--- Vertex/vertex/bits.py 2005-08-05 06:02:56 +0000
+++ Vertex/vertex/bits.py 1970-01-01 00:00:00 +0000
@@ -1,142 +0,0 @@
-# Copyright 2005 Divmod, Inc. See LICENSE file for details
-# -*- test-case-name: vertex.test.test_bits -*-
-""" The purpose of this module is to provide the class BitArray, a compact
-overlay onto an array of bytes which is instead bit-addressable. It also
-includes several bitwise operators.
-
-It does not include all array operations yet, most notably those related to
-slicing, since it is written primarily for use by the swarming implementation
-and swarming only requires fixed-size bit masks.
-
-"""
-
-__metaclass__ = type
-
-import array
-import operator
-import math
-
-BITS_PER_BYTE = 8
-
-def operate(operation):
- # XXX TODO: optimize this and countbits later
- def __x__(self, other):
- if len(self) < len(other):
- return operation(other, self)
- new = BitArray(size=len(self))
- for offt, (mybit, hisbit) in enumerate(zip(self, other)):
- result = new[offt] = operation(mybit, hisbit)
-
- for j in range(offt+1, len(self)):
- new[j] = operation(self[j], 0)
- return new
- return __x__
-
-
-class BitArray:
- """
- A large mutable array of bits.
- """
-
- def __init__(self, bytes=None, size=None, default=0):
- if bytes is None and size is None:
- size = 0
- if bytes is None:
- bytes = array.array("B")
- bytesize = int(math.ceil(float(size) / BITS_PER_BYTE))
- if default:
- padbyte = 255
- else:
- padbyte = 0
- bytes.fromlist([padbyte] * bytesize)
- self.bytes = bytes
- if size is None:
- size = len(self.bytes) * self.bytes.itemsize * BITS_PER_BYTE
- self.size = size
-
- # initialize 'on' and 'off' lists to optimize various things
- self.on = []
- self.off = []
- blists = self.blists = self.off, self.on
-
- for index, bit in enumerate(self):
- blists[bit].append(index)
-
- def append(self, bit):
- offt = self.size
- self.size += 1
- if (len(self.bytes) * self.bytes.itemsize * BITS_PER_BYTE) < self.size:
- self.bytes.append(0)
- self[offt] = bit
-
- def any(self, req=1):
- return bool(self.blists[req])
-
- def percent(self):
- """
- debugging method; returns a string indicating percentage completion
- """
- if not len(self):
- return 'Inf%'
- return '%0.2f%%'% ((float(self.countbits()) / len(self)) * 100,)
-
- def __getitem__(self, bitcount):
- if bitcount < 0:
- bitcount += self.size
- if bitcount >= self.size:
- raise IndexError("%r >= %r" % (bitcount, self.size))
- div, mod = divmod(bitcount, self.bytes.itemsize * BITS_PER_BYTE)
- byte = self.bytes[div]
- return (byte >> mod) & 1
-
- def __setitem__(self, bitcount, bit):
- if bitcount < 0:
- bitcount += self.size
- if bitcount >= self.size:
- raise IndexError("bitcount too big")
- div, mod = divmod(bitcount, self.bytes.itemsize * BITS_PER_BYTE)
- if bit:
- self.bytes[div] |= 1 << mod
- else:
- self.bytes[div] &= ~(1 << mod)
-
- # change updating
- notbitlist = self.blists[not bit]
- try:
- notbitlist.remove(bitcount)
- except ValueError:
- pass
- bitlist = self.blists[bit]
- if bitcount not in bitlist:
- bitlist.append(bitcount)
-
- def __len__(self):
- return self.size
-
- def __repr__(self):
- l = []
- l.append('[')
- for b in self:
- if b:
- c = 'X'
- else:
- c = ' '
- l.append(c)
- l.append(']')
- return ''.join(l)
-
- def countbits(self, on=True):
- return len(self.blists[on])
-
- def positions(self, bit):
- """
- An iterator of all positions that a bit holds in this BitArray.
-
- @param bit: 1 or 0
- """
- return self.blists[bit][:]
-
- __xor__ = operate(operator.xor)
- __and__ = operate(operator.and_)
- __or__ = operate(operator.or_)
-
=== removed file 'Vertex/vertex/conncache.py'
--- Vertex/vertex/conncache.py 2009-07-06 11:40:18 +0000
+++ Vertex/vertex/conncache.py 1970-01-01 00:00:00 +0000
@@ -1,160 +0,0 @@
-# Copyright 2005 Divmod, Inc. See LICENSE file for details
-# -*- test-case-name: vertex.test.test_q2q.TCPConnection.testSendingFiles -*-
-
-"""
-Connect between two endpoints using a message-based protocol to exchange
-messages lazily in response to UI events, caching the protocol as necessary.
-Using connection-oriented protocols, you will most likely not want to use this
-class - you might end up retrieving a cached connection in the middle of a
-chunk of data being sent. For the purposes of this distinction, a
-'message-oriented' protocol is one which has an API which either::
-
- a) writes only whole messages to its transport so there is never an
- opportunity to insert data into the middle of a message, or
-
- b) provides an API on the Protocol instance for queuing whole messages such
- that if partial messages are sent, calling the API multiple times will
- queue them internally so that clients do not need to care whether the
- connection is made or not.
-
-It is worth noting that all Juice-derived protocols meet constraint (b).
-"""
-
-from zope.interface import implements
-
-from twisted.internet.defer import maybeDeferred, DeferredList, Deferred
-from twisted.internet.main import CONNECTION_LOST
-from twisted.internet import interfaces
-from twisted.internet.protocol import ClientFactory
-
-
-class ConnectionCache:
- def __init__(self):
- """
- """
- # map (fromAddress, toAddress, protoName): protocol instance
- self.cachedConnections = {}
- # map (fromAddress, toAddress, protoName): list of Deferreds
- self.inProgress = {}
-
- def connectCached(self, endpoint, protocolFactory,
- extraWork=lambda x: x,
- extraHash=None):
- """See module docstring
- """
- key = endpoint, extraHash
- D = Deferred()
- if key in self.cachedConnections:
- D.callback(self.cachedConnections[key])
- elif key in self.inProgress:
- self.inProgress[key].append(D)
- else:
- self.inProgress[key] = [D]
- endpoint.connect(
- _CachingClientFactory(
- self, key, protocolFactory,
- extraWork))
- return D
-
- def cacheUnrequested(self, endpoint, extraHash, protocol):
- self.connectionMadeForKey((endpoint, extraHash), protocol)
-
- def connectionMadeForKey(self, key, protocol):
- deferreds = self.inProgress.pop(key, [])
- self.cachedConnections[key] = protocol
- for d in deferreds:
- d.callback(protocol)
-
- def connectionLostForKey(self, key):
- if key in self.cachedConnections:
- del self.cachedConnections[key]
-
- def connectionFailedForKey(self, key, reason):
- deferreds = self.inProgress.pop(key)
- for d in deferreds:
- d.errback(reason)
-
- def shutdown(self):
- return DeferredList(
- [maybeDeferred(p.transport.loseConnection)
- for p in self.cachedConnections.values()])
-
-
-class _CachingClientFactory(ClientFactory):
- debug = False
-
- def __init__(self, cache, key, subFactory, extraWork):
- """
- @param cache: a Q2QService
-
- @param key: a 2-tuple of (endpoint, extra) that represents what
- connections coming from this factory are for.
-
- @param subFactory: a ClientFactory which I forward methods to.
-
- @param extraWork: extraWork(proto) -> Deferred which fires when the
- connection has been prepared sufficiently to be used by subsequent
- connections and can be counted as a success.
- """
-
- self.cache = cache
- self.key = key
- self.subFactory = subFactory
- self.finishedExtraWork = False
- self.extraWork = extraWork
-
- lostAsFailReason = CONNECTION_LOST
-
- def clientConnectionMade(self, protocol):
- def success(reason):
- self.cache.connectionMadeForKey(self.key, protocol)
- self.finishedExtraWork = True
- return protocol
-
- def failed(reason):
- self.lostAsFailReason = reason
- protocol.transport.loseConnection()
- return reason
- maybeDeferred(self.extraWork, protocol).addCallbacks(
- success, failed)
-
- def clientConnectionLost(self, connector, reason):
- if self.finishedExtraWork:
- self.cache.connectionLostForKey(self.key)
- else:
- self.cache.connectionFailedForKey(self.key,
- self.lostAsFailReason)
- self.subFactory.clientConnectionLost(connector, reason)
-
- def clientConnectionFailed(self, connector, reason):
- self.cache.connectionFailedForKey(self.key, reason)
- self.subFactory.clientConnectionFailed(connector, reason)
-
- def buildProtocol(self, addr):
- return _CachingTransportShim(self, self.subFactory.buildProtocol(addr))
-
-
-class _CachingTransportShim:
- disconnecting = property(lambda self: self.transport.disconnecting)
-
- implements(interfaces.IProtocol)
-
- def __init__(self, factory, protocol):
- self.factory = factory
- self.protocol = protocol
-
- # IProtocol
- self.dataReceived = protocol.dataReceived
- self.connectionLost = protocol.connectionLost
-
-
- def makeConnection(self, transport):
- self.transport = transport
- self.protocol.makeConnection(transport)
- self.factory.clientConnectionMade(self.protocol)
-
-
- def __repr__(self):
- return 'Q2Q-Cached<%r, %r>' % (self.transport,
- self.protocol)
-
=== removed file 'Vertex/vertex/depserv.py'
--- Vertex/vertex/depserv.py 2009-07-06 11:40:18 +0000
+++ Vertex/vertex/depserv.py 1970-01-01 00:00:00 +0000
@@ -1,197 +0,0 @@
-# Copyright 2005 Divmod, Inc. See LICENSE file for details
-
-"""
-This module is no longer supported for use outside Vertex.
-"""
-
-from twisted.python import log
-from sets import Set
-from twisted.persisted import sob
-from twisted.application import service, internet
-
-from zope.interface import implements
-
-class Conf(dict):
- """A class to help in construction the configuration for delpoy().
-
- Typical usage::
-
- from vertex.depserv import Conf
- conf = Conf()
- s = conf.section
- s('pop',
- port = 110,
- sslPort = 995)
- ...
- """
- def section(self, name, **kw):
- self.setdefault(name, {}).update(kw)
-
-
-class NotPersistable:
- implements(sob.IPersistable)
- def __init__(self, original):
- self.original = original
-
- def setStyle(self, style):
- self.style = style
-
- def save(self, tag=None, filename=None, passphrase=None):
- pass
-
-
-class StartupError(Exception):
- pass
-
-
-class DependencyService(service.MultiService):
- """A MultiService that can start multiple services with interdependencies.
-
- Each keyword parameter is a dict which serves as the options for that
- service.
-
- Each service defines a method setup_SERVICE, which is called with the
- matching parameters (the service name must be all caps). If there is no key
- for SERVICE in the class parameters, the setup method is not called. The
- return value is ignored, and DependencyService makes no assumptions about
- any side effects.
-
- Each service may also optionally define depends_SERVICE which is called
- before the setup method with the same parameters as the setup method. This
- method returns a list of names of services on which SERVICE depends.
- DependencyService will then initialize the service is the correct order. If
- circular dependencies result, or a service depends on another service which
- does not exist or is not configured to run, StartupError is raised.
-
- The class can define required services by setting 'requiredServices' to a
- list of service names. These services will be initialized first in the
- order they appear in the list, ignoring all dependency information. If
- there are no parameters for a required service (consequently, the setup
- method would not normally be called), StartupError is raised.
- """
-
-
- requiredServices = []
-
-
- def __init__(self, **kw):
- service.MultiService.__init__(self)
-
- # this makes it possible for one service to change the configuration of
- # another. Avoid if possible, there if you need it. Be sure to properly
- # set the dependencies.
- self.config = kw
- self.servers = []
-
- services = kw.keys()
- initedServices = Set()
- uninitedServices = Set(services)
-
- # build dependencies
- dependencies = {}
- for serv in services:
- try:
- dependMethod = self._getDependsMethod(serv)
- except AttributeError:
- continue
- dependencies[serv] = dependMethod(**kw[serv])
-
- def initializeService(svc):
- self._getServiceMethod(svc)(**kw[svc])
- initedServices.add(svc)
- uninitedServices.remove(svc)
-
- for svc in self.requiredServices:
- if dependencies.get(svc):
- raise StartupError(
- '%r is a required service but has unsatisfied '
- 'dependency on %r' % (svc, dependencies[svc]))
- initializeService(svc)
-
- while uninitedServices:
- # iterate over the uninitialized services, adding those with no
- # outstanding dependencies to initThisRound.
- initThisRound = []
- for serv in uninitedServices:
- for dep in dependencies.get(serv, []):
- if dep not in initedServices:
- if dep not in uninitedServices:
- raise StartupError(
- 'service %r depends on service %r, which is not '
- 'configured or does not exist.' % (serv, dep))
- break
- else:
- initThisRound.append(serv)
- if not initThisRound:
- raise StartupError(
- 'Can not initialize all services. Circular dependencies '
- 'between setup methods?')
- for svc in initThisRound:
- initializeService(svc)
-
-
- def _getServiceMethod(self, service):
- return getattr(self, 'setup_%s' % (service.upper(),))
-
-
- def _getDependsMethod(self, service):
- return getattr(self, 'depends_%s' % (service.upper(),))
-
-
- def deploy(Class, name=None, uid=None, gid=None, **kw):
- """Create an application with the give name, uid, and gid.
-
- The application has one child service, an instance of Class
- configured based on the additional keyword arguments passed.
-
- The application is not persistable.
- """
- svc = Class(**kw)
-
- if name is None:
- name = Class.__name__
- # Make it easier (possible) to find this service by name later on
- svc.setName(name)
-
- app = service.Application(name, uid=uid, gid=gid)
- app.addComponent(NotPersistable(app), ignoreClass=True)
- svc.setServiceParent(app)
-
- return app
- deploy = classmethod(deploy)
-
- def attach(self, subservice):
- subservice.setServiceParent(self)
- return subservice
-
- def detach(self, subservice):
- subservice.disownServiceParent()
-
- def addServer(self, normalPort, sslPort, f, name):
- """Add a TCP and an SSL server. Name them `name` and `name`+'s'."""
- tcp = internet.TCPServer(normalPort,f)
- tcp.setName(name)
- self.servers.append(tcp)
- if sslPort is not None:
- ssl = internet.SSLServer(sslPort, f, contextFactory=self.sslfac)
- ssl.setName(name+'s')
- self.servers.append(ssl)
-
- def discernPrivilegedServers(self):
- return [srv for srv in self.servers if srv.args[0] <= 1024]
-
- def discernUnprivilegedServers(self):
- return [srv for srv in self.servers if srv.args[0] > 1024]
-
- def privilegedStartService(self):
- for server in self.discernPrivilegedServers():
- log.msg("privileged attach %r" % server)
- self.attach(server)
- return service.MultiService.privilegedStartService(self)
-
- def startService(self):
- for server in self.discernUnprivilegedServers():
- log.msg("attaching %r" % server)
- self.attach(server)
-
- return service.MultiService.startService(self)
=== removed file 'Vertex/vertex/endpoint.py'
--- Vertex/vertex/endpoint.py 2005-08-05 06:02:56 +0000
+++ Vertex/vertex/endpoint.py 1970-01-01 00:00:00 +0000
@@ -1,56 +0,0 @@
-# Copyright 2005 Divmod, Inc. See LICENSE file for details
-
-def stablesort(self, other):
- return cmp(self.__class__, getattr(other, '__class__', type(other)))
-
-class TCPEndpoint:
- def __init__(self, host, port):
- self.host = host
- self.port = port
-
- def __hash__(self):
- return hash((self.host, self.port)) + 5
-
- def connect(self, protocolFactory):
- from twisted.internet import reactor
- return reactor.connectTCP(self.host, self.port, protocolFactory)
-
- def __repr__(self):
- return '<TCP@%s,%d>' % (self.host, self.port)
-
- def __cmp__(self, other):
- if isinstance(other, TCPEndpoint):
- return cmp((self.host, self.port),
- (other.host, other.port))
- return stablesort(self, other)
-
-
-class Q2QEndpoint:
- def __init__(self, service, fromAddress, toAddress, protocolName):
- self.service = service
- self.fromAddress = fromAddress
- self.toAddress = toAddress
- self.protocolName = protocolName
-
- def __repr__(self):
- return '<Q2Q from <%s> to <%s> on %r>' % (
- self.fromAddress, self.toAddress, self.protocolName)
-
- def __cmp__(self, other):
- if isinstance(other, Q2QEndpoint):
- return cmp((self.fromAddress, self.toAddress, self.protocolName),
- (other.fromAddress, other.toAddress, other.protocolName))
- return stablesort(self, other)
-
- def __hash__(self):
- return hash((self.fromAddress,
- self.toAddress,
- self.protocolName)) + 7
-
- def connect(self, protocolFactory):
- # from twisted.python.context import get
- # get("q2q-service")
- return self.service.connectQ2Q(
- self.fromAddress, self.toAddress, self.protocolName,
- protocolFactory)
-
=== removed file 'Vertex/vertex/gtk2hack.glade'
--- Vertex/vertex/gtk2hack.glade 2006-06-01 04:57:02 +0000
+++ Vertex/vertex/gtk2hack.glade 1970-01-01 00:00:00 +0000
@@ -1,635 +0,0 @@
-<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
-<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd";>
-
-<glade-interface>
-<requires lib="gnome"/>
-
-<widget class="GtkMenu" id="notification_popup">
-
- <child>
- <widget class="GtkImageMenuItem" id="add_contact1">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Add Contact</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="addContact" last_modification_time="Sun, 22 May 2005 01:24:07 GMT"/>
-
- <child internal-child="image">
- <widget class="GtkImage" id="image9">
- <property name="visible">True</property>
- <property name="stock">gtk-add</property>
- <property name="icon_size">1</property>
- <property name="xalign">0.5</property>
- <property name="yalign">0.5</property>
- <property name="xpad">0</property>
- <property name="ypad">0</property>
- </widget>
- </child>
- </widget>
- </child>
-
- <child>
- <widget class="GtkMenuItem" id="identifymenuitem">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Identify</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="identifyDialog" last_modification_time="Sun, 22 May 2005 03:20:18 GMT"/>
- </widget>
- </child>
-
- <child>
- <widget class="GtkSeparatorMenuItem" id="contacts_begin">
- <property name="visible">True</property>
- </widget>
- </child>
-
- <child>
- <widget class="GtkSeparatorMenuItem" id="separator3">
- <property name="visible">True</property>
- </widget>
- </child>
-
- <child>
- <widget class="GtkMenuItem" id="animate">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Animate</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="toggleAnimate" last_modification_time="Sun, 22 May 2005 01:44:10 GMT"/>
- </widget>
- </child>
-
- <child>
- <widget class="GtkSeparatorMenuItem" id="separator1">
- <property name="visible">True</property>
- </widget>
- </child>
-
- <child>
- <widget class="GtkImageMenuItem" id="quit1">
- <property name="visible">True</property>
- <property name="stock_item">GNOMEUIINFO_MENU_EXIT_ITEM</property>
- <signal name="activate" handler="quit" last_modification_time="Sun, 22 May 2005 01:24:07 GMT"/>
- </widget>
- </child>
-</widget>
-
-<widget class="GtkDialog" id="ident_dialog">
- <property name="visible">True</property>
- <property name="title" translatable="yes">Identify Yourself</property>
- <property name="type">GTK_WINDOW_TOPLEVEL</property>
- <property name="window_position">GTK_WIN_POS_CENTER</property>
- <property name="modal">False</property>
- <property name="resizable">False</property>
- <property name="destroy_with_parent">False</property>
- <property name="decorated">True</property>
- <property name="skip_taskbar_hint">False</property>
- <property name="skip_pager_hint">False</property>
- <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
- <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
- <property name="focus_on_map">True</property>
- <property name="urgency_hint">False</property>
- <property name="has_separator">True</property>
-
- <child internal-child="vbox">
- <widget class="GtkVBox" id="dialog-vbox1">
- <property name="visible">True</property>
- <property name="homogeneous">False</property>
- <property name="spacing">0</property>
-
- <child internal-child="action_area">
- <widget class="GtkHButtonBox" id="dialog-action_area1">
- <property name="visible">True</property>
- <property name="layout_style">GTK_BUTTONBOX_END</property>
-
- <child>
- <widget class="GtkButton" id="cancelbutton1">
- <property name="visible">True</property>
- <property name="can_default">True</property>
- <property name="can_focus">True</property>
- <property name="label">gtk-cancel</property>
- <property name="use_stock">True</property>
- <property name="relief">GTK_RELIEF_NORMAL</property>
- <property name="focus_on_click">True</property>
- <property name="response_id">-6</property>
- <signal name="clicked" handler="identifyCancel" last_modification_time="Sun, 22 May 2005 03:14:51 GMT"/>
- </widget>
- </child>
-
- <child>
- <widget class="GtkButton" id="okbutton1">
- <property name="visible">True</property>
- <property name="can_default">True</property>
- <property name="can_focus">True</property>
- <property name="label">gtk-ok</property>
- <property name="use_stock">True</property>
- <property name="relief">GTK_RELIEF_NORMAL</property>
- <property name="focus_on_click">True</property>
- <property name="response_id">-5</property>
- <signal name="clicked" handler="identifyOK" last_modification_time="Sun, 22 May 2005 03:15:00 GMT"/>
- </widget>
- </child>
- </widget>
- <packing>
- <property name="padding">0</property>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="pack_type">GTK_PACK_END</property>
- </packing>
- </child>
-
- <child>
- <widget class="GtkVBox" id="vbox1">
- <property name="visible">True</property>
- <property name="homogeneous">False</property>
- <property name="spacing">0</property>
-
- <child>
- <widget class="GtkTable" id="table1">
- <property name="border_width">5</property>
- <property name="visible">True</property>
- <property name="n_rows">2</property>
- <property name="n_columns">2</property>
- <property name="homogeneous">False</property>
- <property name="row_spacing">2</property>
- <property name="column_spacing">5</property>
-
- <child>
- <widget class="GtkLabel" id="label4">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Address</property>
- <property name="use_underline">False</property>
- <property name="use_markup">False</property>
- <property name="justify">GTK_JUSTIFY_LEFT</property>
- <property name="wrap">False</property>
- <property name="selectable">False</property>
- <property name="xalign">0</property>
- <property name="yalign">0.5</property>
- <property name="xpad">0</property>
- <property name="ypad">0</property>
- <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
- <property name="width_chars">-1</property>
- <property name="single_line_mode">False</property>
- <property name="angle">0</property>
- </widget>
- <packing>
- <property name="left_attach">0</property>
- <property name="right_attach">1</property>
- <property name="top_attach">0</property>
- <property name="bottom_attach">1</property>
- <property name="x_options">fill</property>
- <property name="y_options"></property>
- </packing>
- </child>
-
- <child>
- <widget class="GtkLabel" id="label5">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Password</property>
- <property name="use_underline">False</property>
- <property name="use_markup">False</property>
- <property name="justify">GTK_JUSTIFY_LEFT</property>
- <property name="wrap">False</property>
- <property name="selectable">False</property>
- <property name="xalign">0</property>
- <property name="yalign">0.5</property>
- <property name="xpad">0</property>
- <property name="ypad">0</property>
- <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
- <property name="width_chars">-1</property>
- <property name="single_line_mode">False</property>
- <property name="angle">0</property>
- </widget>
- <packing>
- <property name="left_attach">0</property>
- <property name="right_attach">1</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- <property name="x_options">fill</property>
- <property name="y_options"></property>
- </packing>
- </child>
-
- <child>
- <widget class="GtkEntry" id="addressEntry">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="has_focus">True</property>
- <property name="editable">True</property>
- <property name="visibility">True</property>
- <property name="max_length">0</property>
- <property name="text" translatable="yes"></property>
- <property name="has_frame">True</property>
- <property name="invisible_char">*</property>
- <property name="activates_default">False</property>
- </widget>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">0</property>
- <property name="bottom_attach">1</property>
- <property name="y_options"></property>
- </packing>
- </child>
-
- <child>
- <widget class="GtkEntry" id="passwordEntry">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="editable">True</property>
- <property name="visibility">False</property>
- <property name="max_length">0</property>
- <property name="text" translatable="yes"></property>
- <property name="has_frame">True</property>
- <property name="invisible_char">*</property>
- <property name="activates_default">True</property>
- </widget>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- <property name="y_options"></property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="padding">0</property>
- <property name="expand">True</property>
- <property name="fill">True</property>
- </packing>
- </child>
-
- <child>
- <widget class="GtkProgressBar" id="identifyProgressBar">
- <property name="visible">True</property>
- <property name="orientation">GTK_PROGRESS_LEFT_TO_RIGHT</property>
- <property name="fraction">0</property>
- <property name="pulse_step">0.10000000149</property>
- <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
- </widget>
- <packing>
- <property name="padding">0</property>
- <property name="expand">False</property>
- <property name="fill">False</property>
- </packing>
- </child>
-
- <child>
- <widget class="GtkLabel" id="identifyProgressLabel">
- <property name="visible">True</property>
- <property name="label" translatable="yes"></property>
- <property name="use_underline">False</property>
- <property name="use_markup">False</property>
- <property name="justify">GTK_JUSTIFY_LEFT</property>
- <property name="wrap">False</property>
- <property name="selectable">False</property>
- <property name="xalign">0.5</property>
- <property name="yalign">0.5</property>
- <property name="xpad">0</property>
- <property name="ypad">0</property>
- <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
- <property name="width_chars">-1</property>
- <property name="single_line_mode">False</property>
- <property name="angle">0</property>
- </widget>
- <packing>
- <property name="padding">3</property>
- <property name="expand">False</property>
- <property name="fill">False</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="padding">0</property>
- <property name="expand">False</property>
- <property name="fill">True</property>
- </packing>
- </child>
- </widget>
- </child>
-</widget>
-
-<widget class="GtkDialog" id="add_contact_dialog">
- <property name="visible">True</property>
- <property name="title" translatable="yes">Add Contact</property>
- <property name="type">GTK_WINDOW_TOPLEVEL</property>
- <property name="window_position">GTK_WIN_POS_NONE</property>
- <property name="modal">False</property>
- <property name="resizable">True</property>
- <property name="destroy_with_parent">False</property>
- <property name="decorated">True</property>
- <property name="skip_taskbar_hint">False</property>
- <property name="skip_pager_hint">False</property>
- <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
- <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
- <property name="focus_on_map">True</property>
- <property name="urgency_hint">False</property>
- <property name="has_separator">True</property>
-
- <child internal-child="vbox">
- <widget class="GtkVBox" id="dialog-vbox2">
- <property name="visible">True</property>
- <property name="homogeneous">False</property>
- <property name="spacing">0</property>
-
- <child internal-child="action_area">
- <widget class="GtkHButtonBox" id="dialog-action_area2">
- <property name="visible">True</property>
- <property name="layout_style">GTK_BUTTONBOX_END</property>
-
- <child>
- <widget class="GtkButton" id="cancelbutton2">
- <property name="visible">True</property>
- <property name="can_default">True</property>
- <property name="can_focus">True</property>
- <property name="label">gtk-cancel</property>
- <property name="use_stock">True</property>
- <property name="relief">GTK_RELIEF_NORMAL</property>
- <property name="focus_on_click">True</property>
- <property name="response_id">-6</property>
- <signal name="activate" handler="popdownDialog" last_modification_time="Tue, 28 Feb 2006 09:34:42 GMT"/>
- </widget>
- </child>
-
- <child>
- <widget class="GtkButton" id="okbutton2">
- <property name="visible">True</property>
- <property name="can_default">True</property>
- <property name="can_focus">True</property>
- <property name="label">gtk-ok</property>
- <property name="use_stock">True</property>
- <property name="relief">GTK_RELIEF_NORMAL</property>
- <property name="focus_on_click">True</property>
- <property name="response_id">-5</property>
- <signal name="activate" handler="doAddContact" last_modification_time="Tue, 28 Feb 2006 09:34:23 GMT"/>
- <signal name="clicked" handler="doAddContact" last_modification_time="Tue, 28 Feb 2006 10:36:25 GMT"/>
- </widget>
- </child>
- </widget>
- <packing>
- <property name="padding">0</property>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="pack_type">GTK_PACK_END</property>
- </packing>
- </child>
-
- <child>
- <widget class="GtkFrame" id="frame1">
- <property name="visible">True</property>
- <property name="label_xalign">0</property>
- <property name="label_yalign">0.5</property>
- <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
-
- <child>
- <widget class="GtkAlignment" id="alignment1">
- <property name="visible">True</property>
- <property name="xalign">0.5</property>
- <property name="yalign">0.5</property>
- <property name="xscale">1</property>
- <property name="yscale">1</property>
- <property name="top_padding">0</property>
- <property name="bottom_padding">0</property>
- <property name="left_padding">12</property>
- <property name="right_padding">0</property>
-
- <child>
- <widget class="GtkTable" id="table2">
- <property name="border_width">16</property>
- <property name="visible">True</property>
- <property name="n_rows">2</property>
- <property name="n_columns">2</property>
- <property name="homogeneous">False</property>
- <property name="row_spacing">16</property>
- <property name="column_spacing">16</property>
-
- <child>
- <widget class="GtkEntry" id="nameentry">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="editable">True</property>
- <property name="visibility">True</property>
- <property name="max_length">0</property>
- <property name="text" translatable="yes"></property>
- <property name="has_frame">True</property>
- <property name="invisible_char">*</property>
- <property name="activates_default">False</property>
- </widget>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">0</property>
- <property name="bottom_attach">1</property>
- <property name="y_options"></property>
- </packing>
- </child>
-
- <child>
- <widget class="GtkEntry" id="q2qidentry">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="editable">True</property>
- <property name="visibility">True</property>
- <property name="max_length">0</property>
- <property name="text" translatable="yes"></property>
- <property name="has_frame">True</property>
- <property name="invisible_char">*</property>
- <property name="activates_default">False</property>
- </widget>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- <property name="y_options"></property>
- </packing>
- </child>
-
- <child>
- <widget class="GtkLabel" id="label7">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Name</property>
- <property name="use_underline">False</property>
- <property name="use_markup">False</property>
- <property name="justify">GTK_JUSTIFY_LEFT</property>
- <property name="wrap">False</property>
- <property name="selectable">False</property>
- <property name="xalign">0</property>
- <property name="yalign">0.5</property>
- <property name="xpad">0</property>
- <property name="ypad">0</property>
- <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
- <property name="width_chars">-1</property>
- <property name="single_line_mode">False</property>
- <property name="angle">0</property>
- </widget>
- <packing>
- <property name="left_attach">0</property>
- <property name="right_attach">1</property>
- <property name="top_attach">0</property>
- <property name="bottom_attach">1</property>
- <property name="x_options">fill</property>
- <property name="y_options"></property>
- </packing>
- </child>
-
- <child>
- <widget class="GtkLabel" id="label8">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Q2QID</property>
- <property name="use_underline">False</property>
- <property name="use_markup">False</property>
- <property name="justify">GTK_JUSTIFY_LEFT</property>
- <property name="wrap">False</property>
- <property name="selectable">False</property>
- <property name="xalign">0</property>
- <property name="yalign">0.5</property>
- <property name="xpad">0</property>
- <property name="ypad">0</property>
- <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
- <property name="width_chars">-1</property>
- <property name="single_line_mode">False</property>
- <property name="angle">0</property>
- </widget>
- <packing>
- <property name="left_attach">0</property>
- <property name="right_attach">1</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- <property name="x_options">fill</property>
- <property name="y_options"></property>
- </packing>
- </child>
- </widget>
- </child>
- </widget>
- </child>
-
- <child>
- <widget class="GtkLabel" id="label6">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Contact Information</property>
- <property name="use_underline">False</property>
- <property name="use_markup">True</property>
- <property name="justify">GTK_JUSTIFY_LEFT</property>
- <property name="wrap">False</property>
- <property name="selectable">False</property>
- <property name="xalign">0.5</property>
- <property name="yalign">0.5</property>
- <property name="xpad">0</property>
- <property name="ypad">0</property>
- <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
- <property name="width_chars">-1</property>
- <property name="single_line_mode">False</property>
- <property name="angle">0</property>
- </widget>
- <packing>
- <property name="type">label_item</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="padding">0</property>
- <property name="expand">True</property>
- <property name="fill">True</property>
- </packing>
- </child>
- </widget>
- </child>
-</widget>
-
-<widget class="GtkDialog" id="accept_connection_dialog">
- <property name="visible">True</property>
- <property name="title" translatable="yes">Accept Connection?</property>
- <property name="type">GTK_WINDOW_TOPLEVEL</property>
- <property name="window_position">GTK_WIN_POS_NONE</property>
- <property name="modal">False</property>
- <property name="resizable">True</property>
- <property name="destroy_with_parent">False</property>
- <property name="decorated">True</property>
- <property name="skip_taskbar_hint">False</property>
- <property name="skip_pager_hint">False</property>
- <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
- <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
- <property name="focus_on_map">True</property>
- <property name="urgency_hint">False</property>
- <property name="has_separator">True</property>
- <signal name="destroy" handler="rejectConnectionEvt" last_modification_time="Tue, 28 Feb 2006 10:00:37 GMT"/>
-
- <child internal-child="vbox">
- <widget class="GtkVBox" id="dialog-vbox3">
- <property name="visible">True</property>
- <property name="homogeneous">False</property>
- <property name="spacing">0</property>
-
- <child internal-child="action_area">
- <widget class="GtkHButtonBox" id="dialog-action_area3">
- <property name="visible">True</property>
- <property name="layout_style">GTK_BUTTONBOX_END</property>
-
- <child>
- <widget class="GtkButton" id="cancelbutton3">
- <property name="visible">True</property>
- <property name="can_default">True</property>
- <property name="can_focus">True</property>
- <property name="label">gtk-cancel</property>
- <property name="use_stock">True</property>
- <property name="relief">GTK_RELIEF_NORMAL</property>
- <property name="focus_on_click">True</property>
- <property name="response_id">-6</property>
- <signal name="activate" handler="destroyit" last_modification_time="Tue, 28 Feb 2006 10:00:55 GMT"/>
- <signal name="clicked" handler="destroyit" last_modification_time="Tue, 28 Feb 2006 10:36:09 GMT"/>
- </widget>
- </child>
-
- <child>
- <widget class="GtkButton" id="okbutton3">
- <property name="visible">True</property>
- <property name="can_default">True</property>
- <property name="can_focus">True</property>
- <property name="label">gtk-ok</property>
- <property name="use_stock">True</property>
- <property name="relief">GTK_RELIEF_NORMAL</property>
- <property name="focus_on_click">True</property>
- <property name="response_id">-5</property>
- <signal name="activate" handler="acceptConnectionEvt" last_modification_time="Tue, 28 Feb 2006 09:59:48 GMT"/>
- <signal name="clicked" handler="acceptConnectionEvt" last_modification_time="Tue, 28 Feb 2006 10:36:51 GMT"/>
- </widget>
- </child>
- </widget>
- <packing>
- <property name="padding">0</property>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="pack_type">GTK_PACK_END</property>
- </packing>
- </child>
-
- <child>
- <widget class="GtkLabel" id="accept_connection_label">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Accept connection?</property>
- <property name="use_underline">False</property>
- <property name="use_markup">False</property>
- <property name="justify">GTK_JUSTIFY_LEFT</property>
- <property name="wrap">False</property>
- <property name="selectable">False</property>
- <property name="xalign">0.5</property>
- <property name="yalign">0.5</property>
- <property name="xpad">0</property>
- <property name="ypad">0</property>
- <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
- <property name="width_chars">-1</property>
- <property name="single_line_mode">False</property>
- <property name="angle">0</property>
- </widget>
- <packing>
- <property name="padding">0</property>
- <property name="expand">False</property>
- <property name="fill">False</property>
- </packing>
- </child>
- </widget>
- </child>
-</widget>
-
-</glade-interface>
=== removed file 'Vertex/vertex/gtk2hack.py'
--- Vertex/vertex/gtk2hack.py 2006-06-01 04:57:02 +0000
+++ Vertex/vertex/gtk2hack.py 1970-01-01 00:00:00 +0000
@@ -1,270 +0,0 @@
-
-import os
-import rfc822
-
-from twisted.python.filepath import FilePath
-
-# import gtk ### pyflakes complains about this, due to the next line
-import gtk.glade
-
-from vertex.q2qclient import ClientQ2QService
-from vertex.q2q import Q2QAddress
-
-class _NullCb:
- def __init__(self, name):
- self.name = name
-
- def __call__(self, *a, **kw):
- print 'No callback provided for', self.name, a, kw
-
-class _SignalAttacher:
- def __init__(self, original):
- self.original = original
-
- def __getitem__(self, callbackName):
- return getattr(self.original, callbackName, None) or _NullCb(callbackName)
-
-GLADE_FILE = os.path.splitext(__file__)[0] + '.glade'
-
-class IdentificationDialog:
- def __init__(self, clientService, plug):
- self.xml = gtk.glade.XML(GLADE_FILE, "ident_dialog")
- self.clientService = clientService
- self.xml.signal_autoconnect(_SignalAttacher(self))
- self.addressEntry = self.xml.get_widget('addressEntry')
- self.passwordEntry = self.xml.get_widget('passwordEntry')
- self.progressBar = self.xml.get_widget('identifyProgressBar')
- self.progressLabel = self.xml.get_widget('identifyProgressLabel')
- self.identifyWindow = self.xml.get_widget("ident_dialog")
- self.cancelButton = self.xml.get_widget('cancelbutton1')
- self.okButton = self.xml.get_widget('okbutton1')
- self.plug = plug
-
- def identifyCancel(self, event):
- self.identifyWindow.destroy()
-
- def identifyOK(self, event):
- idstr = self.addressEntry.get_text()
- D = self.clientService.authorize(
- Q2QAddress.fromString(idstr),
- self.passwordEntry.get_text())
-
- sensitiveWidgets = [self.addressEntry,
- self.passwordEntry,
- self.okButton,
- self.cancelButton]
- for widget in sensitiveWidgets:
- widget.set_sensitive(False)
- self.progressLabel.set_text("Authenticating...")
- def itWorked(workedNone):
- self.identifyWindow.destroy()
- self.plug.setCurrentID(idstr)
- def itDidntWork(error):
- self.progressLabel.set_text(error.getErrorMessage())
- for widget in sensitiveWidgets:
- widget.set_sensitive(True)
- D.addCallbacks(itWorked, itDidntWork)
-
-class AddContactDialog:
- def __init__(self, plug):
- self.xml = gtk.glade.XML(GLADE_FILE, "add_contact_dialog")
- self.xml.signal_autoconnect(_SignalAttacher(self))
- self.window = self.xml.get_widget("add_contact_dialog")
- self.window.show_all()
- self.plug = plug
-
- def doAddContact(self, evt):
- name = self.xml.get_widget("nameentry").get_text()
- addr = self.xml.get_widget("q2qidentry").get_text()
- self.plug.addBuddy(name, addr)
- self.popdownDialog()
-
- def popdownDialog(self, evt=None):
- self.window.destroy()
-
-class AcceptConnectionDialog:
- def __init__(self, d, From, to, protocol):
- self.d = d
- self.xml = gtk.glade.XML(GLADE_FILE, "accept_connection_dialog")
- self.xml.signal_autoconnect(_SignalAttacher(self))
- self.label = self.xml.get_widget("accept_connection_label")
- self.label.set_text(
- "Accept connection from %s for %s?" % (From, protocol))
- self.window = self.xml.get_widget("accept_connection_dialog")
- self.window.show_all()
-
- done = False
-
- def destroyit(self, evt):
- self.window.destroy()
-
- def acceptConnectionEvt(self, evt):
- self.done = True
- print "YES"
- self.d.callback(1)
- print "WHAT"
- self.window.destroy()
-
- def rejectConnectionEvt(self, evt):
- print "DSTRY"
- if not self.done:
- print "DIE!"
- from twisted.python import failure
- self.d.errback(failure.Failure(KeyError("Connection rejected by user")))
- else:
- print "OK"
-
-from twisted.internet.protocol import ServerFactory
-from twisted.internet.protocol import Protocol
-
-class VertexDemoProtocol(Protocol):
-
- def connectionMade(self):
- print 'CONN MADE'
-
- def dataReceived(self, data):
- print 'HOLY SHNIKIES', data
-
-class VertexFactory(ServerFactory):
- protocol = VertexDemoProtocol
-
- def __init__(self, plug):
- self.plug = plug
-
- def startFactory(self):
- #self.plug.animator.stop(1)
- pass
-
- def stopFactory(self):
- #self.plug.animator.stop(0)
- pass
-
-
-class BuddyItem:
- def __init__(self, plug, alias, q2qaddress):
- mi = self.menuItem = gtk.MenuItem(alias + " <"+q2qaddress+">")
- mi.connect("activate", self.initiateFileTransfer)
- mi.show_all()
- self.plug = plug
- self.alias = alias
- self.q2qaddress = q2qaddress
- self.plug.loadedBuddies[q2qaddress] = self
-
- def initiateFileTransfer(self, evt):
- print 'Initiate transfer with ' + self.alias + self.q2qaddress
-
- def addToMenu(self):
- self.plug.section.append(self.menuItem)
-
- def removeFromMenu(self):
- self.plug.section.remove(self.menuItem)
-
-from twisted.plugin import IPlugin
-from prime.iprime import IMenuApplication
-from zope.interface import implements
-
-class PlugEntry:
- implements(IMenuApplication, IPlugin)
-
- def __init__(self):
- self.xml = gtk.glade.XML(GLADE_FILE, "notification_popup")
-
- def register(self, section):
- print 'REGISTER'
- self.section = section
-
- workingdir = FilePath(os.path.expanduser("~/.vertex"))
- self.clientService = ClientQ2QService(
- workingdir.child("q2q-certificates").path,
- verifyHook=self.displayVerifyDialog,
- inboundTCPPortnum=8172,
- # q2qPortnum=8173,
- udpEnabled=False)
- self.setCurrentID(self.clientService.getDefaultFrom())
- self.buddiesfile = workingdir.child("q2q-buddies.txt")
- self.loadedBuddies = {}
- self.parseBuddies()
-
- def parseBuddies(self):
- try:
- self.buddyList = rfc822.AddressList(self.buddiesfile.open().read())
- except IOError:
- return
- self.clearContactMenu()
- for dispn, addr in self.buddyList:
- if addr not in self.loadedBuddies:
- BuddyItem(self, dispn, addr)
- self.buildContactMenu()
-
- def clearContactMenu(self):
- for bud in self.loadedBuddies.values():
- bud.removeFromMenu()
-
- def buildContactMenu(self):
- l = self.loadedBuddies.values()
- l.sort(key=lambda x: x.alias)
- l.reverse()
- for bud in l:
- bud.addToMenu()
-
- def addBuddy(self, alias, q2qaddr):
- temp = self.buddiesfile.temporarySibling()
- try:
- origdata = self.buddiesfile.open().read()
- except IOError:
- origdata = ''
- moredata = '\n%s <%s>' % (alias, q2qaddr)
- ftemp = temp.open('w')
- ftemp.write(origdata)
- ftemp.write(moredata)
- ftemp.close()
- temp.moveTo(self.buddiesfile)
- self.parseBuddies()
-
- def displayVerifyDialog(self, From, to, protocol):
- from twisted.internet import defer
- d = defer.Deferred()
- AcceptConnectionDialog(d, From, to, protocol)
- return d
-
- def setCurrentID(self, idName):
-
- if idName is not None:
- currentID = Q2QAddress.fromString(idName)
- # log in?
- # self.animator.start()
- SL = self.xml.get_widget("identifymenuitem").get_children()[0].set_label
- def loggedIn(result):
- SL(str(currentID))
- self.currentID = currentID
- def notLoggedIn(error):
- SL("Identify")
- # self.animator.stop(0)
- # This following order is INSANE - you should definitely not have
- # to wait until the LISTEN succeeds to start the service; quite the
- # opposite, you should wait until the service has started, then
- # issue the LISTEN!! For some reason, the connection drops
- # immediately if you do that, and I have no idea why. As soon as I
- # can fix that issue the startService should be moved up previous
- # to listenQ2Q.
- self.clientService.listenQ2Q(currentID,
- {'vertex': VertexFactory(self)},
- "desktop vertex UI").addCallbacks(
- loggedIn, notLoggedIn).addCallback(
- lambda ign: self.clientService.startService())
-
- # XXX event handlers
-
- def toggleAnimate(self, event):
- if self.animator.animating:
- # SL("Animate")
- self.animator.stop()
- else:
- # SL("Stop Animating")
- self.animator.start()
-
- def identifyDialog(self, event):
- IdentificationDialog(self.clientService, self)
-
- def addContact(self, event):
- AddContactDialog(self)
=== removed file 'Vertex/vertex/icon-active.png'
Binary files Vertex/vertex/icon-active.png 2006-06-01 04:57:02 +0000 and Vertex/vertex/icon-active.png 1970-01-01 00:00:00 +0000 differ
=== removed file 'Vertex/vertex/icon-inactive.png'
Binary files Vertex/vertex/icon-inactive.png 2006-06-01 04:57:02 +0000 and Vertex/vertex/icon-inactive.png 1970-01-01 00:00:00 +0000 differ
=== removed file 'Vertex/vertex/ivertex.py'
--- Vertex/vertex/ivertex.py 2012-03-14 16:23:22 +0000
+++ Vertex/vertex/ivertex.py 1970-01-01 00:00:00 +0000
@@ -1,108 +0,0 @@
-# Copyright 2005 Divmod, Inc. See LICENSE file for details
-
-from zope.interface import Interface
-
-class IQ2QTransport(Interface):
- """
- I am a byte-stream-oriented transport which has Q2Q identifiers associated
- with the endpoints, and possibly some cryptographic verification of the
- authenticity of those endpoints.
- """
-
- def getQ2QHost():
- """ Returns a Q2QAddress object representing the user on this end of the
- connection.
- """
-
- def getQ2QPeer():
- """ Returns a Q2QAddress object representing the user on the other end of the
- connection.
- """
-
-class IQ2QUser(Interface):
- """
- A cred interface for Q2Q users.
- """
- def signCertificateRequest(certificateRequest, domainCert, suggestedSerial):
- """
- Return a signed certificate object if the subject fields in the
- certificateRequest are valid.
- """
-
-class IFileTransfer(Interface):
-
- def getUploadSink(self, path):
- """
- @param path: a PathFragment that the client wishes to upload to.
-
- @return: a DataSink where we'll save the data to.
- """
-
- def getDownloadSource(self, path):
- """
- @param path: a PathFragment that the client wishes to download.
-
- @return: a DataSource to download data from.
- """
-
- def listChildren(self, path):
- """
- @param path: a PathFragment that the client wishes to get a list of.
-
- @return: a list of dictionaries mapping::
- {'name': str,
- 'size': int,
- 'type': vertex.filexfer.MIMEType,
- 'modified': datetime.datetime}
- """
-
-class ISessionTokenStorage(Interface):
- def idFromCookie(self, cookie, domain):
- """Look up a user ID from the given cookie in the given domain.
- """
-
-class ICertificateStorage(Interface):
- def getSelfSignedCertificate(self, domainName):
- """
- @return: a Deferred which will fire with the certificate for the given
- domain name.
- """
-
- def storeSelfSignedCertificate(self, domainName, mainCert):
- """
- @type mainCert: C{str}
- @param mainCert: Serialized, self-signed certificate to associate
- with the given domain.
-
- @return: a Deferred which will fire when the certificate has been
- stored successfully.
- """
-
- def getPrivateCertificate(self, domainName):
- """
- @return: a PrivateCertificate instance, e.g. a certificate including a
- private key, for 'domainName'.
- """
-
- def addPrivateCertificate(self, domainName, existingCertificate=None):
- """
- """
-
-class IOfferUp(Interface):
- """
- Sharing control database storage.
- """
-
-class IPlugin(Interface):
- """
- """
-
-class ITestPlugin(Interface):
- """
- Dummy plug-in interface for unit testing.
- """
-
-class ITestPlugin2(Interface):
- """
- Dummy plug-in interface for unit testing.
- """
=== removed file 'Vertex/vertex/ptcp.py'
--- Vertex/vertex/ptcp.py 2012-03-14 16:23:22 +0000
+++ Vertex/vertex/ptcp.py 1970-01-01 00:00:00 +0000
@@ -1,1050 +0,0 @@
-# -*- test-case-name: vertex.test.test_ptcp -*-
-
-import struct
-
-from binascii import crc32 # used to use zlib.crc32 - but that gives different
- # results on 64-bit platforms!!
-
-import itertools
-
-from twisted.python.failure import Failure
-from twisted.internet.defer import Deferred
-from twisted.internet import protocol, error, reactor, defer
-from twisted.internet.main import CONNECTION_DONE
-from twisted.python import log, util
-
-from vertex import tcpdfa
-from vertex.statemachine import StateError
-
-
-genConnID = itertools.count(8).next
-
-MAX_PSEUDO_PORT = (2 ** 16)
-
-_packetFormat = ('!' # WTF did you think
- 'H' # sourcePseudoPort
- 'H' # destPseudoPort
- 'L' # sequenceNumber
- 'L' # acknowledgementNumber
- 'L' # window
- 'B' # flags
- 'l' # checksum
- # (signed because of binascii.crc32)
- 'H' # dlen
- )
-_fixedSize = struct.calcsize(_packetFormat)
-
-_SYN, _ACK, _FIN, _RST, _STB = [1 << n for n in range(5)]
-
-def _flagprop(flag):
- def setter(self, value):
- if value:
- self.flags |= flag
- else:
- self.flags &= ~flag
- return property(lambda self: bool(self.flags & flag), setter)
-
-def relativeSequence(wireSequence, initialSequence, lapNumber):
- """ Compute a relative sequence number from a wire sequence number so that we
- can use natural Python comparisons on it, such as <, >, ==.
-
- @param wireSequence: the sequence number received on the wire.
-
- @param initialSequence: the ISN for this sequence, negotiated at SYN time.
-
- @param lapNumber: the number of times that this value has wrapped around
- 2**32.
- """
- return (wireSequence + (lapNumber * (2**32))) - initialSequence
-
-class PTCPPacket(util.FancyStrMixin, object):
- showAttributes = (
- ('sourcePseudoPort', 'sourcePseudoPort', '%d'),
- ('destPseudoPort', 'destPseudoPort', '%d'),
- ('shortdata', 'data', '%r'),
- ('niceflags', 'flags', '%s'),
- ('dlen', 'dlen', '%d'),
- ('seqNum', 'seq', '%d'),
- ('ackNum', 'ack', '%d'),
- ('checksum', 'checksum', '%x'),
- ('peerAddressTuple', 'peerAddress', '%r'),
- ('retransmitCount', 'retransmitCount', '%d'),
- )
-
- syn = _flagprop(_SYN)
- ack = _flagprop(_ACK)
- fin = _flagprop(_FIN)
- rst = _flagprop(_RST)
- stb = _flagprop(_STB)
-
- # Number of retransmit attempts left for this segment. When it reaches
- # zero, this segment is dead.
- retransmitCount = 50
-
- def shortdata():
- def get(self):
- if len(self.data) > 13:
- return self.data[:5] + '...' + self.data[-5:]
- else:
- return self.data
- return get,
- shortdata = property(*shortdata())
-
- def niceflags():
- def get(self):
- res = []
- for (f, v) in [
- (self.syn, 'S'), (self.ack, 'A'), (self.fin, 'F'),
- (self.rst, 'R'), (self.stb, 'T')]:
- res.append(f and v or '.')
- return ''.join(res)
- return get,
- niceflags = property(*niceflags())
-
- def create(cls,
- sourcePseudoPort, destPseudoPort,
- seqNum, ackNum, data,
- window=(1 << 15),
- syn=False, ack=False, fin=False,
- rst=False, stb=False,
- destination=None):
- i = cls(sourcePseudoPort, destPseudoPort,
- seqNum, ackNum, window,
- 0, 0, len(data), data)
- i.syn = syn
- i.ack = ack
- i.fin = fin
- i.rst = rst
- i.stb = stb
- i.checksum = i.computeChecksum()
- i.destination = destination
- return i
- create = classmethod(create)
-
-
- def __init__(self,
- sourcePseudoPort,
- destPseudoPort,
- seqNum, ackNum, window, flags,
- checksum, dlen, data, peerAddressTuple=None,
- seqOffset=0, ackOffset=0, seqLaps=0, ackLaps=0):
- self.sourcePseudoPort = sourcePseudoPort
- self.destPseudoPort = destPseudoPort
- self.seqNum = seqNum
- self.ackNum = ackNum
- self.window = window
- self.flags = flags
- self.checksum = checksum
- self.dlen = dlen
- self.data = data
- self.peerAddressTuple = peerAddressTuple # None if local
-
- self.seqOffset = seqOffset
- self.ackOffset = ackOffset
- self.seqLaps = seqLaps
- self.ackLaps = ackLaps
-
- def segmentLength(self):
- """RFC page 26: 'The segment length (SEG.LEN) includes both data and sequence
- space occupying controls'
- """
- return self.dlen + self.syn + self.fin
-
- def relativeSeq(self):
- return relativeSequence(self.seqNum, self.seqOffset, self.seqLaps)
-
- def relativeAck(self):
- return relativeSequence(self.ackNum, self.ackOffset, self.ackLaps)
-
-
- def verifyChecksum(self):
- if len(self.data) != self.dlen:
- if len(self.data) > self.dlen:
- raise GarbageDataError(self)
- else:
- raise TruncatedDataError(self)
- expected = self.computeChecksum()
- received = self.checksum
- if expected != received:
- raise ChecksumMismatchError(expected, received)
-
- def computeChecksum(self):
- return crc32(self.data)
-
- def decode(cls, bytes, hostPortPair):
- fields = struct.unpack(_packetFormat, bytes[:_fixedSize])
- sourcePseudoPort, destPseudoPort, seq, ack, window, flags, checksum, dlen = fields
- data = bytes[_fixedSize:]
- pkt = cls(sourcePseudoPort, destPseudoPort, seq, ack, window, flags,
- checksum, dlen, data, hostPortPair)
- return pkt
- decode = classmethod(decode)
-
- def mustRetransmit(self):
- """Check to see if this packet must be retransmitted until it was received.
- """
- if self.syn or self.fin or self.dlen:
- return True
- return False
-
- def encode(self):
- dlen = len(self.data)
- checksum = self.computeChecksum()
- return struct.pack(
- _packetFormat,
- self.sourcePseudoPort, self.destPseudoPort,
- self.seqNum, self.ackNum, self.window,
- self.flags, checksum, dlen) + self.data
-
- def fragment(self, mtu):
- if self.dlen < mtu:
- return [self]
- assert not self.syn, "should not be originating syn packets w/ data"
- seqOfft = 0
- L = []
- # XXX TODO: need to take seqLaps into account, etc.
- for chunk in iterchunks(self.data, mtu):
- last = self.create(self.sourcePseudoPort,
- self.destPseudoPort,
- self.seqNum + seqOfft,
- self.ackNum,
- chunk,
- self.window,
- destination=self.destination,
- ack=self.ack)
- L.append(last)
- seqOfft += len(chunk)
- if self.fin:
- last.fin = self.fin
- last.checksum = last.computeChecksum()
- return L
-
-
-def iterchunks(data, chunksize):
- """iterate chunks of data
- """
- offt = 0
- while offt < len(data):
- yield data[offt:offt+chunksize]
- offt += chunksize
-
-
-def ISN():
- """
- Initial Sequence Number generator.
- """
- # return int((time.time() * 1000000) / 4) % 2**32
- return 0
-
-
-def segmentAcceptable(RCV_NXT, RCV_WND, SEG_SEQ, SEG_LEN):
- # RFC page 26.
- if SEG_LEN == 0 and RCV_WND == 0:
- return SEG_SEQ == RCV_NXT
- if SEG_LEN == 0 and RCV_WND > 0:
- return ((RCV_NXT <= SEG_SEQ) and (SEG_SEQ < RCV_NXT + RCV_WND))
- if SEG_LEN > 0 and RCV_WND == 0:
- return False
- if SEG_LEN > 0 and RCV_WND > 0:
- return (( (RCV_NXT <= SEG_SEQ) and (SEG_SEQ < RCV_NXT + RCV_WND))
- or ((RCV_NXT <= SEG_SEQ+SEG_LEN-1) and
- (SEG_SEQ+SEG_LEN-1 < RCV_NXT + RCV_WND)))
- assert 0, 'Should be impossible to get here.'
- return False
-
-class BadPacketError(Exception):
- """
- A packet was bad for some reason.
- """
-
-class ChecksumMismatchError(Exception):
- """
- The checksum and data received did not match.
- """
-
-class TruncatedDataError(Exception):
- """
- The packet was truncated in transit, and all of the data did not arrive.
- """
-
-class GarbageDataError(Exception):
- """
- Too much data was received (???)
- """
-
-class PTCPConnection(tcpdfa.TCP):
- """
- Implementation of RFC 793 state machine.
-
- @ivar oldestUnackedSendSeqNum: (TCP RFC: SND.UNA) The oldest (relative)
- sequence number referring to an octet which we have sent or may send which
- is unacknowledged. This begins at 0, which is special because it is not
- for an octet, but rather for the initial SYN packet. Unless it is 0, this
- represents the sequence number of self._outgoingBytes[0].
-
- @ivar nextSendSeqNum: (TCP RFC: SND.NXT) The next (relative) sequence
- number that we will send to our peer after the current buffered segments
- have all been acknowledged. This is the sequence number of the
- not-yet-extant octet in the stream at
- self._outgoingBytes[len(self._outgoingBytes)].
-
- @ivar nextRecvSeqNum: (TCP RFC: RCV.NXT) The next (relative) sequence
- number that the peer should send to us if they want to send more data;
- their first unacknowledged sequence number as far as we are concerned; the
- left or lower edge of the receive window; the sequence number of the first
- octet that has not been delivered to the application. changed whenever we
- receive an appropriate ACK.
-
- @ivar peerSendISN: the initial sequence number that the peer sent us during
- the negotiation phase. All peer-relative sequence numbers are computed
- using this. (see C{relativeSequence}).
-
- @ivar hostSendISN: the initial sequence number that the we sent during the
- negotiation phase. All host-relative sequence numbers are computed using
- this. (see C{relativeSequence})
-
- @ivar retransmissionQueue: a list of packets to be re-sent until their
- acknowledgements come through.
-
- @ivar recvWindow: (TCP RFC: RCV.WND) - the size [in octets] of the current
- window allowed by this host, to be in transit from the other host.
-
- @ivar sendWindow: (TCP RFC: SND.WND) - the size [in octets] of the current
- window allowed by our peer, to be in transit from us.
-
- """
-
- mtu = 512 - _fixedSize
-
- recvWindow = mtu
- sendWindow = mtu
- sendWindowRemaining = mtu * 2
-
- protocol = None
-
- def __init__(self,
- hostPseudoPort, peerPseudoPort,
- ptcp, factory, peerAddressTuple):
- tcpdfa.TCP.__init__(self)
- self.hostPseudoPort = hostPseudoPort
- self.peerPseudoPort = peerPseudoPort
- self.ptcp = ptcp
- self.factory = factory
- self._receiveBuffer = []
- self.retransmissionQueue = []
- self.peerAddressTuple = peerAddressTuple
-
- self.oldestUnackedSendSeqNum = 0
- self.nextSendSeqNum = 0
- self.hostSendISN = 0
- self.nextRecvSeqNum = 0
- self.peerSendISN = 0
- self.setPeerISN = False
-
- peerSendISN = None
-
- def packetReceived(self, packet):
- # XXX TODO: probably have to do something to the packet here to
- # identify its relative sequence number.
-
- # print 'received', self, packet
-
- if packet.stb:
- # Shrink the MTU
- [self.mtu] = struct.unpack('!H', packet.data)
- rq = []
- for pkt in self.retransmissionQueue:
- rq.extend(pkt.fragment(self.mtu))
- self.retransmissionQueue = rq
- return
-
- if self._paused:
- return
-
- generatedStateMachineInput = False
- if packet.syn:
- if packet.dlen:
- # Whoops, what? SYNs probably can contain data, I think, but I
- # certainly don't see anything in the spec about how to deal
- # with this or in ethereal for how linux deals with it -glyph
- raise BadPacketError(
- "currently no data allowed in SYN packets: %r"
- % (packet,))
- else:
- assert packet.segmentLength() == 1
- if self.peerAddressTuple is None:
- # we're a server
- assert self.wasEverListen, "Clients must specify a connect address."
- self.peerAddressTuple = packet.peerAddressTuple
- else:
- # we're a client
- assert self.peerAddressTuple == packet.peerAddressTuple
- if self.setPeerISN:
- if self.peerSendISN != packet.seqNum:
- raise BadPacketError(
- "Peer ISN was already set to %s but incoming packet "
- "tried to set it to %s" % (
- self.peerSendISN, packet.seqNum))
- if not self.retransmissionQueue:
- # If our retransmissionQueue is hot, we are going to send
- # them an ACK to this with the next packet we send them
- # anyway; as a bonus, this will properly determine whether
- # we're sending a SYN+ACK or merely an ACK; the only time
- # we send an ACK is when we have nothing to say to them and
- # they're blocked on getting a response to their SYN+ACK
- # from us. -glyph
- self.originate(ack=True)
- return
- self.setPeerISN = True
- self.peerSendISN = packet.seqNum
- # syn, fin, and data are mutually exclusive, so this relative
- # sequence-number increment is done both here, and below in the
- # data/fin processing block.
- self.nextRecvSeqNum += packet.segmentLength()
- if not packet.ack:
- generatedStateMachineInput = True
- self.input(tcpdfa.SYN)
-
- SEG_ACK = packet.relativeAck() # aliasing this for easier reading w/
- # the RFC
- if packet.ack:
- if (self.oldestUnackedSendSeqNum < SEG_ACK and
- SEG_ACK <= self.nextSendSeqNum):
- # According to the spec, an 'acceptable ack
- rq = self.retransmissionQueue
- while rq:
- segmentOnQueue = rq[0]
- qSegSeq = segmentOnQueue.relativeSeq()
- if qSegSeq + segmentOnQueue.segmentLength() <= SEG_ACK:
- # fully acknowledged, as per RFC!
- rq.pop(0)
- sminput = None
- self.sendWindowRemaining += segmentOnQueue.segmentLength()
- # print 'inc send window', self, self.sendWindowRemaining
- if segmentOnQueue.syn:
- if packet.syn:
- sminput = tcpdfa.SYN_ACK
- else:
- sminput = tcpdfa.ACK
- elif segmentOnQueue.fin:
- sminput = tcpdfa.ACK
- if sminput is not None:
- # print 'ack input:', segmentOnQueue, packet, sminput
- generatedStateMachineInput = True
- self.input(sminput)
- else:
- break
- else:
- # write buffer is empty; alert the application layer.
- self._writeBufferEmpty()
- self.oldestUnackedSendSeqNum = SEG_ACK
-
- if packet.syn:
- assert generatedStateMachineInput
- return
-
- # XXX TODO: examine 'window' field and adjust sendWindowRemaining
- # is it 'occupying a portion of valid receive sequence space'? I think
- # this means 'packet which might acceptably contain useful data'
- if not packet.segmentLength():
- assert packet.ack, "What the _HELL_ is wrong with this packet:" +str(packet)
- return
-
- if not segmentAcceptable(self.nextRecvSeqNum,
- self.recvWindow,
- packet.relativeSeq(),
- packet.segmentLength()):
- # We have to transmit an ack here since it's old data. We probably
- # need to ack in more states than just ESTABLISHED... but which
- # ones?
- if not self.retransmissionQueue:
- self.originate(ack=True)
- return
-
- # OK! It's acceptable! Let's process the various bits of data.
- # Where is the useful data in the packet?
- if packet.relativeSeq() > self.nextRecvSeqNum:
- # XXX: Here's what's going on. Data can be 'in the window', but
- # still in the future. For example, if I have a window of length 3
- # and I send segments DATA1(len 1) DATA2(len 1) FIN and you receive
- # them in the order FIN DATA1 DATA2, you don't actually want to
- # process the FIN until you've processed the data.
-
- # For the moment we are just dropping anything that isn't exactly
- # the next thing we want to process. This is perfectly valid;
- # these packets might have been dropped, so the other end will have
- # to retransmit them anyway.
- return
-
- if packet.dlen:
- assert not packet.syn, 'no seriously I _do not_ know how to handle this'
- usefulData = packet.data[self.nextRecvSeqNum - packet.relativeSeq():]
- # DONT check/slice the window size here, the acceptability code
- # checked it, we can over-ack if the other side is buggy (???)
- if self.protocol is not None:
- try:
- self.protocol.dataReceived(usefulData)
- except:
- log.err()
- self.loseConnection()
-
- self.nextRecvSeqNum += packet.segmentLength()
- if self.state == tcpdfa.ESTABLISHED:
- # In all other states, the state machine takes care of sending ACKs
- # in its output process.
- self.originate(ack=True)
-
- if packet.fin:
- self.input(tcpdfa.FIN)
-
-
- def getHost(self):
- tupl = self.ptcp.transport.getHost()
- return PTCPAddress((tupl.host, tupl.port),
- self.pseudoPortPair)
-
- def getPeer(self):
- return PTCPAddress(self.peerAddressTuple,
- self.pseudoPortPair)
-
- _outgoingBytes = ''
- _nagle = None
-
- def write(self, bytes):
- assert not self.disconnected, 'Writing to a transport that was already disconnected.'
- self._outgoingBytes += bytes
- self._writeLater()
-
-
- def writeSequence(self, seq):
- self.write(''.join(seq))
-
-
- def _writeLater(self):
- if self._nagle is None:
- self._nagle = reactor.callLater(0.001, self._reallyWrite)
-
- def _originateOneData(self):
- amount = min(self.sendWindowRemaining, self.mtu)
- sendOut = self._outgoingBytes[:amount]
- # print 'originating data packet', len(sendOut)
- self._outgoingBytes = self._outgoingBytes[amount:]
- self.sendWindowRemaining -= len(sendOut)
- self.originate(ack=True, data=sendOut)
-
- def _reallyWrite(self):
- # print self, 'really writing', self._paused
- self._nagle = None
- if self._outgoingBytes:
- # print 'window and bytes', self.sendWindowRemaining, len(self._outgoingBytes)
- while self.sendWindowRemaining and self._outgoingBytes:
- self._originateOneData()
-
- _retransmitter = None
- _retransmitTimeout = 0.5
-
- def _retransmitLater(self):
- assert self.state != tcpdfa.CLOSED
- if self._retransmitter is None:
- self._retransmitter = reactor.callLater(self._retransmitTimeout, self._reallyRetransmit)
-
- def _stopRetransmitting(self):
- # used both as a quick-and-dirty test shutdown hack and a way to shut
- # down when we die...
- if self._retransmitter is not None:
- self._retransmitter.cancel()
- self._retransmitter = None
- if self._nagle is not None:
- self._nagle.cancel()
- self._nagle = None
- if self._closeWaitLoseConnection is not None:
- self._closeWaitLoseConnection.cancel()
- self._closeWaitLoseConnection = None
-
- def _reallyRetransmit(self):
- # XXX TODO: packet fragmentation & coalescing.
- # print 'Wee a retransmit! What I got?', self.retransmissionQueue
- self._retransmitter = None
- if self.retransmissionQueue:
- for packet in self.retransmissionQueue:
- packet.retransmitCount -= 1
- if packet.retransmitCount:
- packet.ackNum = self.currentAckNum()
- self.ptcp.sendPacket(packet)
- else:
- self.input(tcpdfa.TIMEOUT)
- return
- self._retransmitLater()
-
- disconnecting = False # This is *TWISTED* level state-machine stuff,
- # not TCP-level.
-
- def loseConnection(self):
- if not self.disconnecting:
- self.disconnecting = True
- if not self._outgoingBytes:
- self._writeBufferEmpty()
-
-
- def _writeBufferEmpty(self):
- if self._outgoingBytes:
- self._reallyWrite()
- elif self.producer is not None:
- if (not self.streamingProducer) or self.producerPaused:
- self.producerPaused = False
- self.producer.resumeProducing()
- elif self.disconnecting and (not self.disconnected
- or self.state == tcpdfa.CLOSE_WAIT):
- self.input(tcpdfa.APP_CLOSE)
-
-
- def _writeBufferFull(self):
- # print 'my write buffer is full'
- if (self.producer is not None
- and not self.producerPaused):
- self.producerPaused = True
- # print 'producer pausing'
- self.producer.pauseProducing()
- # print 'producer paused'
- else:
- # print 'but I am not telling my producer to pause!'
- # print ' ', self.producer, self.streamingProducer, self.producerPaused
- pass
-
-
- disconnected = False
- producer = None
- producerPaused = False
- streamingProducer = False
-
- def registerProducer(self, producer, streaming):
- if self.producer is not None:
- raise RuntimeError(
- "Cannot register producer %s, "
- "because producer %s was never unregistered."
- % (producer, self.producer))
- if self.disconnected:
- producer.stopProducing()
- else:
- self.producer = producer
- self.streamingProducer = streaming
- if not streaming and not self._outgoingBytes:
- producer.resumeProducing()
-
- def unregisterProducer(self):
- self.producer = None
- if not self._outgoingBytes:
- self._writeBufferEmpty()
-
- _paused = False
- def pauseProducing(self):
- self._paused = True
-
- def resumeProducing(self):
- self._paused = False
-
- def currentAckNum(self):
- return (self.nextRecvSeqNum + self.peerSendISN) % (2**32)
-
- def originate(self, data='', syn=False, ack=False, fin=False):
- if syn:
- # We really should be randomizing the ISN but until we finish the
- # implementations of the various bits of wraparound logic that were
- # started with relativeSequence
- assert self.nextSendSeqNum == 0
- assert self.hostSendISN == 0
- p = PTCPPacket.create(self.hostPseudoPort,
- self.peerPseudoPort,
- seqNum=(self.nextSendSeqNum + self.hostSendISN) % (2**32),
- ackNum=self.currentAckNum(),
- data=data,
- window=self.recvWindow,
- syn=syn, ack=ack, fin=fin,
- destination=self.peerAddressTuple)
- # do we want to enqueue this packet for retransmission?
- sl = p.segmentLength()
- self.nextSendSeqNum += sl
-
- if p.mustRetransmit():
- # print self, 'originating retransmittable packet', len(self.retransmissionQueue)
- if self.retransmissionQueue:
- if self.retransmissionQueue[-1].fin:
- raise AssertionError("Sending %r after FIN??!" % (p,))
- # print 'putting it on the queue'
- self.retransmissionQueue.append(p)
- # print 'and sending it later'
- self._retransmitLater()
- if not self.sendWindowRemaining: # len(self.retransmissionQueue) > 5:
- # print 'oh no my queue is too big'
- # This is a random number (5) because I ought to be summing the
- # packet lengths or something.
- self._writeBufferFull()
- else:
- # print 'my queue is still small enough', len(self.retransmissionQueue), self, self.sendWindowRemaining
- pass
- self.ptcp.sendPacket(p)
-
- # State machine transition definitions, hooray.
- def transition_SYN_SENT_to_CLOSED(self):
- """
- The connection never got anywhere. Goodbye.
- """
- # XXX CONNECTOR API OMFG
- self.factory.clientConnectionFailed(None, error.TimeoutError())
-
-
- wasEverListen = False
-
- def enter_LISTEN(self):
- # Spec says this is necessary for RST handling; we need it for making
- # sure it's OK to bind port numbers.
- self.wasEverListen = True
-
- def enter_CLOSED(self):
- self.ptcp.connectionClosed(self)
- self._stopRetransmitting()
- if self._timeWaitCall is not None:
- self._timeWaitCall.cancel()
- self._timeWaitCall = None
-
- _timeWaitCall = None
- _timeWaitTimeout = 0.01 # REALLY fast timeout, right now this is for
- # the tests...
-
- def enter_TIME_WAIT(self):
- self._stopRetransmitting()
- self._timeWaitCall = reactor.callLater(self._timeWaitTimeout, self._do2mslTimeout)
-
- def _do2mslTimeout(self):
- self._timeWaitCall = None
- self.input(tcpdfa.TIMEOUT)
-
- peerAddressTuple = None
-
- def transition_LISTEN_to_SYN_SENT(self):
- """
- Uh, what? We were listening and we tried to send some bytes.
- This is an error for PTCP.
- """
- raise StateError("You can't write anything until someone connects to you.")
-
-# def invalidInput(self, datum):
-# print self, self.protocol, 'invalid input', datum
-
- def pseudoPortPair():
- def get(self):
- return (self.hostPseudoPort,
- self.peerPseudoPort)
- return get,
- pseudoPortPair = property(*pseudoPortPair())
-
- def enter_ESTABLISHED(self):
- """
- We sent out SYN, they acknowledged it. Congratulations, you
- have a new baby connection.
- """
- assert not self.disconnecting
- assert not self.disconnected
- try:
- p = self.factory.buildProtocol(PTCPAddress(
- self.peerAddressTuple, self.pseudoPortPair))
- p.makeConnection(self)
- except:
- log.msg("Exception during PTCP connection setup.")
- log.err()
- self.loseConnection()
- else:
- self.protocol = p
-
- def exit_ESTABLISHED(self):
- assert not self.disconnected
- self.disconnected = True
- try:
- self.protocol.connectionLost(Failure(CONNECTION_DONE))
- except:
- log.err()
- self.protocol = None
-
- if self.producer is not None:
- try:
- self.producer.stopProducing()
- except:
- log.err()
- self.producer = None
-
-
- _closeWaitLoseConnection = None
-
- def enter_CLOSE_WAIT(self):
- # Twisted automatically reacts to network half-close by issuing a full
- # close.
- self._closeWaitLoseConnection = reactor.callLater(0.01, self._loseConnectionBecauseOfCloseWait)
-
- def _loseConnectionBecauseOfCloseWait(self):
- self._closeWaitLoseConnection = None
- self.loseConnection()
-
- def immediateShutdown(self):
- """_IMMEDIATELY_ shut down this connection, sending one (non-retransmitted)
- app-close packet, emptying our buffers, clearing our producer and
- getting ready to die right after this call.
- """
- self._outgoingBytes = ''
- if self.state == tcpdfa.ESTABLISHED:
- self.input(tcpdfa.APP_CLOSE)
- self._stopRetransmitting()
- self._reallyRetransmit()
-
- # All states that we can reasonably be in handle a timeout; force our
- # connection to think that it's become desynchronized with the other
- # end so that it will totally shut itself down.
-
- self.input(tcpdfa.TIMEOUT)
- assert self._retransmitter is None
- assert self._nagle is None
-
- def output_ACK(self):
- self.originate(ack=True)
-
- def output_FIN(self):
- self.originate(fin=True)
-
- def output_SYN_ACK(self):
- self.originate(syn=True, ack=True)
-
- def output_SYN(self):
- self.originate(syn=True)
-
-class PTCPAddress(object):
- # garbage
-
- def __init__(self, (host, port), (pseudoHostPort, pseudoPeerPort)):
- self.host = host
- self.port = port
- self.pseudoHostPort = pseudoHostPort
- self.pseudoPeerPort = pseudoPeerPort
-
- def __repr__(self):
- return 'PTCPAddress((%r, %r), (%r, %r))' % (
- self.host, self.port,
- self.pseudoHostPort,
- self.pseudoPeerPort)
-
-
-
-class _PendingEvent(object):
- def __init__(self):
- self.listeners = []
-
-
- def deferred(self):
- d = Deferred()
- self.listeners.append(d)
- return d
-
-
- def callback(self, result):
- l = self.listeners
- self.listeners = []
- for d in l:
- d.callback(result)
-
-
- def errback(self, result=None):
- if result is None:
- result = Failure()
- l = self.listeners
- self.listeners = []
- for d in l:
- d.errback(result)
-
-
-
-class PTCP(protocol.DatagramProtocol):
- """
- L{PTCP} implements a strongly TCP-like protocol on top of UDP. It
- provides a transport which is connection-oriented, streaming,
- ordered, and reliable.
-
- @ivar factory: A L{ServerFactory} which is used to create
- L{IProtocol} providers whenever a new PTCP connection is made
- to this port.
-
- @ivar _connections: A mapping of endpoint addresses to connection
- objects. These are the active connections being multiplexed
- over this UDP port. Many PTCP connections may run over the
- same L{PTCP} instance, communicating with many different
- remote hosts as well as multiplexing different PTCP
- connections to the same remote host. The mapping keys,
- endpoint addresses, are three-tuples of:
-
- - The destination pseudo-port which is always C{1}
- - The source pseudo-port
- - A (host, port) tuple giving the UDP address of a PTCP
- peer holding the other side of the connection
-
- The mapping values, connection objects, are L{PTCPConnection}
- instances.
- @type _connections: C{dict}
-
- """
- # External API
-
- def __init__(self, factory):
- self.factory = factory
- self._allConnectionsClosed = _PendingEvent()
-
-
- def connect(self, factory, host, port, pseudoPort=1):
- """
- Attempt to establish a new connection via PTCP to the given
- remote address.
-
- @param factory: A L{ClientFactory} which will be used to
- create an L{IProtocol} provider if the connection is
- successfully set up, or which will have failure callbacks
- invoked on it otherwise.
-
- @param host: The IP address of another listening PTCP port to
- connect to.
- @type host: C{str}
-
- @param port: The port number of that other listening PTCP port
- to connect to.
- @type port: C{int}
-
- @param pseudoPort: Not really implemented. Do not pass a
- value for this parameter or things will break.
-
- @return: A L{PTCPConnection} instance representing the new
- connection, but you really shouldn't use this for
- anything. Write a protocol!
- """
- sourcePseudoPort = genConnID() % MAX_PSEUDO_PORT
- conn = self._connections[(pseudoPort, sourcePseudoPort, (host, port))
- ] = PTCPConnection(
- sourcePseudoPort, pseudoPort, self, factory, (host, port))
- conn.input(tcpdfa.APP_ACTIVE_OPEN)
- return conn
-
- def sendPacket(self, packet):
- if self.transportGoneAway:
- return
- self.transport.write(packet.encode(), packet.destination)
-
-
- # Internal stuff
- def startProtocol(self):
- self.transportGoneAway = False
- self._lastConnID = 10 # random.randrange(2 ** 32)
- self._connections = {}
-
- def _finalCleanup(self):
- """
- Clean up all of our connections by issuing application-level close and
- stop notifications, sending hail-mary final FIN packets (which may not
- reach the other end, but nevertheless can be useful) when possible.
- """
- for conn in self._connections.values():
- conn.immediateShutdown()
- assert not self._connections
-
- def stopProtocol(self):
- """
- Notification from twisted that our underlying port has gone away;
- make sure we're not going to try to send any packets through our
- transport and blow up, then shut down all of our protocols, issuing
- appr
- opriate application-level messages.
- """
- self.transportGoneAway = True
- self._finalCleanup()
-
- def cleanupAndClose(self):
- """
- Clean up all remaining connections, then close our transport.
-
- Although in a pinch we will do cleanup after our socket has gone away
- (if it does so unexpectedly, above in stopProtocol), we would really
- prefer to do cleanup while we still have access to a transport, since
- that way we can force out a few final packets and save the remote
- application an awkward timeout (if it happens to get through, which
- is generally likely).
- """
- self._finalCleanup()
- return self._stop()
-
- def datagramReceived(self, bytes, addr):
- if len(bytes) < _fixedSize:
- # It can't be any good.
- return
-
- pkt = PTCPPacket.decode(bytes, addr)
- try:
- pkt.verifyChecksum()
- except TruncatedDataError:
-# print '(ptcp packet truncated: %r)' % (pkt,)
- self.sendPacket(
- PTCPPacket.create(
- pkt.destPseudoPort,
- pkt.sourcePseudoPort,
- 0,
- 0,
- struct.pack('!H', len(pkt.data)),
- stb=True,
- destination=addr))
- except GarbageDataError:
- print "garbage data!", pkt
- except ChecksumMismatchError, cme:
- print "bad checksum", pkt, cme
- print repr(pkt.data)
- print hex(pkt.checksum), hex(pkt.computeChecksum())
- else:
- self.packetReceived(pkt)
-
- stopped = False
- def _stop(self, result=None):
- if not self.stopped:
- self.stopped = True
- return self.transport.stopListening()
- else:
- return defer.succeed(None)
-
- def waitForAllConnectionsToClose(self):
- """
- Wait for all currently-open connections to enter the 'CLOSED' state.
- Currently this is only usable from test fixtures.
- """
- if not self._connections:
- return self._stop()
- return self._allConnectionsClosed.deferred().addBoth(self._stop)
-
- def connectionClosed(self, ptcpConn):
- packey = (ptcpConn.peerPseudoPort, ptcpConn.hostPseudoPort,
- ptcpConn.peerAddressTuple)
- del self._connections[packey]
- if ((not self.transportGoneAway) and
- (not self._connections) and
- self.factory is None):
- self._stop()
- if not self._connections:
- self._allConnectionsClosed.callback(None)
-
- def packetReceived(self, packet):
- packey = (packet.sourcePseudoPort, packet.destPseudoPort, packet.peerAddressTuple)
- if packey not in self._connections:
- if packet.flags == _SYN and packet.destPseudoPort == 1: # SYN and _ONLY_ SYN set.
- conn = PTCPConnection(packet.destPseudoPort,
- packet.sourcePseudoPort, self,
- self.factory, packet.peerAddressTuple)
- conn.input(tcpdfa.APP_PASSIVE_OPEN)
- self._connections[packey] = conn
- else:
- log.msg("corrupted packet? %r %r %r" % (packet,packey, self._connections))
- return
- try:
- self._connections[packey].packetReceived(packet)
- except:
- log.msg("PTCPConnection error on %r:" % (packet,))
- log.err()
- del self._connections[packey]
=== removed file 'Vertex/vertex/q2q.py'
--- Vertex/vertex/q2q.py 2012-03-14 23:42:53 +0000
+++ Vertex/vertex/q2q.py 1970-01-01 00:00:00 +0000
@@ -1,2763 +0,0 @@
-# -*- test-case-name: vertex.test.test_q2q -*-
-# Copyright 2005-2008 Divmod, Inc. See LICENSE file for details
-
-"""
-I{Quotient to Quotient} protocol implementation.
-"""
-
-# stdlib
-import itertools
-from hashlib import md5
-import struct
-import datetime
-import time
-from collections import namedtuple
-
-from pprint import pformat
-
-from zope.interface import implements
-
-# twisted
-from twisted.internet import reactor, defer, interfaces, protocol, error
-from twisted.internet.main import CONNECTION_DONE
-from twisted.internet.ssl import (
- CertificateRequest, Certificate, PrivateCertificate, KeyPair,
- DistinguishedName)
-from twisted.python import log
-from twisted.python.failure import Failure
-from twisted.application import service
-
-# twisted.cred
-from twisted.cred.checkers import ICredentialsChecker
-from twisted.cred.portal import IRealm, Portal
-from twisted.cred.credentials import IUsernamePassword, UsernamePassword
-from twisted.cred.error import UnauthorizedLogin
-
-from twisted.protocols.amp import Argument, Boolean, Integer, String, Unicode, ListOf, AmpList
-from twisted.protocols.amp import AmpBox, Command, StartTLS, ProtocolSwitchCommand, AMP
-from twisted.protocols.amp import _objectsToStrings
-
-# vertex
-from vertex import subproducer, ptcp
-from vertex import endpoint, ivertex
-from vertex.conncache import ConnectionCache
-
-MESSAGE_PROTOCOL = 'q2q-message'
-port = 8788
-
-class ConnectionError(Exception):
- pass
-
-class AttemptsFailed(ConnectionError):
- pass
-
-class NoAttemptsMade(ConnectionError):
- pass
-
-class VerifyError(Exception):
- pass
-
-class BadCertificateRequest(VerifyError):
- pass
-
-class IgnoreConnectionFailed(protocol.ClientFactory):
- def __init__(self, realFactory):
- self.realFactory = realFactory
-
- def clientConnectionLost(self, connector, reason):
- self.realFactory.clientConnectionLost(connector, reason)
-
- def clientConnectionFailed(self, connector, reason):
- pass
-
- def buildProtocol(self, addr):
- return self.realFactory.buildProtocol(addr)
-
-class Q2QAddress(object):
- def __init__(self, domain, resource=None):
- self.resource = resource
- self.domain = domain
-
- def domainAddress(self):
- """ Return an Address object which is the same as this one with ONLY the
- 'domain' attribute set, not 'resource'.
-
- May return 'self' if 'resource' is already None.
- """
- if self.resource is None:
- return self
- else:
- return Q2QAddress(self.domain)
-
- def claimedAsIssuerOf(self, cert):
- """
- Check if the information in a provided certificate *CLAIMS* to be issued by
- this address.
-
- PLEASE NOTE THAT THIS METHOD IS IN NO WAY AUTHORITATIVE. It does not
- perform any cryptographic checks.
-
- Currently this check is if L{Q2QAddress.__str__}C{(self)} is equivalent
- to the commonName on the certificate's issuer.
- """
- return cert.getIssuer().commonName == str(self)
-
- def claimedAsSubjectOf(self, cert):
- """
- Check if the information in a provided certificate *CLAIMS* to be
- provided for use by this address.
-
- PLEASE NOTE THAT THIS METHOD IS IN NO WAY AUTHORITATIVE. It does not
- perform any cryptographic checks.
-
- Currently this check is if L{Q2QAddress.__str__}C{(self)} is equivalent
- to the commonName on the certificate's subject.
- """
- return cert.getSubject().commonName == str(self)
-
- def __cmp__(self, other):
- if not isinstance(other, Q2QAddress):
- return cmp(self.__class__, other.__class__)
- return cmp((self.domain, self.resource), (other.domain, other.resource))
-
- def __iter__(self):
- return iter((self.resource, self.domain))
-
- def __str__(self):
- """
- Return a string of the normalized form of this address. e.g.::
-
- glyph@xxxxxxxxxx # for a user
- divmod.com # for a domain
- """
- if self.resource:
- resource = self.resource + '@'
- else:
- resource = ''
- return (resource + self.domain).encode('utf-8')
-
- def __repr__(self):
- return '<Q2Q at %s>' % self.__str__()
-
- def __hash__(self):
- return hash(str(self))
-
- def fromString(cls, string):
- args = string.split("@",1)
- args.reverse()
- return cls(*args)
- fromString = classmethod(fromString)
-
-
-class VirtualTransportAddress:
- def __init__(self, underlying):
- self.underlying = underlying
-
- def __repr__(self):
- return 'VirtualTransportAddress(%r)' % (self.underlying,)
-
-class Q2QTransportAddress:
- """
- The return value of getPeer() and getHost() for Q2Q-enabled transports.
- Passed to buildProtocol of factories passed to listenQ2Q.
-
- @ivar underlying: The return value of the underlying transport's getPeer()
- or getHost(); an address which indicates the path which the bytes carrying
- Q2Q traffic are travelling over. It is tempting to think of this as a
- 'physical' layer but that it not necessarily accurate; there are
- potentially multiple layers of wrapping on any Q2Q transport, as an SSL
- transport may be tunnelled over a UDP NAT-traversal layer. Implements
- C{IAddress} from Twisted, for all the good that will do you.
-
- @ivar logical: a L{Q2QAddress}, The logical peer; the user ostensibly
- listening to data on the other end of this transport.
-
- @ivar protocol: a L{str}, the name of the protocol that is connected.
- """
-
- def __init__(self, underlying, logical, protocol):
- self.underlying = underlying
- self.logical = logical
- self.protocol = protocol
-
- def __repr__(self):
- return 'Q2QTransportAddress(%r, %r, %r)' % (
- self.underlying,
- self.logical,
- self.protocol)
-
-
-class AmpTime(Argument):
- def toString(self, inObject):
- return inObject.strftime("%Y-%m-%dT%H:%M:%S")
-
-
- def fromString(self, inString):
- return datetime.datetime.strptime(inString, "%Y-%m-%dT%H:%M:%S")
-
-
-
-class Q2QAddressArgument(Argument):
- fromString = Q2QAddress.fromString
- toString = Q2QAddress.__str__
-
-class HostPort(Argument):
- def toString(self, inObj):
- return "%s:%d" % tuple(inObj)
-
- def fromString(self, inStr):
- host, sPort = inStr.split(":")
- return (host, int(sPort))
-
-
-
-class _BinaryLoadable(String):
- def toString(self, arg):
- assert isinstance(arg, self.loader), "%r not %r" % (arg, self.loader)
- return String.toString(self, arg.dump())
-
- def fromString(self, arg):
- return self.loader.load(String.fromString(self, arg))
-
-class CertReq(_BinaryLoadable):
- loader = CertificateRequest
-
-class Cert(_BinaryLoadable):
- loader = Certificate
-
-from twisted.internet import protocol
-
-class Q2QClientProtocolFactoryWrapper:
-
- def __init__(self, service, cpf, fromAddress, toAddress, protocolName,
- connectionEstablishedDeferred):
- self.service = service
- self.cpf = cpf
- self.fromAddress = fromAddress
- self.toAddress = toAddress
- self.protocolName = protocolName
- self.connectionEstablishedDeferred = connectionEstablishedDeferred
- connectionEstablishedDeferred.addCallback(self.setMyClient)
-
- myClient = None
- def setMyClient(self, myClient):
- # print '***CLIENT SET***', self, self.fromAddress, self.toAddress, self.cpf
- self.myClient = myClient
- return myClient
-
- def buildProtocol(self, addr):
- # xxx modify addr to include q2q information.
- subProto = self.cpf.buildProtocol(self.toAddress)
- myProto = SeparateConnectionTransport(self.service, subProto, self.fromAddress,
- self.toAddress, self.protocolName,
- self.connectionEstablishedDeferred)
- return myProto
-
- def clientConnectionFailed(self, connector, reason):
- # DON'T forward this to our client protocol factory; only one attempt
- # has failed; let that happen later, when _ALL_ attempts have failed.
- assert self.myClient is None
- self.connectionEstablishedDeferred.errback(reason)
-
- def clientConnectionLost(self, connector, reason):
- # as in clientConnectionFailed, don't bother to forward; this
- # clientConnectionLost is actually a clientConnectionFailed for the
- # underlying transport.
- if self.myClient is not None:
- # forward in this case because it's likely that we need to pass it
- # along...
- self.cpf.clientConnectionLost(connector, reason)
-
- def doStart(self):
- self.cpf.doStart()
-
- def doStop(self):
- self.cpf.doStop()
-
-class ImmediatelyLoseConnection(protocol.Protocol):
- def connectionMade(self):
- self.transport.loseConnection()
-
-class AbstractConnectionAttempt(protocol.ClientFactory):
-
-
- def __init__(self, method, q2qproto, connectionID, fromAddress, toAddress,
- protocolName, clientProtocolFactory, issueGreeting=False):
- self.method = method
- self.q2qproto = q2qproto
- assert isinstance(connectionID, str)
- self.connectionID = connectionID
- self.q2qproto = q2qproto
- self.fromAddress = fromAddress
- self.toAddress = toAddress
- self.protocolName = protocolName
- self.deferred = defer.Deferred()
- self.clientProtocolFactory = Q2QClientProtocolFactoryWrapper(
- q2qproto.service,
- clientProtocolFactory, fromAddress, toAddress, protocolName,
- self.deferred)
- self.issueGreeting = issueGreeting
-
-
- def startAttempt(self):
- """
- +-+
- |?|
- +-+
- """
- raise NotImplementedError()
-
-
- q2qb = None
-
- cancelled = False
-
- def buildProtocol(self, addr):
- if self.cancelled:
- return ImmediatelyLoseConnection()
- assert self.q2qb is None
- self.q2qb = Q2QBootstrap(
- self.connectionID, self.clientProtocolFactory)
- return self.q2qb
-
- def clientConnectionFailed(self, connector, reason):
- """
- """
- # Don't bother forwarding. In fact this should probably never be
- # called because we're not bothering to forward them along from
- # Q2QClientProtocolFactoryWrapper
-
- def clientConnectionLost(self, connector, reason):
- """
- """
- # we don't care - this will be handled by Q2QBootstrap.
-
- def cancel(self):
- """
- - Stop attempting to connect.
-
- - If a connection is somehow made after this has been cancelled, reject
- it.
-
- - Clean up any resources, such as listening UDP or TCP ports,
- associated with this connection attempt [obviously, that are unshared
- by other connection attempt]
-
- """
- self.cancelled = True
-
-
-class TCPConnectionAttempt(AbstractConnectionAttempt):
- attempted = False
- def startAttempt(self):
- assert not self.attempted
- self.attempted = True
- reactor.connectTCP(self.method.host, self.method.port, self)
- return self.deferred
-
-
-class TCPMethod:
- def __init__(self, hostport):
- self.host, port = hostport.split(':')
- self.port = int(port)
-
- attemptFactory = TCPConnectionAttempt
- relayable = True
- ptype = 'tcp'
-
- def toString(self):
- return '%s@%s:%d' % (self.ptype, self.host, self.port)
-
- def __repr__(self):
- return '<%s>'%self.toString()
-
- def attempt(self, *a):
- return [self.attemptFactory(self, *a)]
-
-connectionCounter = itertools.count().next
-connectionCounter()
-
-class VirtualConnectionAttempt(AbstractConnectionAttempt):
- attempted = False
- def startAttempt(self):
- assert not self.attempted
- self.attempted = True
- cid = connectionCounter()
- if self.q2qproto.isServer:
- cid = -cid
- innerTransport = VirtualTransport(self.q2qproto, cid, self, True)
- def startit(result):
- proto = innerTransport.startProtocol()
- return self.deferred
-
- d = self.q2qproto.callRemote(Virtual, id=cid)
- d.addCallback(startit)
- return d
-
-
-class VirtualMethod:
- def __init__(self, virt=None):
- pass
-
- relayable = False
-
- def toString(self):
- return 'virtual'
-
- def __repr__(self):
- return '<%s>' % (self.toString(),)
-
- def attempt(self, *a):
- return [VirtualConnectionAttempt(self, *a)]
-
-
-class _PTCPConnectionAttempt1NoPress(AbstractConnectionAttempt):
- attempted = False
- def startAttempt(self):
- assert not self.attempted
- self.attempted = True
- svc = self.q2qproto.service
- dsp = svc.dispatcher
- dsp.connectPTCP(
- self.method.host, self.method.port, self,
- svc.sharedUDPPortnum)
- return self.deferred
-
-class _PTCPConnectionAttemptPress(AbstractConnectionAttempt):
- attempted = False
- def startAttempt(self):
- assert not self.attempted
- self.attempted = True
-
- svc = self.q2qproto.service
- dsp = svc.dispatcher
- newPort = self.newPort = dsp.bindNewPort()
- dsp.connectPTCP(
- self.method.host, self.method.port, self,
- newPort)
-
- return self.deferred
-
- def cancel(self):
- if not self.cancelled:
- self.q2qproto.service.dispatcher.unbindPort(self.newPort)
- else:
- print 'totally wacky, [press] cancelled twice!'
- AbstractConnectionAttempt.cancel(self)
-
-class PTCPMethod(TCPMethod):
- """Pseudo-TCP method.
- """
- ptype = 'ptcp'
-
- def attempt(self, *a):
- return [_PTCPConnectionAttempt1NoPress(self, *a),
- _PTCPConnectionAttemptPress(self, *a)]
-
-class RPTCPConnectionAttempt(AbstractConnectionAttempt):
- attempted = False
- def startAttempt(self):
- assert not self.attempted
- self.attempted = True
-
- realLocalUDP = self.newPort = self.q2qproto.service.dispatcher.seedNAT((self.method.host, self.method.port))
- # self.host and self.port are remote host and port
- # realLocalUDP is a local port
-
- # The arguments here are given from the perspective of the recipient of
- # the command. we are asking the recipient of the connection to map a
- # NAT entry of a pre-existing listening UDP socket on their end of the
- # connection by sending us some traffic. therefore the src is their
- # endpoint, the dst is our endpoint, the user we are asking them to
- # send TO is us, the user we are asking them to accept this FROM is us.
-
- # we include protocol as an arg because this is helpful for relaying.
-
- def enbinden(boundereded):
- if not self.cancelled:
- self.q2qproto.service.dispatcher.connectPTCP(
- self.method.host, self.method.port, self, realLocalUDP
- )
- return self.deferred
-
- def swallowKnown(error):
- error.trap(ConnectionError)
- self.deferred.errback(CONNECTION_DONE)
- return self.deferred
-
- d = self.q2qproto.callRemote(
- BindUDP,
- q2qsrc=self.toAddress,
- q2qdst=self.fromAddress,
- protocol=self.protocolName,
- udpsrc=(self.method.host, self.method.port),
- udpdst=(self.q2qproto._determinePublicIP(), realLocalUDP))
- d.addCallbacks(enbinden, swallowKnown)
- return d
-
- def cancel(self):
- if not self.cancelled:
- self.q2qproto.service.dispatcher.unbindPort(self.newPort)
- else:
- print 'totally wacky, [rptcp] cancelled twice!'
- AbstractConnectionAttempt.cancel(self)
-
-
-
-
-class RPTCPMethod(TCPMethod):
- """ Certain NATs respond very poorly to seed traffic: e.g. if they receive
- unsolicited traffic to a particular port, they will make that outbound port
- unavailable for outbound traffic originated internally. The
- Reverse-Pseudo-TCP method is a way to have the *sender* send the first UDP
- packet, so they will bind it.
-
- This is a worst-case scenario: if both ends of the connection have NATs
- which behave this way, there is no way to establish a connection.
- """
-
- ptype = 'rptcp'
- attemptFactory = RPTCPConnectionAttempt
-
-
-class UnknownMethod:
-
- relayable = True
-
- def __init__(self, S):
- self.string = S
-
- def attemptConnect(self, q2qproto, connectionID, From, to,
- protocolName, protocolFactory):
- return defer.fail(Failure(ConnectionError(
- "unknown connection method: %s" % (self.string,))))
-
-
-_methodFactories = {'virtual': VirtualMethod,
- 'tcp': TCPMethod,
- 'ptcp': PTCPMethod,
- 'rptcp': RPTCPMethod}
-
-class Method(Argument):
- def toString(self, inObj):
- return inObj.toString()
-
-
- def fromString(self, inString):
- f = inString.split("@", 1)
- factoryName = f[0]
- if len(f) > 1:
- factoryData = f[1]
- else:
- factoryData = ''
- methodFactory = _methodFactories.get(factoryName, None)
- if methodFactory is None:
- factory = UnknownMethod(inString)
- else:
- factory = methodFactory(factoryData)
- return factory
-
-
-class Secure(StartTLS):
-
- commandName = "secure"
- arguments = StartTLS.arguments + [
- ('From', Q2QAddressArgument(optional=True)),
- ('to', Q2QAddressArgument()),
- ('authorize', Boolean())
- ]
-
-
-
-class Listen(Command):
- """
- A simple command for registering interest with an active Q2Q connection
- to hear from a server when others come calling. An occurrence of this
- command might have this appearance on the wire::
-
- C: -Command: Listen
- C: -Ask: 1
- C: From: glyph@xxxxxxxxxx
- C: Protocols: q2q-example, q2q-example2
- C: Description: some simple protocols
- C:
- S: -Answer: 1
- S:
-
- This puts some state on the server side that will affect any Connect
- commands with q2q-example or q2q-example2 in the Protocol: header.
- """
-
- commandName = 'listen'
- arguments = [
- ('From', Q2QAddressArgument()),
- ('protocols', ListOf(String())),
- ('description', Unicode())]
-
- result = []
-
-class ConnectionStartBox(AmpBox):
- def __init__(self, __transport):
- super(ConnectionStartBox, self).__init__()
- self.virtualTransport = __transport
-
- # XXX Overriding a private interface
- def _sendTo(self, proto):
- super(ConnectionStartBox, self)._sendTo(proto)
- self.virtualTransport.startProtocol()
-
-class Virtual(Command):
- commandName = 'virtual'
- result = []
-
- arguments = [('id', Integer())]
-
- def makeResponse(cls, objects, proto):
- tpt = objects.pop('__transport__')
- # XXX Using a private API
- return _objectsToStrings(
- objects, cls.response,
- ConnectionStartBox(tpt),
- proto)
-
- makeResponse = classmethod(makeResponse)
-
-class Identify(Command):
- """
- Respond to an IDENTIFY command with a self-signed certificate for the
- domain requested, assuming we are an authority for said domain. An
- occurrence of this command might have this appearance on the wire::
-
- C: -Command: Identify
- C: -Ask: 1
- C: Domain: divmod.com
- C:
- S: -Answer: 1
- S: Certificate: <<<base64-encoded self-signed certificate of divmod.com>>>
- S:
-
- """
-
- commandName = 'identify'
-
- arguments = [('subject', Q2QAddressArgument())]
-
- response = [('certificate', Cert())]
-
-class BindUDP(Command):
- """
- See UDPXMethod
- """
-
- commandName = 'bind-udp'
-
- arguments = [
- ('protocol', String()),
- ('q2qsrc', Q2QAddressArgument()),
- ('q2qdst', Q2QAddressArgument()),
- ('udpsrc', HostPort()),
- ('udpdst', HostPort()),
- ]
-
- errors = {ConnectionError: 'ConnectionError'}
-
- response = []
-
-class SourceIP(Command):
- """
- Ask a server on the public internet what my public IP probably is. An
- occurrence of this command might have this appearance on the wire::
-
- C: -Command: Source-IP
- C: -Ask: 1
- C:
- S: -Answer: 1
- S: IP: 4.3.2.1
- S:
-
- """
-
- commandName = 'source-ip'
-
- arguments = []
-
- response = [('ip', String())]
-
-class Inbound(Command):
- """
- Request information about where to connect to a particular resource.
-
- Generally speaking this is an "I want to connect to you" request.
-
- The format of this request is transport neutral except for the optional
- 'Udp_Source' header, which specifies an IP/Port pair for all receiving peers to
- send an almost-empty (suggested value of '\\r\\n') UDP packet to to help
- with NAT traversal issues.
-
- See L{Q2QService.connectQ2Q} for details.
-
- An occurrence of this command might have this appearance on the wire::
-
- C: -Command: Inbound
- C: -Ask: 1
- C: From: glyph@xxxxxxxxxx
- C: Id: 681949ffa3be@xxxxxxxxxxxxxxxxx
- C: To: radix@xxxxxxxxxxxxxxxxx
- C: Protocol: q2q-example
- C: Udp_Source: 1.2.3.4:4321
- C:
- S: -Answer: 1
- S: Listeners:
- S: Description: at lab
- S: Methods: tcp@18.38.12.4:3827, virtual
- S:
- S: Description: my home machine
- S: Methods: tcp@187.48.38.3:49812, udp@187.48.38.3:49814, virtual
-
- Now the connection-id has been registered and either client or server can
- issue WRITE or CLOSE commands.
-
- Failure modes::
-
- - "NotFound": the toResource or toDomain is invalid, or the resource does
- not speak that protocol.
-
- - "VerifyError": Authenticity or security for the requested connection
- could not be authorized. This is a fatal error: the connection will be
- dropped.
-
- The "Udp_Source" header indicates the address from which this Inbound chain
- originated. It is to be used to establish connections where possible
- between NATs which require traffic between two host/port pairs to be
- bidirectional before a "hole" is established, such as port restricted cone
- and symmetric NATs. (Note, this only has about a 30% probability of
- working on a symmetric NAT, but it's worth trying sometimes anyway). Any
- UDP-based connection methods (currently only Gin, but in principle others
- such as RTP, RTCP, SIP and Quake traffic) that wish to use this connection
- must first send some garbage traffic to the host/port specified by the
- "Udp_Source" header.
-
- The response is a list of "listeners" - a small (unicode) textual
- description of a host, plus a list of methods describing how to connect to
- it.
- """
-
- commandName = 'inbound'
- arguments = [('From', Q2QAddressArgument()),
- ('to', Q2QAddressArgument()),
- ('protocol', String()),
- ('udp_source', HostPort(optional=True))]
-
- response = [('listeners', AmpList(
- [('id', String()),
- ('certificate', Cert(optional=True)),
- ('methods', ListOf(Method())),
- ('expires', AmpTime()),
- ('description', Unicode())]))]
-
- errors = {KeyError: "NotFound"}
- fatalErrors = {VerifyError: "VerifyError"}
-
-class Outbound(Command):
- """Similar to Inbound, but _requires that the recipient already has the
- id parameter as an outgoing connection attempt_.
- """
- commandName = 'outbound'
-
- arguments = [('From', Q2QAddressArgument()),
- ('to', Q2QAddressArgument()),
- ('protocol', String()),
- ('id', String()),
- ('methods', ListOf(Method()))]
-
- response = []
-
- errors = {AttemptsFailed: 'AttemptsFailed'}
-
-class Sign(Command):
- commandName = 'sign'
- arguments = [('certificate_request', CertReq()),
- ('password', String())]
-
- response = [('certificate', Cert())]
-
- errors = {KeyError: "NoSuchUser",
- BadCertificateRequest: "BadCertificateRequest"}
-
-class Choke(Command):
- """Ask our peer to be quiet for a while.
- """
- commandName = 'Choke'
- arguments = [('id', Integer())]
- requiresAnswer = False
-
-
-class Unchoke(Command):
- """Reverse the effects of a choke.
- """
- commandName = 'Unchoke'
- arguments = [('id', Integer())]
- requiresAnswer = False
-
-
-def safely(f, *a, **k):
- """try/except around something, w/ twisted error handling.
- """
- try:
- f(*a,**k)
- except:
- log.err()
-
-class Q2Q(AMP, subproducer.SuperProducer):
- """ Quotient to Quotient protocol.
-
- At a low level, this uses a protocol called 'Juice' (JUice Is Concurrent
- Events), which is a simple rfc2822-inspired (although not -compliant)
- protocol for request/response pair hookup.
-
- At a higher level, it provides a mechanism for SSL certificate exchange,
- looking up physical locations of users' data, and switching into other
- protocols after an initial handshake.
-
- @ivar publicIP: The IP that the other end of the connection claims to know
- us by. This will be used when responding to L{Inbound} commands if the Q2Q
- service I am attached to does not specify a public IP to use.
-
- @ivar authorized: A boolean indicating whether SSL verification has taken
- place to ensure that this connection's peer has claimed an accurate identity.
- """
-
- protocolName = 'q2q'
- service = None
- publicIP = None
- authorized = False
-
- def __init__(self, *a, **kw):
- """ Q2Q instances should only be created by Q2QService. See
- L{Q2QService.connectQ2Q} and L{Q2QService.listenQ2Q}.
- """
- subproducer.SuperProducer.__init__(self)
- AMP.__init__(self, *a, **kw)
-
- def connectionMade(self):
- self.producingTransports = {}
- self.connections = {}
- self.listeningClient = []
- self.connectionObservers = []
- if self.service.publicIP is None:
- log.msg("Service has no public IP: determining")
- self.service.publicIP = self.transport.getHost().host
- self.service._publicIPIsReallyPrivate = True
- def rememberPublicIP(pubip):
- ip = pubip['ip']
- log.msg('remembering public ip as %r' % ip)
- self.publicIP = ip
- self.service.publicIP = ip
- self.service._publicIPIsReallyPrivate = False
- self.callRemote(SourceIP).addCallback(rememberPublicIP)
- else:
- log.msg("Using existing public IP: %r" % (self.service.publicIP,))
-
- def connectionLost(self, reason):
- ""
- AMP.connectionLost(self, reason)
- self._uncacheMe()
- self.producingTransports = {}
- for key, value in self.listeningClient:
- log.msg("removing remote listener for %r" % (key,))
- self.service.listeningClients[key].remove(value)
- self.listeningClient = []
- for xport in self.connections.values():
- safely(xport.connectionLost, reason)
- for observer in self.connectionObservers:
- safely(observer)
-
- def notifyOnConnectionLost(self, observer):
- ""
- self.connectionObservers.append(observer)
-
- def _bindUDP(self, q2qsrc, q2qdst, udpsrc, udpdst, protocol):
-
- # we are representing the src, because they are the ones being told to
- # originate a UDP packet.
-
- self.verifyCertificateAllowed(q2qsrc, q2qdst)
-
- # if I've got a local factory for this 3-tuple, do the bind if I own
- # this IP...
- srchost, srcport = udpsrc
-
- lcget = self.service.listeningClients.get((q2qsrc, protocol), ())
-
- bindery = []
-
- for (listener, listenCert, desc
- ) in lcget:
- # print 'looking at listener', listener
- # print listener.transport.getPeer().host, srchost
- if listener.transport.getPeer().host == srchost:
- # print 'bound in clients loop'
-
- d = listener.callRemote(
- BindUDP,
- q2qsrc=q2qsrc,
- q2qdst=q2qdst,
- udpsrc=udpsrc,
- udpdst=udpdst,
- protocol=protocol)
- def swallowKnown(err):
- err.trap(error.ConnectionDone, error.ConnectionLost)
- d.addErrback(swallowKnown)
- bindery.append(d)
- if bindery:
- # print 'bindery return', len(bindery)
- def _justADict(ign):
- return dict()
- return defer.DeferredList(bindery).addCallback(_justADict)
-
- # print 'what?', lcget
- if (self.service.getLocalFactories(q2qdst, q2qsrc, protocol)
- and srchost == self._determinePublicIP()):
- self.service.dispatcher.seedNAT(udpdst, srcport, conditional=True)
- # print 'bound locally'
- return dict()
- # print 'conn-error'
- raise ConnectionError("unable to find appropriate UDP binder")
-
- BindUDP.responder(_bindUDP)
-
- def _identify(self, subject):
- """
- Implementation of L{Identify}.
- """
- ourCA = self.service.certificateStorage.getPrivateCertificate(str(subject))
- return dict(certificate=ourCA)
- Identify.responder(_identify)
-
-
- def verifyCertificateAllowed(self,
- ourAddress,
- theirAddress):
- """
- Check that the certificate currently in use by this transport is valid to
- claim that the connection offers authorization for this host speaking
- for C{ourAddress}, to a host speaking for C{theirAddress}. The remote
- host (the one claiming to use theirAddress) may have a certificate
- which is issued for the domain for theirAddress or the full address
- given in theirAddress.
-
- This method runs B{after} cryptographic verification of the validity of
- certificates, although it does not perform any cryptographic checks
- itself. It depends on SSL connection handshaking - *and* the
- particular certificate lookup logic which prevents spoofed Issuer
- fields, to work properly. However, all it checks is the X509 names
- present in the certificates matching with the application-level
- security claims being made by our peer.
-
- An example of successful verification, because both parties have
- properly signed certificates for their usage from the domain they
- have been issued::
-
- our current certficate:
- issuer: divmod.com
- subject: glyph@xxxxxxxxxx
- their current certificate:
- issuer: twistedmatrix.com
- subject: exarkun@xxxxxxxxxxxxxxxxx
- Arguments to verifyCertificateAllowed:
- ourAddress: glyph@xxxxxxxxxx
- theirAddress: exarkun@xxxxxxxxxxxxxxxxx
- Result of verifyCertificateAllowed: None
-
- An example of rejected verification, because domain certificates are
- always B{self}-signed in Q2Q; verisign is not a trusted certificate
- authority for the entire internet as with some other TLS
- implementations::
-
- our current certificate:
- issuer: divmod.com
- subject: divmod.com
- their current certificate:
- issuer: verisign.com
- subject: twistedmatrix.com
- Arguments to verifyCertificateAllowed:
- ourAddress: divmod.com
- theirAddress: twistedmatrix.com
- Result of verifyCertificateAllowed: exception VerifyError raised
-
- Another example of successful verification, because we assume our
- current certificate is under the control of this side of the
- connection, so *any* claimed subject is considered acceptable::
-
- our current certificate:
- issuer: divmod.com
- subject: divmod.com
- their current certificate:
- issuer: divmod.com
- subject: glyph@xxxxxxxxxxxxxxxxx
- Arguments to verifyCertificateAllowed:
- ourAddress: divmod.com
- theirAddress: glyph@xxxxxxxxxxxxxxxxx
- Result of verifyCertificateAllowed: None
-
- Another example of successful verification, because the user is
- claiming to be anonymous; there is also a somewhat looser
- cryptographic check applied to signatures for anonymous
- connections::
-
- our current certificate:
- issuer: divmod.com
- subject: divmod.com
- their current certificate:
- issuer: @
- subject: @
- arguments to verifyCertificateAllowed:
- ourAddress: divmod.com
- theirAddress: @
- Result of verifyCertificateAllowed: None
-
- Accept anonymous connections with caution.
-
- @param ourAddress: a L{Q2QAddress} representing the address that we are
- supposed to have authority for, requested by our peer.
-
- @param theirAddress: a L{Q2QAddress} representing the address that our
- network peer claims to be communicating on behalf of. For example, if
- our peer is foobar.com they may claim to be operating on behalf of any
- user @foobar.com.
-
- @raise: L{VerifyError} if the certificates do not match the
- claimed addresses.
- """
-
- # XXX TODO: Somehow, it's got to be possible for a single cluster to
- # internally claim to be agents of any other host when issuing a
- # CONNECT; in other words, we always implicitly trust ourselves. Also,
- # we might want to issue anonymous CONNECTs over unencrypted
- # connections.
-
- # IOW: *we* can sign a certificate to be whoever, but the *peer* can
- # only sign the certificate to be the peer.
-
- # The easiest way to make this work is to issue ourselves a wildcard
- # certificate.
-
- if not self.authorized:
- if theirAddress.domain == '':
- # XXX TODO: document this rule, anonymous connections are
- # allowed to not be authorized because they are not making any
- # claims about who they are
-
- # XXX also TODO: make it so that anonymous connections are
- # disabled by default for most protocols
- return True
- raise VerifyError("No official negotiation has taken place.")
-
- peerCert = Certificate.peerFromTransport(self.transport)
- ourCert = self.hostCertificate
-
- ourClaimedDomain = ourAddress.domainAddress()
- theirClaimedDomain = theirAddress.domainAddress()
-
- # Sanity check #1: did we pick the right certificate on our end?
- if not ourClaimedDomain.claimedAsIssuerOf(ourCert):
- raise VerifyError(
- "Something has gone horribly wrong: local domain mismatch "
- "claim: %s actual: %s" % (ourClaimedDomain,
- ourCert.getIssuer()))
- if theirClaimedDomain.claimedAsIssuerOf(peerCert):
- # Their domain issued their certificate.
- if theirAddress.claimedAsSubjectOf(peerCert) or theirClaimedDomain.claimedAsSubjectOf(peerCert):
- return
- elif ourClaimedDomain.claimedAsIssuerOf(peerCert):
- # *our* domain can spoof *anything*
- return
- elif ourAddress.claimedAsIssuerOf(peerCert):
- # Neither our domain nor their domain signed this. Did *we*?
- # (Useful in peer-to-peer persistent transactions where we don't
- # want the server involved: exarkun@xxxxxxxxxxxxxxxxx can sign
- # glyph@xxxxxxxxxx's certificate).
- return
-
- raise VerifyError(
- "Us: %s Them: %s "
- "TheyClaimWeAre: %s TheyClaimTheyAre: %s" %
- (ourCert, peerCert,
- ourAddress, theirAddress))
-
- def _listen(self, protocols, From, description):
- """
- Implementation of L{Listen}.
- """
- # The peer is coming from a client-side representation of the user
- # described by 'From', and talking *to* a server-side representation of
- # the user described by 'From'.
- self.verifyCertificateAllowed(From, From)
- theirCert = Certificate.peerFromTransport(self.transport)
- for protocolName in protocols:
- if protocolName.startswith('.'):
- raise VerifyError(
- "Internal protocols are for server-server use _only_: %r" %
- protocolName)
-
- key = (From, protocolName)
- value = (self, theirCert, description)
- log.msg("%r listening for %r" % key)
- self.listeningClient.append((key, value))
- self.service.listeningClients.setdefault(key, []).append(value)
- return {}
- Listen.responder(_listen)
-
-
- def _inbound(self, From, to, protocol, udp_source=None):
- """
- Implementation of L{Inbound}.
- """
- # Verify stuff!
-
- self.verifyCertificateAllowed(to, From)
- return self.service.verifyHook(From, to, protocol
- ).addCallback(self._inboundimpl,
- From,
- to,
- protocol,
- udp_source).addErrback(
- lambda f: f.trap(KeyError) and dict(listeners=[]))
- Inbound.responder(_inbound)
-
- def _inboundimpl(self, ign, From, to, protocol, udp_source):
-
- # 2-tuples of factory, description
- srvfacts = self.service.getLocalFactories(From, to, protocol)
-
- result = [] # list of listener dicts
-
- if srvfacts:
- log.msg("local factories found for inbound request: %r" % (srvfacts,))
- localMethods = []
- publicIP = self._determinePublicIP()
- privateIP = self._determinePrivateIP()
- if self.service.inboundTCPPort is not None:
- tcpPort = self.service.inboundTCPPort.getHost().port
- localMethods.append(TCPMethod(
- '%s:%d' %
- (publicIP, tcpPort)))
- if publicIP != privateIP:
- localMethods.append(TCPMethod(
- '%s:%d' %
- (privateIP, tcpPort)))
-
- if not self.service.udpEnabled:
- log.msg("udp not enabled -- but I so want to send udp traffic!")
- elif udp_source is None:
- log.msg("udp_source was none on inbound")
- else:
- if self.service.dispatcher is None:
- log.msg("udp_source %s:%d, but dispatcher not running" %
- udp_source)
- else:
- remoteUDPHost, remoteUDPPort = udp_source
- log.msg(
- "remote PTCP: %s:%d, "
- "local public IP: %s, local private IP: %s"
- % (remoteUDPHost, remoteUDPPort, publicIP, privateIP) )
-
- # Seed my NAT from my shared UDP port
- udpPort = self.service.dispatcher.seedNAT(udp_source, self.service.sharedUDPPortnum)
-
- if remoteUDPHost == publicIP and publicIP != privateIP:
- log.msg(
- "Remote IP matches local, public IP %r;"
- " preferring internal IP %r" % (publicIP, privateIP))
- localMethods.append(
- PTCPMethod("%s:%d" % (privateIP, udpPort)))
- localMethods.append(
- PTCPMethod("%s:%d" % (publicIP, udpPort)))
-
- # XXX CLEANUP!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- privateUDPPort = self.service.dispatcher.seedNAT(udp_source)
- localMethods.append(
- PTCPMethod('%s:%d' % (publicIP, privateUDPPort)))
-
- udpxPort = self.service.dispatcher.seedNAT(udp_source)
- localMethods.append(
- RPTCPMethod("%s:%d" % (publicIP, udpxPort)))
-
- if self.service.virtualEnabled:
- localMethods.append(VirtualMethod())
- log.msg('responding to inbound with local methods: %r' % (localMethods,))
-
- for serverFactory, description in srvfacts:
- expiryTime, listenID = self.service.mapListener(
- to, From, protocol, serverFactory)
- result.append(dict(id=listenID,
- expires=expiryTime,
- methods=localMethods,
- description=description))
-
- # We've looked for our local factory. Let's see if we have any
- # listening protocols elsewhere.
-
-
- key = (to, protocol)
- if key in self.service.listeningClients:
- args = dict(From=From,
- to=to,
- protocol=protocol,
- udp_source=udp_source)
- DL = []
- lclients = self.service.listeningClients[key]
- log.msg("listeners found for %s:%r" % (to, protocol))
- for listener, listenCert, desc in lclients:
- log.msg("relaying inbound to %r via %r" % (to, listener))
- DL.append(listener.callRemote(Inbound, **args).addCallback(
- self._massageClientInboundResponse, listener, result))
-
- def allListenerResponses(x):
- log.msg("all inbound responses received: %s" % (pformat(result),))
- return dict(listeners=result)
- return defer.DeferredList(DL).addCallback(allListenerResponses)
- else:
- log.msg("no listenening clients for %s:%r. local methods: %r" % (to,protocol, result))
- return dict(listeners=result)
-
-
- def _massageClientInboundResponse(self, inboundResponse, listener, result):
- irl = inboundResponse['listeners']
- log.msg("received relayed inbound response: %r via %r" %
- (inboundResponse, listener))
-
- for listenerInfo in irl:
- # inboundResponse['description'] = ??? trust client version for
- # now... maybe the server doesn't even need to know about
- # descriptions...?
- listenerInfo['methods'] = [
- meth for meth in listenerInfo['methods'] if meth.relayable]
- # make sure that the certificate that we're relaying matches the
- # certificate that they gave us!
- if listenerInfo['methods']:
- allowedCertificate = Certificate.peerFromTransport(
- listener.transport)
- listenerInfo['certificate'] = allowedCertificate
- result.append(listenerInfo)
-
- def _determinePublicIP(self):
- reservePublicIP = None
- if self.service.publicIP is not None:
- if self.service._publicIPIsReallyPrivate:
- reservePublicIP = self.service.publicIP
- else:
- return self.service.publicIP
- if self.publicIP is not None:
- return self.publicIP
- if reservePublicIP is not None:
- return reservePublicIP
- return self._determinePrivateIP()
-
- def _determinePrivateIP(self):
- return self.transport.getHost().host
-
- def _sourceIP(self):
- result = {'ip': self.transport.getPeer().host}
- return result
- SourceIP.responder(_sourceIP)
-
- def _resume(self, connection, data, writeDeferred):
- try:
- connection.dataReceived(data)
- except:
- writeDeferred.errback()
- else:
- writeDeferred.callback({})
-
-
- def _choke(self, id):
- connection = self.connections[id]
- connection.choke()
- return {}
- Choke.responder(_choke)
-
-
- def _unchoke(self, id):
- connection = self.connections[id]
- connection.unchoke()
- return {}
- Unchoke.responder(_unchoke)
-
-
- def amp_WRITE(self, box):
- """
- Respond to a WRITE command, sending some data over a virtual channel
- created by VIRTUAL. The answer is simply an acknowledgement, as it is
- simply meant to note that the write went through without errors.
-
- An occurrence of I{Write} on the wire, together with the response
- generated by this method, might have this apperance::
-
- C: -Command: Write
- C: -Ask: 1
- C: -Length: 13
- C: Id: glyph@xxxxxxxxxx->radix@xxxxxxxxxxxxxxxxx:q2q-example:0
- C:
- C: HELLO WORLD
- C:
- S: -Answer: 1
- S:
-
- """
- id = int(box['id'])
- if id not in self.connections:
- raise error.ConnectionDone()
- connection = self.connections[id]
- data = box['body']
- connection.dataReceived(data)
- return AmpBox()
-
- def amp_CLOSE(self, box):
- """
- Respond to a CLOSE command, dumping some data onto the stream. As with
- WRITE, this returns an empty acknowledgement.
-
- An occurrence of I{Close} on the wire, together with the response
- generated by this method, might have this apperance::
-
- C: -Command: Close
- C: -Ask: 1
- C: Id: glyph@xxxxxxxxxx->radix@xxxxxxxxxxxxxxxxx:q2q-example:0
- C:
- S: -Answer: 1
- S:
-
- """
- # The connection is removed from the mapping by connectionLost.
- connection = self.connections[int(box['id'])]
- connection.connectionLost(Failure(CONNECTION_DONE))
- return AmpBox()
-
-
- def _sign(self, certificate_request, password):
- """
- Respond to a request to sign a CSR for a user or agent located within
- our domain.
- """
- if self.service.portal is None:
- raise BadCertificateRequest("This agent cannot sign certificates.")
-
- subj = certificate_request.getSubject()
-
- sk = subj.keys()
- if 'commonName' not in sk:
- raise BadCertificateRequest(
- "Certificate requested with bad subject: %s" % (sk,))
-
- uandd = subj.commonName.split("@")
- if len(uandd) != 2:
- raise BadCertificateRequest("Won't sign certificates for other domains")
- domain = uandd[1]
-
- CS = self.service.certificateStorage
- ourCert = CS.getPrivateCertificate(domain)
-
- D = self.service.portal.login(
- UsernamePassword(subj.commonName,
- password),
- self,
- ivertex.IQ2QUser)
-
- def _(ial):
- (iface, aspect, logout) = ial
- ser = CS.genSerial(domain)
- return dict(certificate=aspect.signCertificateRequest(
- certificate_request, ourCert, ser))
-
- return D.addCallback(_)
- Sign.responder(_sign)
-
-
- def _secure(self, to, From, authorize):
- """
- Response to a SECURE command, starting TLS when necessary, and using a
- certificate identified by the I{To} header.
-
- An occurrence of I{Secure} on the wire, together with the response
- generated by this method, might have the following appearance::
-
- C: -Command: Secure
- C: -Ask: 1
- C: To: divmod.com
- C: From: twistedmatrix.com
- C: Authorize: True
- C:
- Client Starts TLS here with twistedmatrix.com certificate
- S: -Answer: 1
- S:
- Server Starts TLS here with divmod.com certificate
-
- """
- if self.hostCertificate is not None:
- raise RuntimeError("Re-encrypting already encrypted connection")
- CS = self.service.certificateStorage
- ourCert = CS.getPrivateCertificate(str(to.domainAddress()))
- if authorize:
- D = CS.getSelfSignedCertificate(str(From.domainAddress()))
- else:
- self.authorized = False
- return {'tls_localCertificate': ourCert}
-
- def hadCert(peerSigned):
- self.authorized = True
- self._cacheMeNow(From, to, authorize)
- return {'tls_localCertificate': ourCert,
- 'tls_verifyAuthorities': [peerSigned]}
-
- def didNotHaveCert(err):
- err.trap(KeyError)
- return self._retrieveRemoteCertificate(From, port)
-
- D.addErrback(didNotHaveCert)
- D.addCallback(hadCert)
-
- return D
- Secure.responder(_secure)
-
- _cachedUnrequested = False
-
- def _cacheMeNow(self, From, to, authorize):
- tcpeer = self.transport.getPeer()
- # XXX 'port' is insane here, but we lack a better number to hash
- # against. perhaps the SECURE request should give a reciprocal
- # connection identifier...?
- self.service.secureConnectionCache.cacheUnrequested(
- endpoint.TCPEndpoint(tcpeer.host, port),
- (From, to.domain, authorize), self)
- assert not self._cachedUnrequested
- self._cachedUnrequested = (From, to, authorize, tcpeer)
-
- def _uncacheMe(self):
- if self._cachedUnrequested:
- # If this is a client connection, this will never be called, since
- # _cacheMeNow is called from the _server_ half of this business.
- # The uncaching API here is a bit of a ragged edge of conncache.py;
- # the interface should probably be cleaned up, but I don't think
- # there are any functional problems with it.
- From, to, authorize, tcpeer = self._cachedUnrequested
- self.service.secureConnectionCache.connectionLostForKey(
- (endpoint.TCPEndpoint(tcpeer.host, port),
- (From, to.domain, authorize)))
-
- def _retrieveRemoteCertificate(self, From, port=port):
- """
- The entire conversation, starting with TCP handshake and ending at
- disconnect, to retrieve a foreign domain's certificate for the first
- time.
- """
- CS = self.service.certificateStorage
- host = str(From.domainAddress())
- p = AMP()
- p.wrapper = self.wrapper
- f = protocol.ClientCreator(reactor, lambda: p)
- connD = f.connectTCP(host, port)
-
- def connected(proto):
- dhost = From.domainAddress()
- iddom = proto.callRemote(Identify, subject=dhost)
- def gotCert(identifyBox):
- theirCert = identifyBox['certificate']
- theirIssuer = theirCert.getIssuer().commonName
- theirName = theirCert.getSubject().commonName
- if (theirName != str(dhost)):
- raise VerifyError(
- "%r claimed it was %r in IDENTIFY response"
- % (theirName, dhost))
- if (theirIssuer != str(dhost)):
- raise VerifyError(
- "self-signed %r claimed it was issued by "
- "%r in IDENTIFY response" % (dhost, theirIssuer))
- def storedCert(ignored):
- return theirCert
- return CS.storeSelfSignedCertificate(
- str(dhost), theirCert).addCallback(storedCert)
- def nothingify(x):
- proto.transport.loseConnection()
- return x
- return iddom.addCallback(gotCert).addBoth(nothingify)
- connD.addCallback(connected)
- return connD
-
-
- def secure(self, fromAddress, toAddress,
- fromCertificate, foreignCertificateAuthority=None,
- authorize=True):
- """Return a Deferred which fires True when this connection has been secured as
- a channel between fromAddress (locally) and toAddress (remotely).
- Raises an error if this is not possible.
- """
- if self.hostCertificate is not None:
- raise RuntimeError("Re-securing already secured connection.")
-
- def _cbSecure(response):
- if foreignCertificateAuthority is not None:
- self.authorized = True
- return True
- extra = {'tls_localCertificate': fromCertificate}
- if foreignCertificateAuthority is not None:
- extra['tls_verifyAuthorities'] = [foreignCertificateAuthority]
-
- return self.callRemote(
- Secure,
- From=fromAddress,
- to=toAddress,
- authorize=authorize, **extra).addCallback(_cbSecure)
-
- def _virtual(self, id):
- if self.isServer:
- assert id > 0
- else:
- assert id < 0
- # We are double-deferring here so that we only start writing data to
- # our client _after_ they have processed our ACK.
- tpt = VirtualTransport(self, id, self.service._bootstrapFactory, False)
-
-
- return dict(__transport__=tpt)
-
- Virtual.responder(_virtual)
-
-
- # Client/Support methods.
-
- def attemptConnectionMethods(self, methods, connectionID, From, to,
- protocolName, protocolFactory):
- attemptObjects = []
- for meth in methods:
- atts = meth.attempt(self, connectionID, From, to,
- protocolName, protocolFactory)
- attemptObjects.extend(atts)
-
- attemptDeferreds = [att.startAttempt() for att in attemptObjects]
-
- d = defer.DeferredList(attemptDeferreds,
- fireOnOneCallback=True,
- fireOnOneErrback=False)
- def dontLogThat(e):
- e.trap(error.ConnectionLost, error.ConnectionDone)
-
- for attDef in attemptDeferreds:
- attDef.addErrback(dontLogThat)
-
- def _unfortunate_defer_hack(results):
- #Do you see what you've made me become?
- if isinstance(results, tuple):
- stuff = [(False, None)] * len(attemptObjects)
- stuff[results[1]] = (True, results[0])
- return stuff
- return results
-
-
- def gotResults(results):
- theResult = None
- anyResult = False
- for index, (success, result) in enumerate(results):
- if success:
- # woohoo! home free.
- # XXX Cancel outstanding attempts, maybe. They'll fail anyway,
- # because the factory will return None from buildProtocol().
- theResult = result
- anyResult = True
- else:
- attemptObjects[index].cancel()
- if anyResult:
- # theResult will be a SeparateConnectionTransport
- return theResult.subProtocol
- else:
- reason = Failure(AttemptsFailed([fobj for (f, fobj) in results]))
- return reason
-
- d.addCallback(_unfortunate_defer_hack)
- d.addCallback(gotResults)
- return d
-
-
- def listen(self, fromAddress, protocols, serverDescription):
- return self.callRemote(
- Listen, From=fromAddress,
- protocols=protocols, description=serverDescription)
-
-
- def connect(self, From, to,
- protocolName, clientFactory,
- chooser):
- """
- Issue an INBOUND command, creating a virtual connection to the peer,
- given identifying information about the endpoint to connect to, and a
- protocol factory.
-
- @param clientFactory: a *Client* ProtocolFactory instance which will
- generate a protocol upon connect.
-
- @return: a Deferred which fires with the protocol instance that was
- connected, or fails with AttemptsFailed if the connection was not
- possible.
- """
-
- publicIP = self._determinePublicIP()
-
- A = dict(From=From,
- to=to,
- protocol=protocolName)
-
- if self.service.dispatcher is not None:
- # tell them exactly where they can shove it
- A['udp_source'] = (publicIP,
- self.service.sharedUDPPortnum)
- else:
- # don't tell them because we don't know
- log.msg("dispatcher unavailable when connecting")
-
- D = self.callRemote(Inbound, **A)
-
- def _connected(answer):
- listenersD = defer.maybeDeferred(chooser, answer['listeners'])
- def gotListeners(listeners):
- allConnectionAttempts = []
- for listener in listeners:
- d = self.attemptConnectionMethods(
- listener['methods'],
- listener['id'],
- From, to,
- protocolName, clientFactory,
- )
- allConnectionAttempts.append(d)
- return defer.DeferredList(allConnectionAttempts)
- listenersD.addCallback(gotListeners)
- def finishedAllAttempts(results):
- succeededAny = False
- failures = []
- if not results:
- return Failure(NoAttemptsMade(
- "there was no available path for connections "
- "(%r->%r/%s)" % (From, to, protocolName)))
- for succeeded, result in results:
- if succeeded:
- succeededAny = True
- randomConnection = result
- break
- else:
- failures.append(result)
- if not succeededAny:
- return Failure(AttemptsFailed(
- [failure.getBriefTraceback() for failure in failures]))
-
- # XXX TODO: this connection is really random; connectQ2Q should
- # not return one of the connections it's made, put it into your
- # protocol's connectionMade handler
-
- return randomConnection
-
- return listenersD.addCallback(finishedAllAttempts)
- return D.addCallback(_connected)
-
-
-class SeparateConnectionTransport(object):
- def __init__(self,
- service,
- subProtocol,
- q2qhost,
- q2qpeer,
- protocolName,
- connectionEstablishedDeferred=None):
- self.service = service
- self.subProtocol = subProtocol
- self.q2qhost = q2qhost
- self.q2qpeer = q2qpeer
- self.protocolName = protocolName
- self.connectionEstablishedDeferred = connectionEstablishedDeferred
-
- subProtocol = None
- q2qhost = None
- q2qpeer = None
- protocolName = 'unknown'
-
- # ITransport
- disconnecting = property(lambda self: self.transport.disconnecting)
-
- # IQ2QTransport
-
- def getQ2QHost(self):
- return self.q2qhost
-
- def getQ2QPeer(self):
- return self.q2qpeer
-
- def makeConnection(self, tpt):
- self.transport = tpt
- self.service.subConnections.append(self)
- self.subProtocol.makeConnection(self)
- if self.connectionEstablishedDeferred is not None:
- self.connectionEstablishedDeferred.callback(self)
-
- def getPeer(self):
- return Q2QTransportAddress(self.getQ2QPeer(),
- self.transport.getPeer(),
- self.protocolName)
-
- def getHost(self):
- return Q2QTransportAddress(self.getQ2QHost(),
- self.transport.getHost(),
- self.protocolName)
-
- def dataReceived(self, data):
- self.subProtocol.dataReceived(data)
-
- def write(self, data):
- self.transport.write(data)
-
- def writeSequence(self, data):
- self.transport.writeSequence(data)
-
- def registerProducer(self, producer, streaming):
- self.transport.registerProducer(producer, streaming)
-
- def unregisterProducer(self):
- self.transport.unregisterProducer()
-
- def loseConnection(self):
- self.transport.loseConnection()
-
- def connectionLost(self, reason):
- self.service.subConnections.remove(self)
- if self.subProtocol is not None:
- self.subProtocol.connectionLost(reason)
- self.subProtocol = None
-
-class WhoAmI(Command):
- commandName = 'Who-Am-I'
-
- response = [
- ('address', HostPort()),
- ]
-
-class RetrieveConnection(ProtocolSwitchCommand):
- commandName = 'Retrieve-Connection'
-
- arguments = [
- ('identifier', String()),
- ]
-
- fatalErrors = {KeyError: "NoSuchConnection"}
-
-class Q2QBootstrap(AMP):
- def __init__(self, connIdentifier=None, protoFactory=None):
- AMP.__init__(self)
- assert connIdentifier is None or isinstance(connIdentifier, (str))
- self.connIdentifier = connIdentifier
- self.protoFactory = protoFactory
-
- def connectionMade(self):
- if self.connIdentifier is not None:
- def swallowKnown(err):
- err.trap(error.ConnectionDone, KeyError)
- self.retrieveConnection(self.connIdentifier, self.protoFactory).addErrback(swallowKnown)
-
- def whoami(self):
- """Return a Deferred which fires with a 2-tuple of (dotted quad ip, port
- number).
- """
- def cbWhoAmI(result):
- return result['address']
- return self.callRemote(WhoAmI).addCallback(cbWhoAmI)
-
-
- def _whoami(self):
- peer = self.transport.getPeer()
- return {
- 'address': (peer.host, peer.port),
- }
- WhoAmI.responder(_whoami)
-
-
- def retrieveConnection(self, identifier, factory):
- return self.callRemote(RetrieveConnection, factory, identifier=identifier)
-
-
- def _retrieveConnection(self, identifier):
- listenerInfo = self.service.lookupListener(identifier)
- if listenerInfo is None:
- raise KeyError(identifier)
- else:
- proto = listenerInfo.protocolFactory.buildProtocol(listenerInfo.From)
- return SeparateConnectionTransport(
- self.service,
- proto,
- listenerInfo.to,
- listenerInfo.From,
- listenerInfo.protocolName)
-
- RetrieveConnection.responder(_retrieveConnection)
-
-
-
-class Q2QBootstrapFactory(protocol.Factory):
- protocol = Q2QBootstrap
-
- def __init__(self, service):
- self.service = service
-
- def buildProtocol(self, addr):
- q2etc = protocol.Factory.buildProtocol(self, addr)
- q2etc.service = self.service
- return q2etc
-
-class VirtualTransport(subproducer.SubProducer):
- implements(interfaces.IProducer, interfaces.ITransport, interfaces.IConsumer)
- disconnecting = False
-
- def __init__(self, q2q, connectionID, protocolFactory, isClient):
- """
- @param q2q: a Q2Q Protocol instance.
-
- @param connectionID: an integer identifier, unique to the q2q instance
- that I am wrapping (my underlying physical connection).
-
- @param protocolFactory: an IProtocolFactory implementor which returns a
- protocol instance for me to use. I'll use it to build the protocol,
- and if the 'client' flag is True, also use it to notify
- connectionLost/connectionFailed.
-
- @param isClient: a boolean describing whether my protocol is the
- initiating half of this connection or not.
- """
- subproducer.SubProducer.__init__(self, q2q)
- self.q2q = q2q
-
- self.id = connectionID
- self.isClient = isClient
- self.q2q.connections[self.id] = self
- self.protocolFactory = protocolFactory
-
- protocol = None
-
- def startProtocol(self):
- self.protocol = self.protocolFactory.buildProtocol(self.getPeer())
- self.protocol.makeConnection(self)
- return self.protocol
-
- def pauseProducing(self):
- self.q2q.callRemote(Choke, id=self.id)
-
- def resumeProducing(self):
- self.q2q.callRemote(Unchoke, id=self.id)
-
- def writeSequence(self, iovec):
- self.write(''.join(iovec))
-
- def loseConnection(self):
- if self.disconnecting:
- # print 'omg wtf loseConnection!???!'
- return
- self.disconnecting = True
- d = self.q2q.callRemoteString('close', id=str(self.id))
- def cbClosed(ignored):
- self.connectionLost(Failure(CONNECTION_DONE))
- def ebClosed(reason):
- if self.id in self.q2q.connections:
- self.connectionLost(reason)
- elif not reason.check(error.ConnectionDone):
- # Anything but a ConnectionDone (or similar things, perhaps)
- # is fishy. Like an IndexError, that'd be wacko. But a
- # ConnectionDone when self.id is already out of the Q2Q's
- # connections mapping means the connection was closed after
- # we thought it was supposed to be closed. No harm there.
- log.err(reason, "Close virtual #%d failed" % (self.id,))
- d.addCallbacks(cbClosed, ebClosed)
-
-
- def connectionLost(self, reason):
- del self.q2q.connections[self.id]
- if self.protocol is not None:
- self.protocol.connectionLost(reason)
- if self.isClient:
- self.protocolFactory.clientConnectionLost(None, reason)
-
-
- def dataReceived(self, data):
- try:
- self.protocol.dataReceived(data)
- except:
- # XXX: unconditionally logging errors from user code makes it hard
- # to write tests, and is not always the right thing to do. we
- # should revamp Twisted to have some kind of control over this
- # behavior, and add that control back in to this code path as well
- # (although logging exceptions from dataReceived is _by default_
- # certainly the right thing to do) --glyph+exarkun
- reason = Failure()
- log.err(reason)
- self.connectionLost(reason)
-
- def write(self, data):
- self.q2q.callRemoteString(
- 'write', False, body=data, id=str(self.id))
-
- def getHost(self):
- return VirtualTransportAddress(self.q2q.transport.getHost())
-
- def getPeer(self):
- return VirtualTransportAddress(self.q2q.transport.getPeer())
-
-
-_counter = 0
-def _nextJuiceLog():
- global _counter
- try:
- return str(_counter)
- finally:
- _counter = _counter + 1
-
-class DefaultQ2QAvatar:
- implements(ivertex.IQ2QUser)
-
- def __init__(self, username, domain):
- self.username = username
- self.domain = domain
-
- def signCertificateRequest(self, certificateRequest,
- domainCert, suggestedSerial):
- keyz = certificateRequest.getSubject().keys()
- if keyz != ['commonName']:
- raise BadCertificateRequest(
- "Don't know how to verify fields other than CN: " +
- repr(keyz))
- newCert = domainCert.signRequestObject(
- certificateRequest,
- suggestedSerial)
- log.msg('signing certificate for user %s@%s: %s' % (
- self.username, self.domain, newCert.digest()))
- return newCert
-
-
-
-class DefaultCertificateStore:
-
- implements(ICredentialsChecker, IRealm)
-
- credentialInterfaces = [IUsernamePassword]
-
- def requestAvatar(self, avatarId, mind, interface):
- assert interface is ivertex.IQ2QUser, (
- "default certificate store only supports one interface")
- return interface, DefaultQ2QAvatar(*avatarId.split("@")), lambda : None
-
- def requestAvatarId(self, credentials):
- username, domain = credentials.username.split("@")
- pw = self.users.get((domain, username))
- if pw is None:
- return defer.fail(UnauthorizedLogin())
- def _(passwordIsCorrect):
- if passwordIsCorrect:
- return username + '@' + domain
- else:
- raise UnauthorizedLogin()
- return defer.maybeDeferred(
- credentials.checkPassword, pw).addCallback(_)
-
- def __init__(self):
- self.remoteStore = {}
- self.localStore = {}
- self.users = {}
-
- def getSelfSignedCertificate(self, domainName):
- return defer.maybeDeferred(self.remoteStore.__getitem__, domainName)
-
- def addUser(self, domain, username, privateSecret):
- self.users[domain, username] = privateSecret
-
- def checkUser(self, domain, username, privateSecret):
- if self.users.get((domain, username)) != privateSecret:
- return defer.fail(KeyError())
- return defer.succeed(True)
-
- def storeSelfSignedCertificate(self, domainName, mainCert):
- """
-
- @return: a Deferred which will fire when the certificate has been
- stored successfully.
- """
- assert not isinstance(mainCert, str)
- return defer.maybeDeferred(self.remoteStore.__setitem__, domainName, mainCert)
-
- def getPrivateCertificate(self, domainName):
- """
-
- @return: a PrivateCertificate instance, e.g. a certificate including a
- private key, for 'domainName'.
- """
- return self.localStore[domainName]
-
-
- def genSerial(self, name):
- return abs(struct.unpack('!i', md5(name).digest()[:4])[0])
-
- def addPrivateCertificate(self, subjectName, existingCertificate=None):
- """
- Add a PrivateCertificate object to this store for this subjectName.
-
- If existingCertificate is None, add a new self-signed certificate.
- """
- if existingCertificate is None:
- assert '@' not in subjectName, "Don't self-sign user certs!"
- mainDN = DistinguishedName(commonName=subjectName)
- mainKey = KeyPair.generate()
- mainCertReq = mainKey.certificateRequest(mainDN)
- mainCertData = mainKey.signCertificateRequest(mainDN, mainCertReq,
- lambda dn: True,
- self.genSerial(subjectName))
- mainCert = mainKey.newCertificate(mainCertData)
- else:
- mainCert = existingCertificate
- self.localStore[subjectName] = mainCert
-
-import os
-
-class _pemmap(object):
- def __init__(self, pathname, certclass):
- self.pathname = pathname
- try:
- os.makedirs(pathname)
- except (OSError, IOError):
- pass
- self.certclass = certclass
-
- def file(self, name, mode):
- try:
- return file(os.path.join(self.pathname, name)+'.pem', mode)
- except IOError, ioe:
- raise KeyError(name, ioe)
-
- def __setitem__(self, key, cert):
- kn = cert.getSubject().commonName
- assert kn == key
- self.file(kn, 'wb').write(cert.dumpPEM())
-
- def __getitem__(self, cn):
- return self.certclass.loadPEM(self.file(cn, 'rb').read())
-
- def iteritems(self):
- files = os.listdir(self.pathname)
- for file in files:
- if file.endswith('.pem'):
- key = file[:-4]
- value = self[key]
- yield key, value
-
- def items(self):
- return list(self.iteritems())
-
- def iterkeys(self):
- for k, v in self.iteritems():
- yield k
-
- def keys(self):
- return list(self.iterkeys())
-
- def itervalues(self):
- for k, v in self.iteritems():
- yield v
-
- def values(self):
- return list(self.itervalues())
-
-
-
-class DirectoryCertificateStore(DefaultCertificateStore):
- def __init__(self, filepath):
- self.remoteStore = _pemmap(os.path.join(filepath, 'public'),
- Certificate)
- self.localStore = _pemmap(os.path.join(filepath, 'private'),
- PrivateCertificate)
-
-class MessageSender(AMP):
- """
- """
-
-theMessageFactory = protocol.ClientFactory()
-theMessageFactory.protocol = MessageSender
-
-class _MessageChannel(object):
- """Conceptual curry over source and destination addresses, as well as a namespace.
-
- Acts as a transport for delivering Q2Q commands between two particular endpoints.
- """
-
- def __init__(self, q2qsvc,
- fromAddress, toAddress,
- namespace):
- self.q2qsvc = q2qsvc
- self.fromAddress = fromAddress
- self.toAddress = toAddress
- self.namespace = namespace
-
- def __call__(self, command):
- return self.q2qsvc.sendMessage(
- self.fromAddress,
- self.toAddress,
- self.namespace, command)
-
-_ConnectionWaiter = namedtuple('_ConnectionWaiter',
- 'From to protocolName protocolFactory isClient')
-
-
-
-class Q2QClientFactory(protocol.ClientFactory):
-
- protocol = Q2Q
-
- def __init__(self, service):
- self.service = service
-
- def buildProtocol(self, addr):
- p = protocol.ClientFactory.buildProtocol(self, addr)
- p.isServer = False
- p.service = self.service
- p.factory = self
- p.wrapper = self.service.wrapper
- return p
-
-
-class YourAddress(Command):
- arguments = [
- ('address', HostPort()),
- ]
-
-
-
-class AddressDiscoveryProtocol(Q2QBootstrap):
- def __init__(self, addrDiscDef):
- Q2QBootstrap.__init__(self)
- self.addrDiscDef = addrDiscDef
-
-
- def connectionMade(self):
- self.whoami().chainDeferred(self.addrDiscDef)
-
-
-
-class _AddressDiscoveryFactory(protocol.ClientFactory):
- def __init__(self, addressDiscoveredDeferred):
- self.addressDiscoveredDeferred = addressDiscoveredDeferred
-
- def clientConnectionFailed(self, connector, reason):
- self.addressDiscoveredDeferred.errback(reason)
-
- def clientConnectionLost(self, connector, reason):
- """
- """
-
- def buildProtocol(self, addr):
- adp = AddressDiscoveryProtocol(self.addressDiscoveredDeferred)
- return adp
-
-
-def _noResults(*x):
- return []
-
-class PTCPConnectionDispatcher(object):
- def __init__(self, factory):
- self.factory = factory
- self._ports = {}
-
- def seedNAT(self, (host, port), sourcePort=0, conditional=True):
- if sourcePort not in self._ports:
- if sourcePort != 0:
- if conditional:
- return None
- else:
- raise AssertionError('tried to seed %r in %r %r %r' %
- (sourcePort, self, self._ports, self.factory.service))
- sourcePort = self.bindNewPort(sourcePort)
- else:
- assert sourcePort != 0
- p, proto = self._ports[sourcePort]
- p.write('NAT!', (host, port))
- return sourcePort
-
- def bindNewPort(self, portNum=0, iface=''):
- iPortNum = portNum
- proto = ptcp.PTCP(self.factory)
- p = reactor.listenUDP(portNum, proto, interface=iface)
- portNum = p.getHost().port
- log.msg("Binding PTCP/UDP %d=%d" % (iPortNum,portNum))
- self._ports[portNum] = (p, proto)
- return portNum
-
- def unbindPort(self, portNum):
- log.msg("Unbinding PTCP/UDP %d" % portNum)
- port, proto = self._ports.pop(portNum)
- proto.cleanupAndClose()
-
- def connectPTCP(self, host, port, factory, sourcePort):
- p, proto = self._ports[sourcePort]
- return proto.connect(factory, host, port)
-
- def iterconnections(self):
- for (p, proto) in self._ports.itervalues():
- for c in p.protocol._connections.itervalues():
- if c.protocol is not None:
- yield c.protocol
- else:
- # print 'NOT yielding', c, 'in', c.state
- pass
-
- def killAllConnections(self):
- dl = []
- for p, proto in self._ports.itervalues():
- for c in p.protocol._connections.itervalues():
- c._stopRetransmitting()
- dl.append(defer.maybeDeferred(p.stopListening))
- self._ports = {}
- return defer.DeferredList(dl)
-
-
-class Q2QService(service.MultiService, protocol.ServerFactory):
- # server factory stuff
- publicIP = None
- _publicIPIsReallyPrivate = False
-
- debugName = 'service'
-
- protocol = Q2Q
-
- def __repr__(self):
- return '<Q2QService %r@%x>' % (self.debugName, id(self))
-
- def buildProtocol(self, addr):
- p = protocol.ServerFactory.buildProtocol(self, addr)
- p.isServer = True
- p.service = self
- p.factory = self
- p.wrapper = self.wrapper
- return p
-
- def iterconnections(self):
- """
- Iterator of all connections associated with this service, whether cached or
- not. For testing purposes only.
- """
- return itertools.chain(
- self.appConnectionCache.cachedConnections.itervalues(),
- self.secureConnectionCache.cachedConnections.itervalues(),
- iter(self.subConnections),
- (self.dispatcher or ()) and self.dispatcher.iterconnections())
-
- def __init__(self,
- protocolFactoryFactory=None,
- certificateStorage=None, wrapper=None,
- q2qPortnum=port,
- inboundTCPPortnum=None,
- publicIP=None,
- udpEnabled=None,
- portal=None,
- verifyHook=None):
- """
-
- @param protocolFactoryFactory: A callable of three arguments
- (fromAddress, toAddress, protocolName) which returns a list of 2-tuples
- of (ProtocolFactory, description) appropriate for constructing
- protocols which can serve the resource specified by the toAddress.
-
- @param certificateStorage: an implementor of ICertificateStore, or None
- for the default implementation.
- """
-
- if udpEnabled is not None:
- self.udpEnabled = udpEnabled
-
- if protocolFactoryFactory is None:
- protocolFactoryFactory = _noResults
- self.protocolFactoryFactory = protocolFactoryFactory
- if certificateStorage is None:
- certificateStorage = DefaultCertificateStore()
- if portal is None:
- portal = Portal(certificateStorage, checkers=[certificateStorage])
- self.certificateStorage = certificateStorage
-
- # allow protocols to wrap message handlers in transactions.
- self.wrapper = wrapper
-
- # clients which have registered for network events: maps {(q2q_id,
- # protocol_name): clientQ2QProtocol}
- self.listeningClients = {}
-
- self.inboundConnections = {} # map of str(Id) to _ConnectionWaiter
- self.q2qPortnum = q2qPortnum # port number for q2q
-
- # port number for inbound almost-raw TCP
- self.inboundTCPPortnum = inboundTCPPortnum
-
- # list of independent TCP connections relaying Q2Q traffic.
- self.subConnections = []
-
- # map of {(fromAddress, protocolName): [(factory, description)]}
- self.localFactoriesMapping = {}
-
- # currently only used for password-lookup for SIGN, but should be
- # invoked for everything related to connection setup.
- self.portal = portal
-
- if publicIP is not None:
- self.publicIP = publicIP
-
- if verifyHook is not None:
- self.verifyHook = verifyHook
-
- self.appConnectionCache = ConnectionCache()
- self.secureConnectionCache = ConnectionCache()
-
- service.MultiService.__init__(self)
-
- inboundListener = None
-
- _publicUDPPort = None
-
- def verifyHook(self, From, to, protocol):
- return defer.succeed(1)
-
- def _retrievePublicUDPPortNumber(self, registrationServerAddress):
- # Create a PTCP port, bounce some traffic off the indicated server,
- # wait for it to tell us what our address is
- d = defer.Deferred()
- addressDiscoveryFactory = _AddressDiscoveryFactory(d)
-
- host, port = registrationServerAddress
- self.dispatcher.connectPTCP(host, port, addressDiscoveryFactory,
- self.sharedUDPPortnum)
- return d
-
-
- def listenQ2Q(self, fromAddress, protocolsToFactories, serverDescription):
- """
- Right now this is really only useful in the client implementation,
- since it is transient. protocolFactoryFactory is used for persistent
- listeners.
- """
- myDomain = fromAddress.domainAddress()
- D = self.getSecureConnection(fromAddress, myDomain)
- def _secured(proto):
- lfm = self.localFactoriesMapping
- def startup(listenResult):
- for protocol, factory in protocolsToFactories.iteritems():
- key = (fromAddress, protocol)
- if key not in lfm:
- lfm[key] = []
- lfm[key].append((factory, serverDescription))
- factory.doStart()
-
- def shutdown():
- for protocol, factory in protocolsToFactories.iteritems():
- lfm[fromAddress, protocol].remove(
- (factory, serverDescription))
- factory.doStop()
-
- proto.notifyOnConnectionLost(shutdown)
- return listenResult
-
- if self.dispatcher is not None:
- gp = proto.transport.getPeer()
- udpAddress = (gp.host, gp.port)
- pubUDPDeferred = self._retrievePublicUDPPortNumber(udpAddress)
- else:
- pubUDPDeferred = defer.succeed(None)
-
- def _gotPubUDPPort(publicAddress):
- self._publicUDPAddress = publicAddress
- return proto.listen(fromAddress, protocolsToFactories.keys(),
- serverDescription).addCallback(startup)
- pubUDPDeferred.addCallback(_gotPubUDPPort)
- return pubUDPDeferred
-
- D.addCallback(_secured)
- return D
-
- def requestCertificateForAddress(self, fromAddress, sharedSecret):
- """
- Connect to the authoritative server for the domain part of the given
- address and obtain a certificate signed by the root certificate for
- that domain, then store that certificate in my local certificate
- storage.
-
- @param fromAddress: an address that this service is authorized to use,
- and should store a separate private certificate for.
-
- @param sharedSecret: a str that represents a secret shared between the
- user of this service and their account on the server running on the
- domain part of the fromAddress.
-
- @return: a Deferred which fires None when the certificate has been
- successfully retrieved, and errbacks if it cannot be retrieved.
- """
- kp = KeyPair.generate()
- subject = DistinguishedName(commonName=str(fromAddress))
- reqobj = kp.requestObject(subject)
- # create worthless, self-signed certificate for the moment, it will be
- # replaced later.
-
- #attemptAddress = q2q.Q2QAddress(fromAddress.domain,
- # fromAddress.resource + '+attempt')
- # fakeSubj = DistinguishedName(commonName=str(attemptAddress))
- fakereq = kp.requestObject(subject)
- ssigned = kp.signRequestObject(subject, fakereq, 1)
- certpair = PrivateCertificate.fromCertificateAndKeyPair
- fakecert = certpair(ssigned, kp)
- apc = self.certificateStorage.addPrivateCertificate
-
- def _2(secured):
- D = secured.callRemote(
- Sign,
- certificate_request=reqobj,
- password=sharedSecret)
- def _1(dcert):
- cert = dcert['certificate']
- privcert = certpair(cert, kp)
- apc(str(fromAddress), privcert)
- return D.addCallback(_1)
- return self.getSecureConnection(
- fromAddress, fromAddress.domainAddress(), authorize=False,
- usePrivateCertificate=fakecert,
- ).addCallback(_2)
-
- def authorize(self, fromAddress, password):
- """To-be-deprecated synonym for requestCertificateForAddress
- """
- return self.requestCertificateForAddress(fromAddress, password)
-
- _lastConnID = 1
-
- def _nextConnectionID(self, From, to):
- lcid = self._lastConnID
- self._lastConnID += 1
- fmt = '%s->%s:%s' % (
- From, to, lcid)
- return fmt
-
- def mapListener(self, to, From, protocolName, protocolFactory, isClient=False):
- """
- Returns 2-tuple of (expiryTime, listenerID)
- """
- listenerID = self._nextConnectionID(From, to)
- call = reactor.callLater(120,
- self.unmapListener,
- listenerID)
- expires = datetime.datetime(*time.localtime(call.getTime())[:7])
- self.inboundConnections[listenerID] = (
- _ConnectionWaiter(From, to, protocolName, protocolFactory, isClient),
- call)
- return expires, listenerID
-
- def unmapListener(self, listenID):
- del self.inboundConnections[listenID]
-
- def lookupListener(self, listenID):
- """(internal)
-
- Retrieve a waiting connection by its connection identifier, passing in
- the transport to be used to connect the waiting protocol factory to.
- """
- if listenID in self.inboundConnections:
- # make the connection?
- cwait, call = self.inboundConnections.pop(listenID)
- # _ConnectionWaiter instance
- call.cancel()
- return cwait
- # raise KeyError(listenID)
-
- def getLocalFactories(self, From, to, protocolName):
- """
- Returns a list of 2-tuples of (protocolFactory, description) to handle
- this from/to/protocolName
- """
- result = []
- x = self.localFactoriesMapping.get((to, protocolName), ())
- result.extend(x)
- y = self.protocolFactoryFactory(From, to, protocolName)
- result.extend(y)
- return result
-
-
- q2qPort = None
- inboundTCPPort = None
- inboundUDPPort = None
- dispatcher = None
- sharedUDPPortnum = None
-
- udpEnabled = True # pretty much you never want to turn this off
- # except in the unit tests, or in some kind of
- # pathological network condition
-
- virtualEnabled = True
-
- def startService(self):
- self._bootstrapFactory = Q2QBootstrapFactory(self)
- if self.udpEnabled:
- self.dispatcher = PTCPConnectionDispatcher(self._bootstrapFactory)
-
- if self.q2qPortnum is not None:
- self.q2qPort = reactor.listenTCP(self.q2qPortnum, self)
- self.q2qPortnum = self.q2qPort.getHost().port
- if self.dispatcher is not None:
- self.sharedUDPPortnum = self.dispatcher.bindNewPort(self.q2qPortnum, iface=self.publicIP or '')
-
- if self.inboundTCPPortnum is not None:
- self.inboundTCPPort = reactor.listenTCP(
- self.inboundTCPPortnum,
- self._bootstrapFactory)
-
- if self.sharedUDPPortnum is None and self.dispatcher is not None:
- self.sharedUDPPortnum = self.dispatcher.bindNewPort()
-
- return service.MultiService.startService(self)
-
- def stopService(self):
- dl = []
- for cwait, delayed in self.inboundConnections.itervalues():
- delayed.cancel()
- self.inboundConnections.clear()
- if self.q2qPort is not None:
- dl.append(defer.maybeDeferred(self.q2qPort.stopListening))
- if self.inboundTCPPort is not None:
- dl.append(defer.maybeDeferred(self.inboundTCPPort.stopListening))
- if self.dispatcher is not None:
- dl.append(self.dispatcher.killAllConnections())
- dl.append(self.appConnectionCache.shutdown())
- dl.append(self.secureConnectionCache.shutdown())
- dl.append(defer.maybeDeferred(service.MultiService.stopService, self))
- for conn in self.subConnections:
- dl.append(defer.maybeDeferred(conn.transport.loseConnection))
- return defer.DeferredList(dl)
-
-
- def sendMessage(self, fromAddress, toAddress, namespace, message):
- """
- Send a message using the Q2Q-Message protocol to a peer. This internally
- uses a connection cache to avoid setting up and tearing down
- connections too often.
-
- @param fromAddress: Q2QAddress instance referring to the sender of the
- message.
-
- @param toAddress: Q2QAddress instance referring to the receiver of the
- message.
-
- @param namespace: str which indicates what juice command namespace the message is in.
-
- @param message: a juice.Command object.
- """
-
-
- return self.connectCachedQ2Q(
- fromAddress, toAddress, MESSAGE_PROTOCOL, theMessageFactory
- ).addCallback(message.do, namespace)
-
-
- def messageChannel(self, fromAddress, toAddress, namespace):
- """Create a one-arg callable that takes a Command and sends it to .
- """
- return _MessageChannel(self, fromAddress, toAddress, namespace)
-
- def connectCachedQ2Q(self, fromAddress,
- toAddress, protocolName, protocolFactory):
- return self.appConnectionCache.connectCached(
- endpoint.Q2QEndpoint(self, fromAddress, toAddress, MESSAGE_PROTOCOL),
- theMessageFactory)
-
-
- def connectQ2Q(self, fromAddress, toAddress, protocolName, protocolFactory,
- usePrivateCertificate=None, fakeFromDomain=None,
- chooser=None):
- """ Connect a named protocol factory from a resource@domain to a
- resource@domain.
-
- This is analagous to something like connectTCP, in that it creates a
- connection-oriented transport for each connection, except instead of
- specifying your credentials with an application-level (username,
- password) and your endpoint with a framework-level (host, port), you
- specify both at once, in the form of your ID (user@my-domain), their ID
- (user@their-domain) and the desired protocol. This provides several
- useful features:
-
- - All connections are automatically authenticated via SSL
- certificates, although not authorized for any particular
- activities, based on their transport interface rather than having
- to have protocol logic to authenticate.
-
- - User-meaningful protocol nicknames are attached to
- implementations of protocol logic, rather than arbitrary
- numbering.
-
- - Endpoints can specify a variety of transport mechanisms
- transparently to the application: for example, you might be
- connecting to an authorized user-agent on the user's server or to
- the user directly using a NAT-circumvention handshake. All the
- application has to know is that it wants to establish a TCP-like
- connection.
-
- XXX Really, really should return an IConnector implementor for symmetry
- with other connection-oriented transport APIs, but currently does not.
-
- The 'resource' parameters are so named (rather than beginning with
- 'user', for example) because they are sometimes used to refer to
- abstract entities or roles, such as 'payments', or groups of users
- (communities) but generally the convention is to document them as
- individual users for simplicity's sake.
-
- The parameters are described as if Alice <alice@xxxxxxxxxx> were trying
- try connect to Bob <bob@xxxxxxxxxxxxx> to transfer a file over HTTP.
-
- @param fromAddress: The address of the connecting user: in this case,
- Q2QAddress("divmod.com", "alice")
-
- @param toAddress: The address of the user connected to: in this case,
- Q2QAddress("notdivmod.com", "bob")
-
- @param protocolName: The name of the protocol, by convention observing
- similar names to http://www.iana.org/assignments/port-numbers when
- appropriate. In this case, 'http'.
-
- @param protocolFactory: An implementation of
- L{twisted.internet.interfaces.IProtocolFactory}
-
- @param usePrivateCertificate: Use a different private certificate for
- initiating the 'secure' call. Mostly for testing different invalid
- certificate attacks.
-
- @param fakeDomainName: This domain name will be used for an argument to
- the 'connect' command, but NOT as an argument to the SECURE command.
- This is to test a particular kind of invalid cert attack.
-
- @param chooser: a function taking a list of connection-describing
- objects and returning another list. Those items in the remaining list
- will be attempted as connections and buildProtocol called on the client
- factory. May return a Deferred.
-
- @default chooser: C{lambda x: x and [x[0]]}
- """
- if chooser is None:
- chooser = lambda x: x and [x[0]]
-
- def onSecureConnection(protocol):
- if fakeFromDomain:
- connectFromAddress = Q2QAddress(fakeFromDomain, toAddress.resource)
- else:
- connectFromAddress = fromAddress
-
- return protocol.connect(connectFromAddress, toAddress,
- protocolName, protocolFactory,
- chooser)
-
- def onSecureConnectionFailure(reason):
- protocolFactory.clientConnectionFailed(None, reason)
- return reason
-
- return self.getSecureConnection(
- fromAddress, toAddress,
- port, usePrivateCertificate).addCallback(
- onSecureConnection).addErrback(onSecureConnectionFailure)
-
- def getSecureConnection(self, fromAddress, toAddress, port=port,
- failIfNoCertificate=False,
- usePrivateCertificate=None,
- authorize=True):
- """
- Get a secure connection between two entities by connecting to the
- domain part of toAddress
-
- (This really shouldn't be _entirely_ public, because it's slightly
- misleading: you pass in fully qualified addresses but the connection
- chops off the resource half of the "to" address, giving you a
- connection to their host rather than their actual client, as this is a
- necessary step to look up where their client *is*.)
- """
-
- # secure connections using users as clients will have to be established
- # using the 'secure' method differently than this does: we are ONLY
- # capable of connecting to other domains (supernodes)
-
- toDomain = toAddress.domainAddress()
- resolveme = reactor.resolve(str(toDomain))
- def cb(toIPAddress, authorize=authorize):
- GPS = self.certificateStorage.getPrivateCertificate
- if usePrivateCertificate:
- ourCert = usePrivateCertificate
- cacheFrom = fromAddress
- log.msg('Using fakie private cert:', fromAddress, ourCert, cacheFrom)
- elif fromAddress.domain == '':
- assert fromAddress.resource == '', "No domain means anonymous, bozo: %r" % (fromAddress,)
- # we are actually anonymous, whoops!
- authorize = False
- # we need to create our own certificate
- ourCert = KeyPair.generate().selfSignedCert(218374, CN='@')
- # feel free to cache the anonymous certificate we just made, whatever
- cacheFrom = fromAddress
- log.msg("Using anonymous cert for anonymous user.")
- else:
- try:
- # Are we in fact a domain, operating on behalf of a user?
- x = fromAddress.domainAddress()
- ourCert = GPS(str(x))
- cacheFrom = x
- log.msg('domain on behalf of user:', fromAddress, ourCert, cacheFrom)
- except KeyError:
- # Nope, guess not. Are we actually that user?
- try:
- x = fromAddress
- ourCert = GPS(str(x))
- cacheFrom = x
- log.msg( 'actual user:', fromAddress, ourCert, cacheFrom)
- except KeyError:
- # Hmm. We're not that user either. Are we trying to
- # pretend to be a user from a *different* domain, to
- # ourselves? (We've got to be a domain to "make
- # believe", since this is effectively a clustering
- # feature...)
-
- try:
- x = toDomain
- ourCert = GPS(str(x))
- cacheFrom = x
- log.msg('fakie domain cert:', fromAddress, ourCert, cacheFrom)
- except KeyError:
- raise VerifyError(
- "We tried to secure a connection "
- "between %s and %s, "
- "but we don't have any certificates "
- "that could be used." % (fromAddress,
- toAddress))
-
- def connected(proto):
- certD = self.certificateStorage.getSelfSignedCertificate(
- str(toDomain))
- def nocert(failure):
- failure.trap(KeyError)
- identD = proto.callRemote(Identify, subject=toDomain).addCallback(
- lambda x: x['certificate'])
- def storeit(certificate):
- return self.certificateStorage.storeSelfSignedCertificate(
- str(toDomain), certificate
- ).addCallback(lambda x: certificate)
- return identD.addCallback(storeit)
- certD.addErrback(nocert)
- def gotcert(foreignCA):
- secdef = proto.secure(cacheFrom, toDomain,
- ourCert, foreignCA,
- authorize=authorize)
- return secdef
- certD.addCallback(gotcert)
- return certD
- return self.secureConnectionCache.connectCached(
- endpoint.TCPEndpoint(toIPAddress, port),
- Q2QClientFactory(self),
- extraWork=connected,
- extraHash=(cacheFrom, toDomain, authorize)
- )
- return resolveme.addCallback(cb)
-
=== removed file 'Vertex/vertex/q2qadmin.py'
--- Vertex/vertex/q2qadmin.py 2010-04-17 15:10:09 +0000
+++ Vertex/vertex/q2qadmin.py 1970-01-01 00:00:00 +0000
@@ -1,21 +0,0 @@
-# Copyright 2005 Divmod, Inc. See LICENSE file for details
-
-from twisted.protocols.amp import Command, String
-
-class NotAllowed(Exception):
- pass
-
-class AddUser(Command):
- """
- Add a user to a domain.
- """
- commandName = "add_user"
-
- arguments = [
- ("name", String()),
- ("password", String())
- ]
-
- response = []
-
- errors = {NotAllowed: "NotAllowed"}
=== removed file 'Vertex/vertex/q2qclient.py'
--- Vertex/vertex/q2qclient.py 2010-04-17 15:10:09 +0000
+++ Vertex/vertex/q2qclient.py 1970-01-01 00:00:00 +0000
@@ -1,453 +0,0 @@
-# Copyright 2005 Divmod, Inc. See LICENSE file for details
-
-import os
-import sys
-import struct
-import getpass
-
-from twisted.protocols.amp import AMP
-
-from vertex import q2q, sigma
-from twisted.python.usage import Options, UsageError
-
-from twisted.python import log
-from twisted.internet import reactor
-from twisted.internet import protocol
-from twisted.internet.task import LoopingCall
-from twisted.internet import error
-from vertex.q2qadmin import AddUser
-
-class Q2QAuthorize(Options):
- def parseArgs(self, who, password=None):
- self.who = who
- self.password = password
-
- def reportNoCertificate(self, error):
- print "No certificate retrieved:", error.getErrorMessage(), "(see ~/.q2q-client-log for details)"
- log.err(error)
- return None
-
- def postOptions(self):
- def go():
- self.parent.getService().authorize(
- q2q.Q2QAddress.fromString(self.who),
- self.password).addErrback(self.reportNoCertificate).addCallback(lambda x: reactor.stop())
-
- if self.password is None:
- self.password = getpass.getpass()
-
- reactor.callWhenRunning(go)
- self.parent.start()
-
-
-class BandwidthEstimator:
- bufsize = 20
- totalBytes = 0
- def __init__(self, message, length):
- self.length = length
- self.message = message
- self.estim = []
- self.bytes = 0
- self.call = LoopingCall(self.estimateBandwidth)
- self.call.start(1)
-
- def estimateBandwidth(self):
- bytes = self.bytes
- self.totalBytes += bytes
- self.estim.append(bytes)
- self.message("%0.2f k/s (%0.2d%%)"
- % ((sum(self.estim) / len(self.estim)) / 1024.,
- (float(self.totalBytes) / self.length) * 100))
- if len(self.estim) > self.bufsize:
- self.estim.pop(0)
- self.bytes = 0
-
- def stop(self):
- self.call.stop()
- self.estimateBandwidth()
- self.message("Finished receiving: %d bytes (%d%%)" % (
- self.totalBytes, (float(self.totalBytes) / self.length) * 100))
-
-class FileReceiver(protocol.Protocol):
- gotLength = False
- estimator = None
-
- def connectionMade(self):
- self.f = open(self.factory.program.filename, 'wb')
- self.factory.program.parent.info("Started receiving...")
-
- def dataReceived(self, data):
- if not self.gotLength:
- self.length ,= struct.unpack("!Q", data[:8])
- data = data[8:]
- self.estimator = BandwidthEstimator(self.factory.program.parent.info,
- self.length)
- self.gotLength = True
-
- self.estimator.bytes += len(data)
- self.f.write(data)
-
- def connectionLost(self, reason):
- self.f.close()
- if self.estimator:
- self.estimator.stop()
- reactor.stop()
-
-from twisted.protocols.basic import FileSender as fsdr
-
-class FileSender(protocol.Protocol):
- def connectionMade(self):
- self.file = self.factory.openFile()
- self.file.seek(0, 2)
- self.length = self.file.tell()
- self.file.seek(0)
- self.estimator = BandwidthEstimator(self.factory.program.parent.info,
- self.length)
- self.transport.write(struct.pack("!Q", self.length))
- fsdr().beginFileTransfer(
- self.file, self).addCallback(
- lambda x: self.done())
-
- def done(self):
- self.factory.program.parent.info("Done sending data: %d bytes" % (
- self.file.tell(),))
- self.transport.loseConnection()
-
- def dataReceived(self, data):
- print "WTF THE CLIENT IS GETTING DATA", repr(data)
-
- def registerProducer(self, producer, streaming):
- self.transport.registerProducer(producer, streaming)
-
- def unregisterProducer(self):
- self.transport.unregisterProducer()
-
- def write(self, data):
- self.estimator.bytes += len(data)
- self.transport.write(data)
-
- def connectionLost(self, reason):
- reactor.stop()
-
-class FileSenderFactory(protocol.ClientFactory):
- protocol = FileSender
-
- def __init__(self, sendprogram):
- self.program = sendprogram
-
- def openFile(self):
- return file(self.program.filename, 'r')
-
- def clientConnectionFailed(self, connector, reason):
- self.program.parent.info(
- "Could not connect: %r" % (reason.getErrorMessage(),))
- reactor.stop()
-
- def clientConnectionLost(self, connector, reason):
- reason.trap(error.ConnectionDone)
-
-class FileReceiverFactory(protocol.Factory):
- def __init__(self, program):
- self.program = program
- protocol = FileReceiver
-
-
-class ClientCertificateStore(q2q.DirectoryCertificateStore):
- def __init__(self, filepath):
- q2q.DirectoryCertificateStore.__init__(self, os.path.expanduser(filepath))
-
-
-class ClientQ2QService(q2q.Q2QService):
- def __init__(self, certspath, *a, **kw):
- q2q.Q2QService.__init__(self,
- certificateStorage=ClientCertificateStore(certspath),
- q2qPortnum=0,
- *a, **kw)
-
- def getDefaultFrom(self, default=None):
- i = self.certificateStorage.localStore.iterkeys()
- try:
- return i.next()
- except StopIteration:
- return default
-
-
-class TunnelProtocol(protocol.Protocol):
- def __init__(self, tunnel):
- self.tunnel = tunnel
- self.buffer = []
-
- def connectionMade(self):
- if self.tunnel is not None:
- self.tunnel.setTunnel(self)
-
- def dataReceived(self, data):
- if self.tunnel is not None:
- self.tunnel.transport.write(data)
- else:
- self.buffer.append(data)
-
- def setTunnel(self, tunnel):
- if self.tunnel is None:
- self.tunnel = tunnel
- self.dataReceived(''.join(self.buffer))
- del self.buffer
- self.tunnel.setTunnel(self)
-
-class TunnelFactory(protocol.ClientFactory):
- def __init__(self, tunnel):
- self.tunnel = tunnel
-
- def buildProtocol(self, addr):
- return TunnelProtocol(self.tunnel)
-
- def clientConnectionFailed(self, connector, reason):
- self.tunnel.transport.loseConnection()
- reactor.stop()
-
- clientConnectionLost = clientConnectionFailed
-
-class Q2QTunnel(Options):
- optParameters = [
- ['port', 'p', '13000', 'Port on which to start the TCP server'],
- ['destination', 'd', None, 'Q2Q address to which to create the tunnel'],
- ['protocol', 'r', None, 'Q2Q protocol which will operate over the tunnel']]
-
- def postOptions(self):
- self.toAddr = q2q.Q2QAddress.fromString(self['destination'])
-
- reactor.listenTCP(int(self['port']), self, interface='127.0.0.1')
- self.parent.start()
-
- def doStart(self):
- pass
-
- def doStop(self):
- pass
-
- def buildProtocol(self, addr):
- p = TunnelProtocol(None)
- svc = self.parent.getService()
- svc.connectQ2Q(self.parent.getFrom(), self.toAddr,
- self['protocol'], TunnelFactory(p))
- return p
-
-class Q2QReceive(Options):
- optParameters = [["port", "p", "41235", "Port to start the listening server on."]]
-
- def parseArgs(self, filename):
- self.filename = filename
-
- def postOptions(self):
- serv = self.parent.getService()
- def pr(x):
- return x
- def stopit(err):
- print "Couldn't Register for File Transfer:", err.getErrorMessage()
- log.err(err)
- reactor.stop()
- serv.listenQ2Q(self.parent.getFrom(),
- {'file-transfer': FileReceiverFactory(self)},
- "simple file transfer test").addCallback(pr).addErrback(stopit)
- self.parent.start()
-
-class Q2QSend(Options):
-
- def parseArgs(self, to, filename):
- self.to = to
- self.filename = filename
-
- def postOptions(self):
- fs = q2q.Q2QAddress.fromString
- toAddress = fs(self.to)
- fromAddress = self.parent.getFrom()
-
- toDomain = toAddress.domainAddress()
- svc = self.parent.getService()
- svc.connectQ2Q(fromAddress, toAddress, 'file-transfer',
- FileSenderFactory(self))
- self.parent.start()
-
-
-class TextNexusUI(sigma.BaseNexusUI):
- def __init__(self):
- sigma.BaseNexusUI.__init__(self)
- self.call = LoopingCall(self.report)
- self.call.start(5)
-
- def report(self):
- print 'Transloads:', len(self.transloads)
- for transloadui in self.transloads:
- print '---', transloadui.name, '---'
- print transloadui.bits.percent()
- for peer, mask in transloadui.masks.items():
- print peer, mask.percent()
- print 'end report'
-
-class Q2QSigma(Options):
-
- def __init__(self, *a, **k):
- Options.__init__(self,*a,**k)
- self.pushers = []
-
- def opt_push(self, filename):
- self.pushers.append([file(filename), filename, []])
-
- def opt_to(self, q2qid):
- fs = q2q.Q2QAddress.fromString
- addr = fs(q2qid)
- self.pushers[-1][-1].append(addr)
-
- def postOptions(self):
- nex = sigma.Nexus(self.parent.getService(),
- self.parent.getFrom(),
- TextNexusUI())
- # XXX TODO: there has _GOT_ to be a smarter way to handle text UI for
- # this.
- for sharefile, sharename, sharepeers in self.pushers:
- nex.push(sharefile, sharename, sharepeers)
- self.parent.start()
-
-class UserAdder(AMP):
- def connectionMade(self):
- self.d = AddUser(name=self.factory.name,
- password=self.factory.password).do(self)
-
-
-class UserAdderFactory(protocol.ClientFactory):
- protocol = UserAdder
-
- def __init__(self, name, password):
- self.name, self.password = name, password
-
-
-def enregister(svc, newAddress, password):
- """
- Register a new account and return a Deferred that fires if it worked.
-
- @param svc: a Q2QService
-
- @param newAddress: a Q2QAddress object
-
- @param password: a shared secret (str)
- """
- def trapit(x):
- x.trap(error.ConnectionDone)
- return svc.connectQ2Q(q2q.Q2QAddress("",""),
- q2q.Q2QAddress(newAddress.domain, "accounts"),
- 'identity-admin',
- UserAdderFactory(newAddress.resource, password)
- ).addCallback(
- lambda proto: proto.d).addErrback(
- trapit)
-
-class Q2QRegister(Options):
- synopsis = "<new Q2Q address> <password>"
- def parseArgs(self, newaddress, password):
- self.newaddress = newaddress
- self.password = password
-
- def postOptions(self):
- fs = q2q.Q2QAddress.fromString
- newAddress = fs(self.newaddress)
- svc = self.parent.getService()
-
- def showit(x):
- print "%s: %s" % (x.value.__class__, x.getErrorMessage())
-
- enregister(svc, newAddress, self.password).addErrback(
- showit).addBoth(lambda nothing: reactor.stop())
- self.parent.start()
-
-
-class Q2QClientProgram(Options):
- subCommands = [
- ['authorize', 'a', Q2QAuthorize, 'Authorize a user'],
- ['register', 'r', Q2QRegister, 'Create a new user '],
- ['tunnel', 't', Q2QTunnel, 'Create an SSL tunnel to a given resource'],
- ['receive', 'l', Q2QReceive, 'Receive for a filetransfer connection'],
- ['send', 's', Q2QSend, 'Send'],
- ['sigma', 'g', Q2QSigma, 'Sigma swarming file-transfer']
- ]
-
- optParameters = [
- ['from', 'f', None, "Who to send as?"],
- ['tcp', 'p', None, 'TCP port number'],
- ['udp', 'u', 0, 'UDP port number'],
- ['certspath', 'c', "~/.q2qcerts",
- "Path to directory full of public/private certificates."],
- ['logfile', 'l', "~/.q2q-client-log",
- "Path to file where logs of client activity will be written."]
- ]
-
- optFlags = []
-
- service = None
-
- def postOptions(self):
- if not self.subCommand:
- self.opt_help()
-
- def info(self, message):
- sys.stderr.write(">> %s\n" % (message,))
-
- def getService(self):
- if self.service is None:
- u = self['udp']
- if u is not None:
- u = int(u)
- t = self['tcp']
- if t is not None:
- t = int(t)
- self.service = ClientQ2QService(self['certspath'],
- inboundTCPPortnum=t)
- return self.service
-
- def getDefaultPath(self):
- return os.path.expanduser(os.path.join(self['certspath'], 'default-address'))
-
- def getFrom(self):
- fr = self['from']
- if not fr:
- defpath = self.getDefaultPath()
- if os.path.exists(defpath):
- fr = file(defpath).read()
- else:
- fr = self.getService().getDefaultFrom()
- if fr is None:
- self.info("No default address available, exiting.")
- self.info(
- " (Try 'q2q register yourself@xxxxxxxxxx; "
- "q2q authorize yourself@xxxxxxxxxx')")
- sys.exit(19)
- self.info("Selected default address:" +fr)
- f = file(defpath, 'wb')
- f.write(fr)
- f.close()
-
- return q2q.Q2QAddress.fromString(fr)
-
- def start(self, portno=None):
- import sys
- lfname = self['logfile']
- if lfname == '-':
- lf = sys.stdout
- else:
- lf = file(os.path.expanduser(lfname), 'ab+')
- log.startLogging(lf,
- setStdout=False)
- srv = self.getService()
- from twisted.application.app import startApplication
- startApplication(srv, False)
- reactor.run()
-
- verbosity = 0
-
- def verboseLogger(self, messageDict):
- self.info(' '.join([str(x) for x in messageDict.get('message', [])]))
-
- def opt_verbose(self):
- self.verbosity += 1
- log.addObserver(log.FileLogObserver(sys.stderr).emit)
-
- opt_v = opt_verbose
=== removed file 'Vertex/vertex/q2qstandalone.py'
--- Vertex/vertex/q2qstandalone.py 2010-04-17 15:10:09 +0000
+++ Vertex/vertex/q2qstandalone.py 1970-01-01 00:00:00 +0000
@@ -1,108 +0,0 @@
-# Copyright 2005 Divmod, Inc. See LICENSE file for details
-
-import os
-
-from twisted.cred.portal import Portal
-
-from twisted.protocols.amp import AMP, Box, parseString
-
-from vertex import q2q
-from vertex.depserv import DependencyService, Conf
-from vertex.q2qadmin import AddUser, NotAllowed
-
-class IdentityAdmin(AMP):
-
- def command_ADD_USER(self, name, password):
- # all security is transport security
- theDomain = self.transport.getQ2QHost().domain
- self.factory.store.addUser(theDomain, name, password)
- return {}
-
- command_ADD_USER.command = AddUser
-
-class IdentityAdminFactory:
- def __init__(self, certstore):
- self.store = certstore
-
- def buildProtocol(self, addr):
- p = IdentityAdmin()
- p.factory = self
- return p
-
- def examineRequest(self, fromAddress, toAddress, protocolName):
- if toAddress.resource == "accounts" and protocolName == "identity-admin":
- return [(self, "identity admin")]
- return []
-
-class _usermap:
- def __init__(self, path):
- self.path = path
-
- def __setitem__(self, (domain, username), password):
- domainpath = os.path.join(self.path, domain)
- if not os.path.exists(domainpath):
- os.makedirs(domainpath)
- userpath = os.path.join(domainpath, username+".info")
- if os.path.exists(userpath):
- raise NotAllowed()
- f = open(userpath, 'w')
- f.write(Box(username=username,
- password=password.encode('hex')).serialize())
- f.close()
-
- def get(self, (domain, username)):
- domainpath = os.path.join(self.path, domain)
- if os.path.exists(domainpath):
- filepath = os.path.join(domainpath, username+".info")
- if os.path.exists(filepath):
- data = parseString(open(filepath).read())[0]
- return data['password'].decode('hex')
-
-class DirectoryCertificateAndUserStore(q2q.DirectoryCertificateStore):
- def __init__(self, filepath):
- q2q.DirectoryCertificateStore.__init__(self, filepath)
- self.users = _usermap(os.path.join(filepath, "users"))
-
- def getPrivateCertificate(self, domain):
- try:
- return q2q.DirectoryCertificateStore.getPrivateCertificate(self, domain)
- except KeyError:
- if len(self.localStore.keys()) > 10:
- # avoid DoS; nobody is going to need autocreated certs for more
- # than 10 domains
- raise
- self.addPrivateCertificate(domain)
- return q2q.DirectoryCertificateStore.getPrivateCertificate(self, domain)
-
-class StandaloneQ2Q(DependencyService):
- def setup_Q2Q(self, path,
- q2qPortnum=q2q.port,
- inboundTCPPortnum=q2q.port+1,
- publicIP=None
- ):
- """Set up a Q2Q service.
- """
- store = DirectoryCertificateAndUserStore(path)
- # store.addPrivateCertificate("kazekage")
- # store.addUser("kazekage", "username", "password1234")
- iaf = IdentityAdminFactory(store)
-
- self.attach(q2q.Q2QService(
- protocolFactoryFactory=IdentityAdminFactory(store).examineRequest,
- certificateStorage=store,
- portal=Portal(store, checkers=[store]),
- q2qPortnum=q2qPortnum,
- inboundTCPPortnum=inboundTCPPortnum,
- publicIP=publicIP,
- ))
-
-def defaultConfig():
- # Put this into a .tac file< and customize to your heart's content
- c = Conf()
- s = c.section
- s('q2q',
- path='q2q-data')
- application = deploy(**c)
- return application
-
-deploy = StandaloneQ2Q.deploy
=== removed directory 'Vertex/vertex/scripts'
=== removed file 'Vertex/vertex/scripts/__init__.py'
=== removed file 'Vertex/vertex/sigma.py'
--- Vertex/vertex/sigma.py 2011-06-01 02:15:15 +0000
+++ Vertex/vertex/sigma.py 1970-01-01 00:00:00 +0000
@@ -1,760 +0,0 @@
-# -*- test-case-name: vertex.test.test_sigma -*-
-# Copyright 2005 Divmod, Inc. See LICENSE file for details
-
-"""
-%file transfer protocol, a-la bittorrent.
-"""
-
-import array
-import random
-import sha
-import os
-import sets
-
-from twisted.internet import protocol
-
-from twisted.python.filepath import FilePath
-
-from twisted.protocols.amp import Integer, String, Command, AMP
-
-from vertex import q2q
-from vertex import bits
-from vertex import conncache
-from vertex import endpoint
-
-__metaclass__ = type
-
-# protocol below
-
-PROTOCOL_NAME = 'sigma'
-
-class VerifyError(Exception):
- pass
-
-class BitArrayArgument(String):
- def toString(self, arr):
- return str(arr.size) + ':' + arr.bytes.tostring()
-
- def fromString(self, st):
- size, bytes = st.split(":", 1)
- b = array.array("B")
- b.fromstring(bytes)
- return bits.BitArray(b, int(size))
-
-class Put(Command):
- """
- Tells the remote end it should request a file from me.
- """
-
- arguments = [("name", String())]
-
-
-class Get(Command):
- """
- Tells the remote it should start sending me chunks of a file.
- """
-
- arguments = [("name", String()),
- ('mask', BitArrayArgument(optional=True))]
-
- response = [("size", Integer())] # number of octets!!
-
-
-
-class Data(Command):
- """
- Sends some data for a transfer.
- """
- requiresAnswer = False
-
- arguments = [('name', String()),
- ('chunk', Integer()),
- ('body', String())]
-
-
-
-class Introduce(Command):
- """
- Tells the remote end about another node which should have information about
- this transfer.
-
- Peer: the address of the peer
- Name: the name of the file given.
- """
- requiresAnswer = False
- arguments = [('peer', q2q.Q2QAddressArgument()),
- ('name', String())]
-
-
-
-class Verify(Command):
- """
- Verify that the checksum of the given chunk is correct.
-
- Errors:
-
- - chunk checksum incorrect
- - host hasn't computed checksum for that chunk yet.
- """
-
- arguments = [('name', String()),
- ('peer', q2q.Q2QAddressArgument()),
- ('chunk', Integer()),
- ('sha1sum', String())]
-
-
-
-# this is a fixed, protocol-level constant
-CHUNK_SIZE = 1024 * 16
-
-DONE = {} # perhaps Juice should map this to None?
-
-def countChunks(bytes):
- div, mod = divmod(bytes, CHUNK_SIZE)
- div += bool(mod)
- return div
-
-class SigmaProtocol(AMP):
- """I am a connection to a peer who has some resources I want in the
- file-swarming network.
- """
-
- def __init__(self, nexus):
- AMP.__init__(self)
- self.nexus = nexus
- self.sentTransloads = []
-
-
- def _get(self, name, mask=None):
- peer = self.transport.getQ2QPeer()
- tl = self.nexus.transloads[name]
- size = tl.getSize()
- if mask is None:
- # all zeroes!
- mask = bits.BitArray(size=countChunks(size))
- # retrieve persistent scoring and such?
- tl.updatePeerMask(peer, mask)
- peerK = tl.peers[peer]
- if (not peerK.sentGet) and peerK.mask.any(0):
- # send a reciprocal GET
- self.get(name, tl.mask)
- return dict(size=size)
- Get.responder(_get)
-
-
- def _data(self, name, chunk, body):
- self.nexus.transloads[name].chunkReceived(
- self.transport.getQ2QPeer(), chunk, body)
- return DONE
- Data.responder(_data)
-
-
- def _put(self, name):
- peer = self.transport.getQ2QPeer()
- incompleteFilePath, fullFilePath = self.nexus.ui.allocateFile(
- name, peer)
- self.nexus.pull(incompleteFilePath, fullFilePath, name, peer)
- return DONE
- Put.responder(_put)
-
-
- def _verify(self, peer, name, chunk, sha1sum):
- if self.nexus.transloads[name].verifyLocalChunk(peer, chunk, sha1sum):
- return dict()
- raise RuntimeError("checksum incorrect")
- Verify.responder(_verify)
-
-
- def data(self, name, chunk, body):
- """
- Issue a DATA command
-
- return None
-
- Sends a chunk of data to a peer.
- """
- self.callRemote(Data, name=name, chunk=chunk, body=body)
-
-
- def introduce(self, name, peerToIntroduce):
- self.callRemote(
- Introduce, peer=peerToIntroduce, name=name)
-
-
- def _introduce(self, peer, name):
- # Like a PUT, really, but assuming the transload is already
- # established.
-
- self.nexus.ui.receivedIntroduction(peer, name)
-
- t = self.nexus.transloads[name]
- if peer in t.peers:
- return {}
-
- # all bits are set until he responds that he wants something.
-
- t.updatePeerMask(peer, bits.BitArray(default=1, size=len(t.mask)))
-
- self.nexus.connectPeer(peer).addCallback(
- lambda peerProto: peerProto.get(name, t.mask))
- return {}
- Introduce.responder(_introduce)
-
-
- def get(self, name, mask=None):
- """
- Issue a GET command
-
- Return a Deferred which fires with the size of the name being requested
- """
- mypeer = self.transport.getQ2QPeer()
- tl = self.nexus.transloads[name]
- peerz = tl.peers
- if mypeer in peerz:
- peerk = peerz[mypeer]
- else:
- # all turned on initially; we aren't going to send them anything.
- peerk = PeerKnowledge(bits.BitArray(size=len(tl.mask), default=1))
- peerz[mypeer] = peerk
- peerk.sentGet = True
- return self.callRemote(
- Get, name=name, mask=mask).addCallback(lambda r: r['size'])
-
-
- def verify(self, name, peer, chunkNumber, sha1sum):
- return self.callRemote(
- Verify, name=name, peer=peer, chunk=chunkNumber, sha1sum=sha1sum)
-
-
- def connectionMade(self):
- self.nexus.conns.cacheUnrequested(endpoint.Q2QEndpoint(
- self.nexus.svc,
- self.nexus.addr,
- self.transport.getQ2QPeer(),
- PROTOCOL_NAME), None, self)
- self.transport.registerProducer(self, 0)
-
- def stopProducing(self):
- ""
-
- pauses = 0
-
- def pauseProducing(self):
- self.pauses += 1
-
- def resumeProducing(self):
- """
- algorithm needed here: determine the proportion of my bandwidth that
- should be going to _ALL_ consumers based on the proportion of the sum
- of all scores that are available. then determine how long I need to
- wait before I send data to my peer.
- """
- self.nexus.callLater(0.0001, self.sendSomeData, 2)
-
- def sendSomeData(self, howMany):
- """
- Send some DATA commands to my peer(s) to relay some data.
-
- @param howMany: an int, the number of chunks to send out.
- """
- # print 'sending some data', howMany
- if self.transport is None:
- return
- peer = self.transport.getQ2QPeer()
- while howMany > 0:
- # sort transloads so that the least-frequently-serviced ones will
- # come first
- tloads = [
- (findin(tl.name, self.sentTransloads),
- tl) for tl in self.nexus.transloadsForPeer(peer)]
- tloads.sort()
- tloads = [tl for (idx, tl) in tloads if tl.peerNeedsData(peer)]
- if not tloads:
- break
-
- wasHowMany = howMany
-
- for myTransload in tloads:
- # move this transload to the end so it will be sorted last next
- # time.
- name = myTransload.name
- if name in self.sentTransloads:
- self.sentTransloads.remove(name)
- self.sentTransloads.append(name)
-
- knowledge = myTransload.peers[peer]
- chunkNumber, chunkData = myTransload.selectOptimalChunk(peer)
- if chunkNumber is None:
- continue
-
- peerToIntroduce = knowledge.selectPeerToIntroduce(
- myTransload.peers.keys())
-
- if peerToIntroduce is not None:
- self.introduce(myTransload.name, peerToIntroduce)
-
- self.data(name, chunkNumber, chunkData)
- # Don't re-send that chunk again unless they explicitly tell us
- # they need it for some reason
- knowledge.mask[chunkNumber] = 1
- howMany -= 1
- if howMany <= 0:
- break
-
- if wasHowMany == howMany:
- # couldn't find anything to send.
- break
-
-
-def findin(item, list):
- """
- Find C{item} in C{list}.
- """
- try:
- return list.index(item)
- except ValueError:
- # x not in list
- return -1
-
-class PeerKnowledge:
- """
- Local representation of a peer's knowledge of a transload.
- """
-
- sentGet = False
-
- def __init__(self, mask):
- self.mask = mask
- self.otherPeers = []
-
- def selectPeerToIntroduce(self, otherPeers):
- """
- Choose a peer to introduce. Return a q2q address or None, if there are
- no suitable peers to introduce at this time.
- """
- for peer in otherPeers:
- if peer not in self.otherPeers:
- self.otherPeers.append(peer)
- return peer
-
-
-class Transload:
- """
- An upload/download currently in progress
-
- @ivar maximumMaskUpdateDelayAfterChange: the maximum amount of time to wait
- after a change to the bitmask before sending out an updated mask to
- our peers.
-
- @ivar maximumChangeCountBeforeMaskUpdate: the maximum number of bits we
- will allow to change in our mask before sending an update to our
- peers.
-
- """
-
- maximumMaskUpdateDelayAfterChange = 30.0
- maximumChangeCountBeforeMaskUpdate = 25
-
- def __init__(self, authority, nexus, name,
- incompletePath, fullPath, ui,
- seed=False):
- """
- Create a Transload.
-
- @param authority: the q2q address of the first authority on this file.
- """
-
- self.incompletePath = incompletePath
- self.fullPath = fullPath
-
- self.ui = ui
- self.authorities = [authority] # q2q address(es) that you send VERIFYs to
-
- self.seed = seed
-
- if not seed:
- self.file = openReadWrite(incompletePath.path)
- else:
- self.file = fullPath.open()
-
- chunkCount = countChunks(self.getSize())
- mask = bits.BitArray(size=chunkCount, default=int(seed))
- if seed:
- maskfile = None
- else:
- maskfile = openMaskFile(incompletePath.path)
-
- self.mask = mask # BitArray object representing which chunks of
- # the file I've got
- self.maskfile = maskfile # ugh - open file object that keeps a record
- # of the bitmask
- self.sha1sums = {} # map {chunk-number: sha1sum}
- self.nexus = nexus # Nexus instance that I belong to
- self.name = name # the name of the file object being
- # transferred.
-
- self.changes = 0 # the number of mask changes since the last update
- self.peers = {} # map {q2q address: [PeerKnowledge]}
-
- # We want to retransmit GET every so often
- self.call = self.nexus.callLater(0.002, self.maybeUpdateMask)
-
- def stop(self):
- if self.call is not None:
- self.call.cancel()
- self.call = None
-
- def changeSize(self, size):
- assert len(self.mask) == 0
- self.file.seek(size-1)
- assert self.file.read(1) == ''
- self.file.write("\x00")
- chunkCount = countChunks(size)
- self.mask = bits.BitArray(size=chunkCount)
- self.writeMaskFile()
-
- def writeMaskFile(self):
- self.maskfile.seek(0)
- self.maskfile.write(buffer(self.mask.bytes))
- self.maskfile.flush()
-
- def updatePeerMask(self, peer, mask):
- if peer in self.peers:
- self.peers[peer].mask = mask
- else:
- self.peers[peer] = PeerKnowledge(mask)
- self.ui.updatePeerMask(peer, mask)
-
- def verifyLocalChunk(self, peer, chunkNumber, remoteSum):
- assert self.mask[chunkNumber] # XXX legit exception(?)
- localSum = self.sha1sums.get(chunkNumber)
- if localSum is None:
- self.file.seek(chunkNumber * CHUNK_SIZE)
- localChunk = self.file.read(CHUNK_SIZE)
- localSum = self.sha1sums[chunkNumber] = sha.new(localChunk).digest()
- return remoteSum == localSum
-
- def getSize(self):
- """
- return the size of my file in bytes
- """
- self.file.seek(0, 2)
- return self.file.tell()
-
- def chunkReceived(self, who, chunkNumber, chunkData):
- """
- A chunk was received from the peer.
- """
- def verifyError(error):
- error.trap(VerifyError)
- self.nexus.decreaseScore(who, self.authorities)
- return self.nexus.verifyChunk(self.name,
- who,
- chunkNumber,
- sha.new(chunkData).digest(),
- self.authorities).addCallbacks(
- lambda whatever: self.chunkVerified(who, chunkNumber, chunkData),
- verifyError)
-
- def chunkVerified(self, who, chunkNumber, chunkData):
- """A chunk (#chunkNumber) containing the data C{chunkData} was verified, sent
- to us by the Q2QAddress C{who}.
- """
- if self.mask[chunkNumber]:
- # already received that chunk.
- return
- self.file.seek(chunkNumber * CHUNK_SIZE)
- self.file.write(chunkData)
- self.file.flush()
- self.sha1sums[chunkNumber] = sha.new(chunkData).digest()
-
- if not self.mask[chunkNumber]:
- self.nexus.increaseScore(who)
- self.mask[chunkNumber] = 1
- self.writeMaskFile()
- self.changes += 1
-
- if self.changes > self.maximumChangeCountBeforeMaskUpdate:
- self.call.cancel()
- self.sendMaskUpdate()
- self.call = self.nexus.callLater(
- self.maximumChangeCountBeforeMaskUpdate,
- self.maybeUpdateMask)
-
- if not self.seed and not self.mask.countbits(0):
- # we're done, let's let other people get at that file.
- self.file.close()
- os.rename(self.incompletePath.path,
- self.fullPath.path)
- self.file = self.fullPath.open()
- self.maskfile.close()
- os.unlink(self.maskfile.name)
-
- self.ui.updateHostMask(self.mask)
-
-
- def maybeUpdateMask(self):
- if self.changes:
- self.sendMaskUpdate()
- self.call = self.nexus.callLater(
- self.maximumMaskUpdateDelayAfterChange,
- self.maybeUpdateMask)
-
-
- def selectOptimalChunk(self, peer):
- """
- select an optimal chunk to send to a peer.
-
- @return: int(chunkNumber), str(chunkData) if there is data to be sent,
- otherwise None, None
- """
-
- # stuff I have
- have = sets.Set(self.mask.positions(1))
- # stuff that this peer wants
- want = sets.Set(self.peers[peer].mask.positions(0))
- exchangeable = have.intersection(want)
- finalSet = dict.fromkeys(exchangeable, 0)
-
- # taking a page from bittorrent, rarest-first
- for chunkNumber in exchangeable:
- for otherPeer in self.peers.itervalues():
- finalSet[chunkNumber] += not otherPeer.mask[chunkNumber]
- rarityList = [(rarity, random.random(), chunkNumber)
- for (chunkNumber, rarity)
- in finalSet.iteritems()]
- if not rarityList:
- return None, None
- rarityList.sort()
- chunkNumber = rarityList[-1][-1] # sorted in ascending order of rarity
-
- # sanity check
- assert self.mask[chunkNumber], "I wanted to send a chunk I didn't have"
-
- self.file.seek(chunkNumber * CHUNK_SIZE)
- chunkData = self.file.read(CHUNK_SIZE)
- self.sha1sums[chunkNumber] = sha.new(chunkData).digest()
- return chunkNumber, chunkData
-
-
- def sendMaskUpdate(self):
- # xxx magic
- self.changes = 0
- for peer in self.peers:
- self.nexus.connectPeer(peer).addCallback(
- self._connectedPeer, peer)
-
- def _connectedPeer(self, proto, peer):
- knowledge = self.peers[peer]
- proto.get(self.name, self.mask)
-
- def peerNeedsData(self, peer):
- mask = self.peers[peer].mask
- return bool(list(mask.positions(0)))
-
- def putToPeers(self, peers):
- def eachPeer(proto):
- proto.callRemote(Put, name=self.name)
- return proto
-
- for peer in peers:
- self.nexus.connectPeer(peer).addCallback(eachPeer)
-
-
-
-
-def openReadWrite(filename):
- """
- Return a 2-tuple of: (whether the file existed before, open file object)
- """
- try:
- os.makedirs(os.path.dirname(filename))
- except OSError:
- pass
- try:
- return file(filename, 'rb+')
- except IOError:
- return file(filename, 'wb+')
-
-def existed(fileobj):
- """
- Returns a boolean indicating whether a file opened by openReadWrite existed
- in the filesystem before it was opened.
- """
- return 'r' in getattr(fileobj, "mode", '')
-
-def openMaskFile(filename):
- """
- Open the bitmask file sitting next to a file in the filesystem.
- """
- dirname, basename = os.path.split(filename)
- newbasename = '_%s_.sbm' % (basename,)
- maskfname = os.path.join(dirname, newbasename)
- maskfile = openReadWrite(maskfname)
- return maskfile
-
-
-class SigmaServerFactory(protocol.ServerFactory):
- def __init__(self, nexus):
- self.nexus = nexus
- def buildProtocol(self, addr):
- return SigmaProtocol(self.nexus)
-
-class SigmaClientFactory(protocol.ClientFactory):
- def __init__(self, nexus):
- self.nexus = nexus
- def buildProtocol(self, addr):
- return SigmaProtocol(self.nexus)
-
-class BaseTransloadUI:
-
- def __init__(self, nexusUI, name, sender):
- self.name = name
- self.sender = sender
- self.nexusUI = nexusUI
- self.masks = {}
- self.bits = bits.BitArray()
-
- def updatePeerMask(self, q2qid, bits):
- self.masks[q2qid] = bits
-
- def updateHostMask(self, bits):
- self.bits = bits
-
-class BaseNexusUI:
-
- transloadFactory = BaseTransloadUI
- receivedIntroductions = 0
-
- def __init__(self, basepath=os.path.expanduser("~/Sigma/Downloads")):
- self.basepath = FilePath(basepath)
- self.transloads = []
-
- def allocateFile(self, sharename, peer):
- """
- return a 2-tuple of incompletePath, fullPath
- """
- peerDir = self.basepath.child(str(peer))
- if not peerDir.isdir():
- peerDir.makedirs()
- return (peerDir.child(sharename+'.incomplete'),
- peerDir.child(sharename))
-
- def receivedIntroduction(self, peer, name):
- self.receivedIntroductions += 1
-
- def startTransload(self, *a, **kw):
- tl = self.transloadFactory(self, *a, **kw)
- self.transloads.append(tl)
- return tl
-
-class Nexus(object):
- """Orchestrator & factory
- """
-
- def __init__(self, svc, addr, ui, callLater=None):
- """
- Create a Sigma Nexus
-
- @param svc: a Q2QService
-
- @param addr: a Q2QAddress
-
- @param ui: an ISigmaNexusUI implementor.
-
- @param callLater: a callable with the signature and semantics of
- IReactorTime.callLater
- """
-
- # callLater is for testing purposes.
- self.scores = {} # map q2qaddress to score
- self.transloads = {} # map filename to active transloads
- self.svc = svc
- self.addr = addr
- self.conns = conncache.ConnectionCache()
- if callLater is None:
- from twisted.internet import reactor
- callLater = reactor.callLater
- self.callLater = callLater
- self.ui = ui
-
- self.serverFactory = SigmaServerFactory(self)
- self.clientFactory = SigmaClientFactory(self)
-
- svc.listenQ2Q(addr, {PROTOCOL_NAME: self.serverFactory},
- 'Nexus device description')
-
- def stopService(self):
- # XXX Not really a service, but maybe it should be? hmm.
- for transload in self.transloads.values():
- transload.stop()
-
- def transloadsForPeer(self, peer):
- """
- Returns an iterator of transloads that apply to a particular peer.
- """
- for tl in self.transloads.itervalues():
- if peer in tl.peers:
- yield tl
-
- def seed(self, path, name):
- """Create a transload from an existing file that is complete.
- """
- t = self.transloads[name] = Transload(self.addr, self, name,
- None, path,
- self.ui.startTransload(name,
- self.addr),
- seed=True)
- return t
-
- def connectPeer(self, peer):
- """Establish a SIGMA connection to the given peer.
-
- @param peer: a Q2QAddress of a peer which has a file that I want
-
- @return: a Deferred which fires a SigmaProtocol.
- """
- return self.conns.connectCached(endpoint.Q2QEndpoint(self.svc,
- self.addr,
- peer,
- PROTOCOL_NAME),
- self.clientFactory)
-
-
- def push(self, fpath, name, peers):
- t = self.seed(fpath, name)
- t.putToPeers(peers)
-
- def pull(self, incompletePath, finalPath, name, peer):
- t = self.transloads[name] = Transload(peer, self, name,
- incompletePath, finalPath,
- self.ui.startTransload(name, peer))
- D = self.connectPeer(peer).addCallback(lambda proto: proto.get(name))
- D.addCallback(t.changeSize)
- return D
-
- def increaseScore(self, participant):
- """
- The participant successfully transferred a chunk to me.
- """
- if participant not in self.scores:
- self.scores[participant] = 0
- self.scores[participant] += 1
-
-
- def decreaseScore(self, participant, authorities):
- """
- Much more severe than increaseScore, this implies that the named
- participant has a broken client or is cheating. Report them to
- authorities if they do this more than once.
- """
- self.scores[participant] -= 10
-
-
- def anyAuthority(self, authorities):
- return self.connectPeer(random.choice(authorities))
-
- def verifyChunk(self, name, who, chunkNumber, digest, authorities):
- return self.anyAuthority(authorities).addCallback(
- lambda authority: authority.verify(name, who, chunkNumber, digest))
-
=== removed file 'Vertex/vertex/statemachine.py'
--- Vertex/vertex/statemachine.py 2005-08-07 03:21:44 +0000
+++ Vertex/vertex/statemachine.py 1970-01-01 00:00:00 +0000
@@ -1,56 +0,0 @@
-# -*- test-case-name: vertex.test.test_statemachine -*-
-
-NOTHING = 'nothing' # be quiet (no output)
-
-class StateError(Exception):
- """
- """
-
-class StateMachine:
-
- initialState = None # a str describing initial state
-
- states = None # dict, mapping state to dict of str input: (str output,
- # str new-state)
-
-
- def __init__(self, initialState=None):
- if initialState is None:
- initialState = self.initialState
- self.state = self.initialState
-
- def transition(self, oldstate, newstate, datum, *a, **kw):
- if oldstate == newstate:
- return
- # print hex(id(self)), 'Going from', oldstate, 'to', newstate, 'because', datum
- exitmeth = getattr(self, 'exit_%s' % (oldstate,), None)
- entermeth = getattr(self, 'enter_%s' % (newstate,), None)
- transmeth = getattr(self, 'transition_%s_to_%s' % (
- oldstate, newstate), None)
- for meth in exitmeth, entermeth, transmeth:
- if meth is not None:
- meth(*a, **kw)
- self.state = newstate
-
- def input(self, datum, *a, **kw):
- oldstate = self.state
- if datum == NOTHING:
- return
- try:
- output, newstate = self.states[self.state][datum]
- except KeyError:
- self.invalidInput(datum)
- else:
- OLDSTATE = self.state.upper()
- NEWSTATE = newstate.upper()
- DATUM = datum.upper()
- self.transition(OLDSTATE, NEWSTATE, DATUM, *a, **kw)
- self.output(output, *a, **kw)
-
- def output(self, datum, *a, **kw):
- foo = getattr(self, 'output_' + datum.upper(), None)
- if foo is not None:
- foo(*a, **kw)
-
- def invalidInput(self, datum):
- raise StateError("Invalid input in %r: %r" % (self.state, datum))
=== removed file 'Vertex/vertex/subproducer.py'
--- Vertex/vertex/subproducer.py 2005-08-10 18:49:01 +0000
+++ Vertex/vertex/subproducer.py 1970-01-01 00:00:00 +0000
@@ -1,125 +0,0 @@
-# -*- test-case-name: vertex.test.test_subproducer -*-
-# Copyright 2005 Divmod, Inc. See LICENSE file for details
-
-from twisted.python import log
-
-class SuperProducer:
- """I am a mixin which provides support for mixing in several producers to one
- producer. I act as a consumer for my producers and as a producer for one
- consumer.
-
- I must be mixed into a protocol, or something else with a 'transport' attribute.
- """
-
- producersPaused = False
-
- def __init__(self):
- self.producingTransports = {}
-
- def pauseProducing(self):
- self.producersPaused = True
- for transport in self.producingTransports.keys():
- try:
- transport.parentPauseProducing()
- except:
- del self.producingTransports[transport]
- log.err()
-
- def resumeProducing(self):
- producersWerePaused = self.producersPaused
- if producersWerePaused:
- self.producersPaused = False
- for transport in self.producingTransports.keys():
- try:
- transport.parentResumeProducing()
- except:
- del self.producingTransports[transport]
- log.err()
-
- def stopProducing(self):
- for transport in self.producingTransports.keys():
- try:
- transport.parentStopProducing()
- except:
- log.err()
- self.producingTransports = {}
-
- def registerProducerFor(self, trans):
- if not self.producersPaused:
- trans.parentResumeProducing()
- wasProducing = bool(self.producingTransports)
- assert trans not in self.producingTransports
- self.producingTransports[trans] = 1
- if not wasProducing:
- self.transport.registerProducer(self, False)
-
- def unregisterProducerFor(self, trans):
- if trans in self.producingTransports:
- del self.producingTransports[trans]
- if not self.producingTransports:
- self.transport.unregisterProducer()
-
-
-class SubProducer:
- """ I am a mixin that provides upwards-registration of my producer to a
- SuperProducer instance.
- """
- def __init__(self, superproducer):
- self.superproducer = superproducer
- self.producer = None
- self.parentAcceptingData = True
- self.peerAcceptingData = True
- self.producerPaused = False
- self.parentStopped = False
-
- def maybeResumeProducing(self):
- if ((self.producer is not None) and
- ((not self.streamingProducer) or
- (self.producerPaused)) and
- (self.peerAcceptingData) and
- (self.parentAcceptingData)):
- self.producerPaused = False
- self.producer.resumeProducing()
-
- def maybePauseProducing(self):
- if ((self.producer is not None) and
- ((not self.peerAcceptingData) or
- (not self.parentAcceptingData)) and
- (not self.producerPaused)):
- self.producerPaused = True
- self.producer.pauseProducing()
-
- def parentResumeProducing(self):
- self.parentAcceptingData = True
- self.maybeResumeProducing()
-
- def parentPauseProducing(self):
- self.parentAcceptingData = False
- self.maybePauseProducing()
-
- def parentStopProducing(self):
- self.parentStopped = True
- if self.producer is not None:
- self.producer.stopProducing()
-
- def choke(self):
- self.peerAcceptingData = False
- self.maybePauseProducing()
-
- def unchoke(self):
- self.peerAcceptingData = True
- self.maybeResumeProducing()
-
- def registerProducer(self, producer, streaming):
- if self.parentStopped:
- producer.stopProducing()
- return
- self.producer = producer
- self.streamingProducer = streaming
- self.superproducer.registerProducerFor(self)
-
- def unregisterProducer(self):
- if not self.parentStopped:
- self.superproducer.unregisterProducerFor(self)
- self.producer = None
-
=== removed file 'Vertex/vertex/tcpdfa.py'
--- Vertex/vertex/tcpdfa.py 2005-08-18 16:10:42 +0000
+++ Vertex/vertex/tcpdfa.py 1970-01-01 00:00:00 +0000
@@ -1,96 +0,0 @@
-# -*- test-case-name: vertex.test.test_ptcp -*-
-# Copyright 2005 Divmod, Inc. See LICENSE file for details
-
-from vertex.statemachine import StateMachine, NOTHING
-
-# States
-
-CLOSED = 'CLOSED'
-LISTEN = 'LISTEN'
-SYN_RCVD = 'SYN_RCVD'
-SYN_SENT = 'SYN_SENT'
-ESTABLISHED = 'ESTABLISHED'
-CLOSE_WAIT = 'CLOSE_WAIT'
-LAST_ACK = 'LAST_ACK'
-CLOSING = 'CLOSING'
-FIN_WAIT_1 = 'FIN_WAIT_1'
-FIN_WAIT_2 = 'FIN_WAIT_2'
-TIME_WAIT = 'TIME_WAIT'
-
-# Network vocabulary
-SYN = 'SYN'
-ACK = 'ACK'
-SYN_ACK = 'SYN_ACK'
-FIN = 'FIN'
-RST = 'RST'
-
-# Application vocabulary
-APP_PASSIVE_OPEN = 'PASSIVE_OPEN'
-APP_ACTIVE_OPEN = 'ACTIVE_OPEN'
-APP_SEND_DATA = 'APP_SEND'
-TIMEOUT = 'TIMEOUT'
-APP_CLOSE = 'APP_CLOSE'
-
-# This isn't detailed by the spec in the diagram, so we use a different
-# identifier, but in various places it does make references to going straight
-# to the 'closed' state.
-BROKEN = 'CLOSED'
-
-
-class TCP(StateMachine):
- # dict, mapping state to dict of input: (output, new-state)
-
- states = {
- CLOSED: {
- APP_PASSIVE_OPEN: (NOTHING, LISTEN),
- APP_ACTIVE_OPEN: (SYN, SYN_SENT),
- },
- SYN_SENT: {
- TIMEOUT: (NOTHING, CLOSED),
- APP_CLOSE: (NOTHING, CLOSED),
- SYN_ACK: (ACK, ESTABLISHED),
- # SYN: (SYN_ACK, SYN_RCVD),
- },
- SYN_RCVD: {
- ACK: (NOTHING, ESTABLISHED),
- APP_CLOSE: (FIN, FIN_WAIT_1),
- TIMEOUT: (RST, CLOSED),
- RST: (NOTHING, LISTEN),
- },
- LISTEN: {
- APP_SEND_DATA: (SYN, SYN_SENT),
- SYN: (SYN_ACK, SYN_RCVD),
- },
- ESTABLISHED: {
- APP_CLOSE: (FIN, FIN_WAIT_1),
- FIN: (ACK, CLOSE_WAIT),
- TIMEOUT: (NOTHING, BROKEN),
- },
- CLOSE_WAIT: {
- APP_CLOSE: (FIN, LAST_ACK),
- TIMEOUT: (NOTHING, BROKEN),
- },
- LAST_ACK: {
- ACK: (NOTHING, CLOSED),
- TIMEOUT: (NOTHING, BROKEN),
- },
- FIN_WAIT_1: {
- ACK: (NOTHING, FIN_WAIT_2),
- FIN: (ACK, CLOSING),
- # FIN_ACK: (ACK, TIME_WAIT),
- TIMEOUT: (NOTHING, BROKEN),
- },
- FIN_WAIT_2: {
- TIMEOUT: (NOTHING, BROKEN),
- FIN: (ACK, TIME_WAIT),
- },
- CLOSING: {
- TIMEOUT: (NOTHING, BROKEN),
- ACK: (NOTHING, TIME_WAIT),
- },
- TIME_WAIT: {
- TIMEOUT: (NOTHING, CLOSED),
- },
- }
-
- initialState = CLOSED
=== removed directory 'Vertex/vertex/test'
=== removed file 'Vertex/vertex/test/__init__.py'
--- Vertex/vertex/test/__init__.py 2005-08-05 06:04:09 +0000
+++ Vertex/vertex/test/__init__.py 1970-01-01 00:00:00 +0000
@@ -1,3 +0,0 @@
-# -*- test-case-name: vertex.test -*-
-# Copyright 2005 Divmod, Inc. See LICENSE file for details
-
=== removed file 'Vertex/vertex/test/mock_data.py'
--- Vertex/vertex/test/mock_data.py 2005-08-05 06:02:56 +0000
+++ Vertex/vertex/test/mock_data.py 1970-01-01 00:00:00 +0000
@@ -1,2 +0,0 @@
-# Copyright 2005 Divmod, Inc. See LICENSE file for details
-data = '\xab\xcd\xaf\x14\xe14\x9d\xba\xd72\xc4\x16\xde\xd7\xcd}\xf8\x9a\x1b*?\\\xcf\xcdsaK\xe3\xcd\xf0\x011\xf3\x99\x90\x85M\x0fgk\x16\xfb\x18\xd5\xd1\xec\x85\x077\xbc@\xa6}z\xa3v\xd9A\xce\xfc\xe9\xf4\x0f^\xe0X\x9f\x13\xcb\xe0|7\xa0\x12\xc8&\x95{\xbe\xe8N\xee\\\x0b\xc7\xb1\x811\xfe\x1cT\x0f\xf0\xb2\x02\xc4\xacd\xe2A\tk\x9co#A\x9a\x0e\xbd\xa6\xc3\x9e{$\x98\xa0m\xc6\xc7vl4\xd6x\x04}\x8c*w\xf4\xc3\xb7!\x17\xb4\xdd\xf1\xbd\xfdk/_\xaeVm\xcc\xf4y)\xf2\xbf\x98]\x94$\x050y\xf0b\xa4\x0e\x89\x1e\xdb\x9bz\xcd^j\x91\xf8\x01\xc2\xcee\xb9\xf3\xb1HqT\xb5A\x8a\x89\x96o\xdfm\xb9\xc1nM\xeay\x01\xefn=\x9e\xfaS\xc7\x7f0Xi\x17"S.\xb2\x9fL\x93\xfe\xb4?\xf8j\tK\xbdp\xbdv\x15\xa8}\xe52\xbcO\x04\xff\xfa\xda\x8c\x83\xe2\xdd7\xbaF\xb0=\xd0M\x82\n\xeb\xaf\x06\xb8"\xe4Yxt\xd9H9\xa8\xe8\xc2T\x87\x08\xc3\xd6\xc7\xf9\xf5K&D\xb0\xae\x9c\xe4FCn\xaf\xc7\xe1h2\xd1\x9aJ#\x07\x05@9\xf78i:\x04z\x01e.\xdb\x01\xb0at\x1fe\x86\xfe\xed\x94\xedc\x11\xe2\xed(o\x0b\x82\xc7\x8a\x02\xaf\x90c{1\xedNB%Z\xe9\xea\xa2\xbf\xdc\x95\\kv\xd6\x17(\xa0\xa39\xc2\x8b\xb8\xd9\xd2B<\x94`\x07|\xce\x11\x0f\x151\xbf]^\xc1>\x08\x17\xde0\xdc=\x16\x84s\x90\x83\x8cT\x0b\xe5\rER\xb5\xe4$\xff\x91\x1dl\xd5\xdd\x82^H\x9a_\xde\xdc\xc4\x11\x88\xd6\x9cp\xba\x05\xd3`\xfb\x9e\xe5^\xba\xd5\x0c\xcd\xa1\t\x83\xdf\x85\xbcy\xeel\xfa\xa5\x00\x90{fG\rl$s\x98\xbc\xd7"\x95\xf7\x94K\xa7\xe6=\x1a\xeb1\x00\x97\xc8\x94\xf0\x11\x8b\x16wv#\x12\x0e\xca\xf4\xb36oc\x1a\xaf\xf2%\xaf\x9f\xc1 N\xb8D.\xf0\x85|\xc8Oj]\xc1\xe7(!\xb8\xb8\x9a\xbc\xd4\xc5\x0f+\xcf\x14\x86\x1c\xc5Jx\xf7\xefNSV\x01\xbb\xfb\x82\x9eZ\x809\x9a\xf0C\x05g\x99!g\xcdVQ0\'0\xa4$\x04W\xbbE\x92\xb8\x971\xc1\x1cd\xaf\xf8\x9e:\x89\xf4\xadR\xfb\x98\x0b\xf2\x82\x07\r\x93\xb7$\x85\x14&\xad#\xc2s\xbah-xm3@\xe8S\x8a\xeb\xfd5\xd4\xa0O\t%\n\xf35\x13\xe3#.j\xcf\xa4wH\xf8\xb0\x02\x89aN\xe3(\xcd\x15s\xc6\xa8\xac\x0b\xa0\xe5\x97%\xcc\xc7\xc8\x93N\x86\x02#,\xf2\xa9\xe3q\xff\x99\x1f+H\r\xf5o<\xe41OKc?\xae\x15\xdc\xbb\xc8\xdd\xa0\xc1\x8f\x14*\xcd\xa3\xba\xc5\xb2\r\xb3\x0fu\xc6\x83f{\x91\x0b\xa1\x9c\xbd"3\xf2l\xb2\x81\xe5l\x10k0`\'\xc1Su\rk\xe92U\xa2\x87J\xb1\x02\xf0\x90%XY\x02s\xc3:\xd5F9.\xd7\xd1IP\xd9;\xccP\x0c\xdeI\x14\xa11\x90\xd9y"\xf3L\xb7\xe8\xb0\xca\xe2Y\xe8\xe8b\xb9\xa4\x14o\xad\xe7\x16\xc4|x\xb0H\xdd\xa2J\x8b\xda*,\xee\xb1\xee\x00 [O\xaa\xc0\x00\x8999mj\xb8\x84\xe1\xd4\xf2Z$3#\xac\x8f\x93P\x0b\xd9\x8a\xd2P\x1bk\xa8\x1f-:\xb1q"\xeb\xf4G=dS\x94}L\xc5\x9e=r\x0fO+\xbc\xe5\xd8r2\xd0\xf1r\xdf\xf1\xdf\x88\xa3AC\x89\x99\xc5s6\x1b\xf7u\xc3\xf1J?R\xd5_\xad\xa9\xfe<%\x89]Z\x04\xe8\xac/Z\xfbm\x83)m\xeev=\xd6m\xd6Y\xf0\xaaJ\'\xce\x1d\xd0t$gs\x17E\xa1\x81\xb5\x15\x85\t\x06\x02%\xdd<\x05\xe4\xb5\xba\x83\xe6\x99x\xc9^]\x02\x012p)/\xcdQM\x14&`\xc7)\x8e\xb4~\xf1\x16P\x1e^\xbe\x85\x87\xa0\xd8\xa0NI\xb69!!F\x03\x89t\xdco1\xc5c\x01\xbc\xe9\x0f\x1c|\xc0\x15\xaed\xc6\x18c#\xf42\xc1R\x00 \xe1\xc5\xed\x12x\x1f\xc7jV\xb7\xd704-\x9b@\xa5s\xa51\x03>\x8b\x02\xda\x84f3r@\xa1\xe9t\xea\xd7d\xa7\x86PS\x81B\xe2\xafKw\x95\\\x7fv\r\x9aC\xeb\x99\xcf~\xd2\xc1g\x8e\xbeXmK|\x05\xd0*\x11\xa3up xt\xe0\r\xb8h\tm\x8f\xf8B<QlQ\x18\xc7\x85\x99\x98\x14\xec\\>#\x8c|\x86\x84\xcb\x0b\xa0\x9a\x1c\xb2\xadv#zfeL\xd9\xa5[\xc5s\x01w\xa9\x7f0\x7f\xa2\xef`e\x1c3\x9d\x17\x8d\x1e\xf4\x85\x9b\xc2H\\\xc8\xe2b1\xc0+\x99\xab\x10p-\xaf\x00\xf0ip\xc8\x06GaG\x1b@\xdc\r|\x7f\x1e\xa7\xa8Y\xc3\xa7Dr\xac<\x93\xe2\xd4\xdc;\x02\xff1\xa9\x9d7X\n6\r\x96\x94\xa3\xa9\xf8e\xbc\xcab\x17\xcd\xc4\xb3/ \x0f\x9c\xe0\xb4m\xf3\x9e\xaa\xc2\x02\xecD\xdc\xbah\xf0\x9a\x99V<\xe4\xef{\xa5\xceB\xf3q\xef\xb31\xd8\xd3\x13\xf9\xf1s\xd7\xc5\x80\x06\xa4{\xdc%\x163\x14\xee\xcdx\xee\xdc\x8d8\xae\xe3l\x97\\\xb4\xd5\x0b\x965\xaf\x1d\xd9\x7f\x97Fptb\xeb\x0e]-c\r)%\x1b\xddBr\xdf\x1b\x1a\x18\xb7|\xed2\xf7\x0c\x8d\xa5\x94\x16\xce>\xd5mI\xfd\xd7,\x17\x1a\x89\xa0Y\x88\xbcE\xa1H\x82\xeb\xf7\xf5\x99\xb3l~W[I\x81f\x90\x17\xc0\xbd\xb8\xac\xee\xb9uQ\xe2\xb4`\xf5\xfb:\x17\x86\xf5\xe7\x85\xad|s\xdb\x1e\x81\x03sq\xf1\xd1\xcb\x8fx\x95\xef\xefJ4v\xbe\xc9\x9b\xf5\x8cLP\xd0\x8e\xbb\xe2w\xef\xaf\xc0\xa08\x93\xb2\xd7\x1b-fP\xa2\xba\xde\xc5\xa5^\ng1c\xeeS\xb5\xff\xf8\x10\xde\x87\x0f\xe5\xd8\xec\xb4\xfa=+\xc6\'\x9a\x80n\xf6\xe4\xcd\xcb\x93\x04v\xd87\xa3\xe63\xc9X\xd2\xff\x7fK\xeaw\x80\x8b\x01#\xec.\x13w\xc0v\xb4\x97V\xf4\xa3\xe4\xc7m\n\xa8\xad\x94\xa2\xc8\xd5\xd4\xba}\x13\x17\x9f4\xe4k\'\xba[\xfa\x19\xfe},\xa3j\x00\xc4\x99\xaeT\x1a\xa5\xc6p\x00\xbc\xb1\x06\x08\xf6=\x07\xa7|\x057\xf2|\x86\xe24\xacs2\xdeD%\r\xf0c\xe5\xc0\xb5\xa8\x850*\x0chK|\x89\x1a \x17v\'\xe3\x96M\xfe\xb5d\x88\xfe(\x9d\xea\x01@\xd9\x16\x00\x87\xe8\x0c\xbdhX\xf9\x02\xe3\xdd\x06i\x98\xaaz\xed\xb5\xa8<\x1a\xd6\x18f\x00\x9du$\x03P!p5b\xa5\xc6~\xe1\x8e\xd6\x03e\xdf\xba\xb7\xfaB\xcb\xb0uz\x88\t\x0e\xc7\xa9\xb0\xe4\x17\xf8\xfe\xf8\x14\r\x99\xf0} \xdbWE\xd8\x96\x05\xa7\x86|\xa3g\xb2\xb4\xfd\x16,]\xed\xed`\xab"\xeb\x97=\xbf\xaf\x96T0\x14\xdf\x9d*\xfbA\x9aK\xa7\xb1M\x16\xba(\xa6\xd6\x87e\xbe\xed\xc9"\x93\xeev\xda@(,:9EM\x81A\x04fH|ajjjjjjjjjjjjjjjjjjjjjjjjjjjjjTue\x96V\x83U\xf7\xad%g/P?\xbe{Ty\xe4\x04\xe3\xf3\xe0\xc2;\x8b\xc0\x89\x8c\x8c$\xac\x1eZ[\x056siZ\xbd\x18\xd9\x89\xf5\xb6\x99\xcbW\xf2L\xb4\x9c\xb6\x7f\xcaT"\xcc%0\t\xd4\x07\x9b\xc2\x92\x1e!a\xdd\xdcaM\x9ekA\x9b\xaad\xbbq\xe4\xa7L_\x84(\x96R\xae"\xfc\xd8^\x94\xbe\xf41t\xb9\xd8x\xb3\x95\xce\xdf\x8f\x80Z%F\x84\xc4R\xa0\xe3\xd4\x9e\x0b\xe2\xcf_\xa6\xb4\r\x06\xde\x96\x0c\xcb\x1f\xe6V4%\xb4\x98\xad\xe0\xb8\xe1[\x9d\xfc\x08\xd8\x02\xa6k\x99.\xf4\x14E.\x10\x14\x0f\x9a\xe2\xd6s\x97\x00\xc8\x14\x91\xfe\xd1\x8b\xb9\xe7\xe3\xf1\x8du\xd99\x83\n\xbe\xd1\xa8\xc8m\xe9shFlqv\xfd4I\xd6\x97-\xdcp\x94\xb8\xbb\x1a\x86\xbe\xceT6\x81\xc2\xc9\x91\xd5#\xd7I\x8c\x1b1kqL\t\x8a~\x98\x99\x1c\x85\xc9\x9fE\xd9\x1c\x0c\xf6\xd8J\xff\xac\'\xd8\xd1\x90\x00\xa9?\xe0\xe2;\xc9\xed\xd6\x08\x15\xff\xf0R6\xe6\xabt\x92\xda7\xab\xdau"^a3\xc8\xa0\xa3Zx\x1b\xd6\x802\xf2\n{w\xdd\xb1C\xa7\x8a\x00\xcb\n\x95[\x0c~g\x95K\xbc.G\x91-\x8c4\xaf\xa0">F\x0e\xb9\xd2@\xf5\x16\xa8VA\xb7;\xe2\x9bg*\xb6\x1c\x1c\x9c\x1c\x82\xf9\xa4A\xe4j/\xf0bG$|\x1c\x8f\x95\x87\xe7\x92\xb0g\x82X);HcY\xe1x\xa6tU\xe8\xa1\xdb\xa1\x8bv\x18 \xb7\xb3b\xff\xc4\xdf\xaa\xb9\x17E\'U\x0c\x07q\x86R\x05_\xc7}n\xd1U\xba\x06G\x93\xadG%F\xcb\xe3x\xef\x17\xeec\xed8n0\x1b\xed?\xc9\xae\xf9\x8d\xf5O\xf5\xddg\x84{l\x83\xef\x93\x1e\xeeUD\xca\xddN\x9fM8\x99\xed\x1c\xa8e\xc3\xe9\x10f\xcc\xcf\xbeDZTS\xa2M,\xb3\x82b\xd8\x01\xd2\xdc\xdc\n\x03%\xc9\xa9\x91\x8f\x93A\xf8l\x7f\xe9\x13\x94<\x0f\xd2\xd3\xca.[\xb3e\xc0\x9b\xb7\xae\xba\xb8\xe2\x07\xfdO\x05\xd1<\x10\\\xf8\xb3\xef\x8f\x88\xb1;\xf9\xe3\x85{zP\x02\xdc\xd6o\x84\x0c\xa5\x85\xc9}T\xe0S\xcbd\x9d\x87FOt$6\xfd\xb7\xfb\x05\n\x91\xfe\xd8\xd1\xab\x93G\x99\x9a\xfd\x08\xcc\x01\xec\xc2\x06>\x8c\xba\x936To\x89\xe5\xf3\x04I0g\xa8\xbb\x85\x0b~u\xca\xc7s\x9e}\x96*:!)\x17\x02\x0c\xaepGu\xcd6\x81}\xd8\x98\x03\xcdQ{w\x92\xe5\x03\x87\xc4\xc3yW\xa2\x1c^*\xf8*\xf1\xc1\xcd\x18\xfa\x1d\xc6]\xb6\xd89\x84y*x\x8a\xf9C\xa9\xf5\xc36\xb8T\x06N\xd0\x8aq\x02\xebH\x03 \xac\x1e!v\xeb}\x974\x0b8\xb5O[8c\x84+7\xd6\xdf\xa2\xf9:k\xf1\xb4\x8a\x83\xd7\xac\x9e(\x1e:YZ|6\xcd\x112\x1d+\x84\xa1\xe3\x87B\x04p\xf0\xa7^\x16\x13\xf5\xb60\x91\x07\xfd\xbdq\xcb\xcd\xb5.\x81\xe1\x8c\xba\x16\xb0\xa7\xb7@J\xbc7\x1f\xa1,\xdd\xc3\x0bK\x89\x14\xc9\xe3\xdf\x0f\xc6Y\xf4\xb8L\x1f85\x9d\xdb\xe9\x1ay\xb1g\xaf\xd6\xa1#\x02\x8b\x1cq\xd2D6a0\x87\xdb\xd6\xd8"OI\xa8n\xc5\xbf\xdf7Qyd\xc4\t\xa6B\x0b\xab\x0b\xf7\xdf\xdb5/\xfd\x89\x04\x18j\x88\'M\x89%H\xc5N\x9e\xa5q$4\x1fWrF\x1f\x89\xa9\xf6_rK\xbcd\x8c\xf5\xdak\xf8R\xab\'z\xd0\xe7S\xe6c\tT\x18\x88\xcc\xed!\xdfPN\x00J\xd2\xba\xe9O\xc5hYG\x9e\x81\x9d\xbe\x17Xy\xe4G_l\xeb\xa5\xea\x11o\xf6\x11u\xb6\xb9Q\xe5\xfb\x0c\x15W\x08\x82\xcd\xb9\x9f\x19\xa2\xe2\xf8\x19fffffffffffffffffffffffffffffffffffffffffff\x01\xbbN\x85\x0ce4\xec\x1d{\xd9D\xb2\x1a\xe2\xc7\xb7\xed\xfe\x91H\xf2\xa3\xd7|\xb1\xdd~\x1e\xf3\xfc\x82\xb7B\xae~\x9f\xa5\x05\x8cM6e\xce\xf6*\xacw\x9d\xe2\xdcF\xdf\xe4\'\xb0\xd1\xae&h\xd6x\x04!e\xded\x1c\xc9\xbe\x06/\xf4\xd0\xd8Z\xf32\xe7_\x8c\x85\x07\xc3\x0f\xbd,\x7f0\xbc\x04sit\xd9\xde\xdeV\x06+\x8d\xb6\xca\x82\xba\xf64\xf6\x91<\x16\xbd]\xa0\xa8\x1d\xdc\xdd\xaamM,\xda\xfb\x92i\xe7\xa2F\x9eN\xc7\x06\xf6\x89p\xd1 \xd8\x9b\xa1\x899\xc4\xa6\xf8U\x19\xa0\xe4D\x9e\xca\x83\x9e`i\xedL\xf2V>\x82\xfaD]b\x12\xeb]\x98\xcc\x836\x19\xc3\x14\xc6\x8f\xb1\x16\xa1\xd3d\x032&\xb7\x1foO/*o\x98\x1b\x7f\xaew\x8cv=\xb1\x17\xe5\x14\\\xe1\xc4\xf8?^\xa9)1+BB\x81\xfci\xf7\xcbf\xe3\x17|-F\x82\xa3\xb7\x91\xba\x1738\xcba\xfd\xea\x99\xefa\xe2V\xfa\xd7\xf4\xdc\xda\x10A\xa3\xc7\xa62\xae\xcb\xc9\x7fa\xcb\xb8+@\xd1T\x96\x03\xa3\xa1~)\x85q\xdc\xd8\x01_\t\\\xc0\xf3\xbc\xb2a\xab\xab[|\xb8\xf4\x8b\xe3_y\x0b\xf0\x10r{\xdb\xb6L~\xc4\x01\xd6\xb2\xab\xfc\xbbh\xf0\x01\xdd\xd0S\x08i\xc6\x14\xad\x04^}\x16sKp\xecEq\x048{=\x8f\xcb#\x94\xc7\x85\x0e\x92\xaf\xcdA%4\xf4\x99Ht\xe9\x8fY\r{\xc2\x9d\xf8\xf1o\x1f\x96:X6\xf8#\xc3\xeb\xf2\xf0\x89\xa8\xf6\x13\xbb\x92\xa1\x1bPn\xea\xa5\xc6\xa1\x18o\x92\xc9\xc6)p\xd3)TO!#t\xb2LB\x92F\xe1 N\t\xf7\xf6n\x18\t\xd0\x19\x8a\xe0\xee \x19\xc1 \xa5\xab\x0cbZ\x95\xeaX\xea$\x1c\x93=\xa7t0\x08w6%\x88C~B\x1dY\x05\xb3\xe2\x03P/w\xe6\xc8}\xe4+\xa2\xac\x82\xd3~\xb9T,!\x1c\xf4J\x0b`\x99\x8e0\x18\xf9zr\xca\x82;\xc1\xe66X\xe7O\x1d\xf9vl\x0f3\xdb\xd9-\x88\x8c\x92JY\xfd\xc4N\xf1\x0e\xfb\xe2\xab-\xd3\x90\x894 \xb8\xb8\xcf\x8az\xb6}\xc6(\xad\xd0\xbak\x06\xbeGp\x0c\x00/\xe6\xf8`\x1b~9\x9b\x9ea\x1a\x0fS\x1d5\xc4\xf9O\x81l\xaf\x9c\x15Rgp^\xed\xfa2O\xdbM\xc9q\xe8\x0e\xc1n\x81\xb1\x1f\x86\xf1\tSC:\x8d!`O^\xba\x7fq\x83-W\td\xcfBP\x86\x0eg\x80<\n>\xc7{\xeeL\x83\xe4W\xcb\xc4\x9a\xed\xa0\xce\xb2\xccS\x0b.\x1d\xc5*\x92\x88\xf7\xac\'N\xc4\xe1\xadn\xdd\x15\x89(\t\xcd\xde\x1bP^d\xa4YJY\xda\xf5C\xce\xbdff\x83\xc0Z\xbe\x106\xc1\\\xd0\x08\x95\xa7\xde\xa6\xd9\xc8pg\xea\xde\xc2\xd7w\xfb\xdf@\xc3\x07\xd1n\xcd\xb2u\x15\xf3,W\x01\x8e\xa5NT\x16\xb2\xbcc\xf1\x01\x8dP\x85\xe6\xd9\xd7[b\xae\xb7\x9cE\x11AD\x9b\x18\xc25*n\x06\x97\x98\xc44wP\x85\x94\xa4\x07\x98\x18\xe0k\xba\xec\x88\xae\x93c\x93$\x8d\xb3\x8d\x10\xfb&Y\xb6\x98W4\x8b\xb6\xad\xad\xa7\xe2\x1bc\xf1\x13\x8f? _\xfc\x0f\xf2\xadK\xc8\x93\xec_{\xc18IMEw\x01N\xad\x19;\x01\xbf\xd6\xa4\xd2\x12\x85`1\xbf\r\xb7\xab\xf0Wupx\xd1\x0b\xf7c\xfe\xc1\xf8\x94\xb4\xe0\x11\xc4\xf1\xf9_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\xf1\xf2@\x87\x1b\xb7-\xb9\x08Q\xefh\xaa\xef\xde\x06\xc8\xbf+\x02g\x9f\x91\x8c\xfc[\xc0\x92H\x8cym7S\x94\xd5q\x12\x03\xaav\xb0\xb2\x86\xca\x99\xadfb\xda(\xe1\x97>U\xb6&\x1ff\xde\xaf\xf5\x0c\x857\xb0\xc8A_\x84\xfa\xd3a\x80F\xc8\x91\xd2\xdf\xee}\x9b\x16\xf3\xd7\x05q\x8e\x08uZ\xf0\x8e\xb8^|\xa6\x05\xcd\xc3}\x81_zQ\xe6\xdeY\x14:o\xc0\x94\xa2\x831Tk\x88H\'\x16j$\x155]T\xfc\xbc\xa1\xb7\x89A5,\xe9;\x8a\xe0c\xab&a\xf0\x87\xcc\xa5\xbd\x16\r/\x81\xe7\xde\xb1&n\xe9\xd9CEu\x1f\xc8p\x8e\xb6\xfd\xa1\x9d\xd6(j\x16<,Li>\rA[Ks\xdd\xea\xbe\x94\xb8\xaf\xf8\xb65\xe5:\x88\xa4\x1d\x084Y\xfb\xe4\xf5\x11uM\xa2+\xc9h\xfb\xac\xfbX\xc2F*\xf4\xaa\x9b!|"O\x8fq\x04\xb0\xc2\x89\x14\xa1#t\xf5\x16\xcbm\xfb\xb1\x9de \xe26\x86>X\x11\xc9\x9c\xe3hb?\xc0P_\x979L9\x86F\xf1[\xa8\xac/!5V\xd7\x10B\x10\xa3\x95\x81\x05\xc2;\xac~\xa9EOB3\xe12\xa4\xcfe\x90\x1ez\x8c)\xbf\x06\xc1\xad\xc1\xef\xbb\xc0\\\xdb\xdbNV\x1a\xc7TV\xc6\x01f.4\x8e=\x8f\xc5\xc6\xde@\xd4\x8e\xdbc!2\x05\xd8\xe3\xfd\x13B\xe2\x8d_*\x95mM\xda\x8aFbj^Bq\xc9MV\xd6\xb1;\xaev\xfd\x0f\xd7Df\x0b\x16\x8dQo\xe9\xadF\xdb\x93c\x04\x80\xb3Cc\xe8b\x82\x89\xdc\x87z%\x04\xa2\xcd\xc6&\xf09z]:\x9a;}L\x1e\\zz\xfe\x8a4!O\x14%\x95\xe3\xadA\xe0&X\xb6\xff\xce\x000\xf3\r\x0e>\xc4cdp\xc7\x8f\t\x1e\x87\xe2\xd5"\xae\xd4T52\x9d\xe3\x08<\xc4\xad\xb9\x1b\x86\x7fQ\xfa0w\xe9Y\x8f>&\xaa4\xa3\xc3\xb8X\xf7\xe3s0B\x0f\x1aG\xd3\xc5\xd6\xefU \xa1u\x90)v;\x08\xd4\x96\xc6\xe6.\x7fd\xc3\x9alr\xfe\xbc]BJ\x8c\x0fUi\x8a\xca\xf9\xe2IP\x9d\x0b\xa9G\xf9h\x19\xa0\xfb\x16f\xa0A\xdaC\x87x\x80U>|:m\x7f@\xe6\xf29_\xe9\x1f\xbdi/\xbe\x05\x80\xda\x18\xbf\x13F\x03\xb8\x0f\xf5\xa6j6\xe4\x92\x06\x81\x81*\xec\x9b\x98_/\xe1\x1d\x0b\xdbQ\x00\xb5 \xca \xe7\xc5\x0e:\xa3r\xb8\x1d\x0f\xd3\x83\xd7\x83\x97!\xed~\xa3\x1dL\xf3U\xc5\xe5\xd9\x1dw\xd1Wy\x83:`\xf0A\xfc\xa2\x16\xd9\t\x9cp\xfa}\xc3\xc3\x0c\x9e<d\xb6\x96[\xcd\x7f\xa2\xc2\xc5/Y\t\x0e\x82\x8e\x0c\xf2\xe1\xc3\x8f\xb2\xa1\xa47\x8f]e\xd5\xdfN\xc2\xec\xe0\xd9\xf6\x15Z\xeb(\xbax\xb3\x87#\x0b[5\x16\xc1\xeb\xe3\xe1\xe0\xbb$\xaa\xb7\xa4U\xfb_KF\xe5\xcc\x17j\x089l\x90\xfaR\xf7\xa7\xa2\x82\xee\xba\x8b\x16\x88\x06\x1c\xd9\x8c\xb0\xc1C\xd3\xe8\xf0C\xd5x\xba\xa0\x00\xa3D\xee\xe4Z6\xd9\xbe\xaa\xee\x1c\x84)\r\xec\xa3\xe4o\x06$\x98\xb0&\xc5\xb2\x97MxI\xd2%\x9e\xd6{Q\xcd\xe0\x04_\xc6\xf9\x82\x9f+_\x8c\xd8\x0e\x89\x8d\x13\x16DS\x0f\\f9\xe0,s\x12cS\x84;R\x18D\xa2\x92k\x15\xbcw\xd6\x14\xc2\xef\xfaA\x1c\x14\x9b\xd4e\xc0\x02\xe9\xde%[\xa8\xc6\x06\x05\xe7D|\xcaO\x7f\t-Dr\xd4\xb8\xcbC\xedkQv\xdca\x06\xdc\x8b\xf6\x96\xeb\\k\xdb\xb6\xc7\xda4\x80\xc8\xf9\xaa3\xdc\xc4\xa8k\t\x9fD@\x1a\xcf(\xa3\xc84\xb0\x9e\x82\x1e<s\xa0\x8c\x8a\xfdYM\xcc\x87\xb7Ejy\xdaVED0\xf9\x8f.\xa8\xa22(}\xb5\x9d\xe1\x83r\xab\xde\xa2i6*ka\xca{\x80\x8d\xf9\xf3\xe7\xee\x9b\xda\xa6\xeeN-\x97/?\x91;=\x92\x90Em\xc4\xe3\x81\xc4\xd7|>y0`t|D\xbe\x97\x12\xab/6\xf0"@%\x87>DW\xbfU\x81\xaa\xc2\x05\x8c\xefd\xe0\xa6\xe6{\xd8c\x10\xe9\x8b\\\xf6\xdfz\xbd(>\x10\xce\x18\x05(\t\x7f\x0f\xb1a\xc2\xe7/\x86\x91"y\xa3\x9f\xd0\xa4\t\x8e\xa4\xd4S\xef\xa6(\xd3\x9b\x13\xd6e\xe9\x02\x81N\x02\x92\x08\x10x\x06oF-\xd1\x010\xeb^/\xb1t\xba\x1b\x8a\xcf\x1bH\x03(\xb0\x04\xf7\xbd\xb9\xb4\xbe\x1f\xfb\x0fTq\xbbr\x19t\x08\xfa\xc8^g>\xf3\x1d}pj\x9e\xaa\x0e\x1e\xed\xbf\xac\xa1\x17\x94:\xed\'\xc1j\xb2\xad\xd2\xf1\xb7<13\xaa\xe5\x0fq\x80\xa5\x11\x1ba\xbfO"d\x1d\xa3 \x9a\xa2\x1bUP\x8f\'d\x06\x0f\r\xc6\xa2\x03\xec\xa4^\xdc\xa4,\xf6\x1f&\x91\x96\xa8\x03\x85\x80\xe2Sf\x08\'Us\xe4\xc7\x0e\xff\xf3\xc9~\xa8\x1f\x8a\xae\x04\x04\xc0\xe7\xb7\xc0X\xcd\xbd<t\x8c\x9f\xbb\xe4\xc58\xdc\x95\xd7\x83\xf2\xa45\xbf\xff\x16\x8f\x05\xcc8Ui\xc0\x08\xd3e\xaa \xf4\x86\xa9\xf2\xd6*\x03t!F)\xd2j\x0e\xc22\xd2\x9b\x94\xd9R[\xb1\x1dX\x80\x960\xfb\xd9\x02\x85\x109\xf2\xc5\x1c\xfe\x1e\xa2\\P\xfaH\x91U=6\xad\xb9L\xc6\xa431)\x9em\xf7\xfcH\xa8S\x9f\xa7\x90\xc3+\xe9\xe85\xa4\x07b\xebD\x98\xfaGC|\x85\xff\x19\x1a\x91\x00\x8c\x95- \xb3\xbc\xd4\x99*o\x0f\x0e\xb1\x9d\xfd\xeeC\x90\xc0V{\xe3(Qs\xec\xe3\xf5\x1bx\x1c\x1f\xc6WN\xadh\xb9\xd8\x8b\xa1\x03\x952\x80]a\x1b%\x7f\xdc\xe1\x08!\xbb\x89\xb0\xccVK\xd2\xb4}\x00w\xec2;(\xed\xd4\xf0K\xe0\xd0\xd1B\xb2\x99-\x8e/"\xfc\x00d\xa9\xa4Q\r\xed\xe0B\xb3\x88f\x17\x80\x80\xb5\xc0\x02=\x99\x9b\x87\xacY\xa3\xe2ha\x16\xeaJ\xa6E\x8d\xdd:(\xa4\xc3\xe1\x10\rM#\xd1\xf2\xb3f\xbcT\x9am\xad,\xd9\x85\xac\xe0\x90U\xf0\x16\'\xf44\x01\xcc\xf7\x19\x96N4G\x0c\xf6\xa3\xa4\xdb\xcb\x92\x9b\xaef\xf7\x82\xcfjWx+jC\x08\xfd+\x85\xb0F\xa6\x99\xb2\xe2G|\x96X\xb1\xc5\x8c\x9d^k\xf7\xa9\n\xd0\x1dX?|\xe7_\xf8\r\xd9(\xdf.p\xda\xf8`\x83\x84\xd0\xe7\x14B\x04K\xe1t\xe5n7u\xbd\xd3\x9f$27f\xdaf]\x15\x94\xfa\xcf\xd5\xb4\x8d=\x80\xc8\x9eE\xea\x13\xd3B\xe6\xd2Gr\xe87\xf7E\xf6\xf2g\xa8#\xfc\xb5C\xfb*\xfbE\x8c\xdc\x11\x04 M\xe9\xbf..O\xa2\xbe(\xb6\xb6\xbd\xa12Vg\xb6\rE\xdd\xa2\xd41C\xdf\x88\x0c|\xa8\x1f\xabL\x03P\xdbw\xfc\x91\x0f\r;\x8bl\xa9zWa\x9a\xd76s\xc5\xc9\xddH,D\xb6\\\xa2\x80\xbb\x9fD\xbf\xc6\xaf\x06B}\xef\xe6\x97\x1a\x01\ny\x80\x88\x98\xcc\x86>\xd0\x84\xf2\x08\x93\xd12\xba\xd9\x8dF\x11\x13\x10|\xa2\x00\x96\x05\xf9\x14\x9cy\xea_\xc2\x98\xdav\xe1J}\xdehs@\x80?uF\xec\x93\xfc\xfeG\x1e%.\xe1\xaa\xd3\x1c\xce\xe3\x9d\x7f9\xd3\xab\x88\xea\x95\xe6\x952\xb93\xf8\n\x01\xdf*\xc9Ci\xd2).&\x08s\xec-b\xee\xbf\x04\x88]T\xe4g}\x7f\xdc4\x80q\xce\xfesj/\x18\x95&+\xf8\xac\x18\xd02\x10{\xfc\xc8=\xb5\xed\xd6Wsn\xb5\xd0\x111p\x99\x9f\x86\xf3\x8c\x06t\xf8n2&|\x03\xe9\x93cYx\x94\xe1\xce\xc6E1>S\x05#\xac\xbdz\xdc\xb9.\x82\xc3"\x16D\xd4o\x1a\xbcD\xbd*\xef\xd1W\x1b6\xa0j\x8d_\x8eY/J>\x8c/FN\xd9\xc8D\x95\xa2\xfb\xf5\x1f\x8d/\xa9\x19^\xf8\x1a\x06Q\x88\xca\x08Q\xb8\xb5"0\xa9u\x01pRP\x12\xe88@k(1`\x1b\x07q\xe2\xd5\x0cHE\x86\xe2@l[|\xe3O\xfb\xb7\x1d\xe0j%h\xa8M\'\xdd\xec\x8d(G\xeb\xf5M\x97[\x0c\xd6\x8a\xec\x14\xdf{$\xf0<\x11=\x00\xfb\x0ci/\\\x8er!&\xf9(\xdf-z\xf5H\xef\xef\xf1\x8f\x05\xe3\x8a\xd6!/R\xa1u^\x8c\x9dq\x9e\xb8:\xb4]\x83\xf4\xcd\x1d\x0e\xf20W\xab\xedd\xce\x96\xeb\xcdE\xd3\x9d|\xce\x9epj\t)\xe8\xdc\xff\x94\x03\x11y;\xd3[\xd8\x84Uz{\xf3\'M\xb8\xa9\xceG\xe2P\xf9\n\xd3\x14;\xdeXU\xac\xc6N\xffA\xfd\n\xb8\xa1\xad\x017\x9ch\xbc\x9ff\x0ev\x9f\xb0-\xe8\x9c)\xff\x04\xccG\xcfz\x9d\xbb?JQ\xff\xe6\tY\xe2\xb0\xb8\xa3\x8b\xbc\x8c7\x01\x04\t\xbc\xda>\x82\xe3`\x1e\x0b&a\x9f\x9d\x15\xf69\xde\xf1h\xea}/\xe3G\x95\xc6\x0b\xfdY\x81B\x7f\x93\xdf\x16C3\x95\r\xf8\x11\x08\xa8\xbb1\x06y2}S3\x7f\x86b~\x82\xc3\xcb\xfd\xcf\x94\x9eal\xdd\xf7Y\x12AE\xc3nR%\xb2jH]2\x07KP\x180\xefyPI5\n\xe5$\xe3\x03K0\xe1\x81\xcf\xa1\x17\xeddg:\xe1j\xcb>\xf7\xc9\xa4O\xdbK\xaaN\x17f\xfbNG\xae\x9a\xda}\xfd1\xbf\x8e\x01\x94\x96R\xe7n\xd6\xf1\xf1B,\xe1H\xb90\xbf4\x16?j\x1d\xf6\r\xe7\x047h\x98%(\x05\xfa.L\xa4C\xa4\x12u\xfe\xeb|\xf9\x17\xb0b1\xd1q\x1e\xf2\xa2\x94`O\xd2\x87\x04m\xc1\xf6\x00\xdf\xf4\xd2\xd8\xcc[\xd0Y\xaeD\x97\xb9\x89\x08f_v9\x1c\x9a\x84\xa1a>jT\xea\x1d\x96\x94\x07\xeb\xbe\xddj\xc4\\\x8b\xa2\xe0\xde\xf2\xc9\xac\xa2\x1bN\n\xe4\xc8\xb9\xe7\xd1\xfe\xf0\xd8\x9bR\x12\x93\xdaV\x921\xfe:\xa7M\xbd\xc8R]\xbaMg\xe2\x13\x1c;\xb6C\xd5\xd4\x0b\xc4\x9d\xd4\xe9A\xb8/\xb9\xf7\xb0=\x07I\xd3Gv\xe7]r\x9aT`}e\t\x19\x9c\xea\x0c\xdf\x08\xbf \x84\xd6\r\r9\x04\x15\x1c \xd3\x10p%\x83*F\xb08QS^\n\xab\xf7\\"y\xb3sX\x89d\x12\xa4\x7fk4\x1c{J&L\x95\x0c(\x13z\\\x1e\xecZd"\xfe\xf1AZ\x7f\xb2\xbe\xc6\xfa\xe8Q\xe8fx\x94\x0b\x0b\xba\xa4\xda\x15\xab\xfc\x0142\x9b\xbcf\xf7\xc0$\xf6<\xb7\xdbc\xea"\r\xec\xa7\x8d\xebL\xbb\xb8\x8f\xbdc\xbaNp\xbb\x86+?\'\x88\x19\xda5\xd8-\xc4\xe05w\xd4|\x93R98\xfb\xf9\xcaa\x8a\xed=F\xcc`\xe8\x9b\xa5\xa4liRO\xf7\x063`CF\xb6\xb96s\x00\x08\xa5[\xc0^\x1b\x92\xbd\xec}?\\;b\x9fI\xf6\x99R\xc4\xf4\x9a\xff\x95\xc2f\x9c\xb3\xb1\x97^\x19\xfc\xe3\xea\xfaZ\xb9`\xf9E\xb1\x1b\x17\xc1\xd9\xed\xe3\xb97\xc8\x88\xac\xafWY\x83D\xa2\x84\x99\x0b\x89\xa6\xd3\x96\t\xe0\x1f\x88\x88q\xaf\xc3\xb4Ne@\xff\xbb0\\\x17`\xbd\xfd*\x9a\xf3\x05\x96\x9b\xcd=\xfa\xf3E\xe6\x8b\xea3\xfb7\x8a$GQ\xbf\xfb\xfal\x84\xbew\\\xdf\\.\xfcW\x8d\xe7\x9e_h\x05\x10\xbd\xbb\xc7\xd3\x1a\r\xe8%\xab\xc0\xf7\x8c\x02\xc1R\xdb\x8d\x14\xc7U\xa0\x96\xff\xdd}\x1a\xad\x17)ap\x82\x94\xf2W\xadB\x98\xdav\x08\x1d\xde\xe7\xd4x\x99\xe05\xd65d\xaa\x0b\xd6\x86\x1a\xb9r9\x8b(n\x08r|OG\xe2\x1b\xdd\x07\x08\xa1\xbd^hbe\x9f\xcfZ\x9a\xb1\x8d\xef\xea\x12B\xb9u\x92W\x9d\x87/\xf5\xab=\xba`\xb1\x91\xd4\xb8\x84/\x02\xe9\x9aP.c\xc1,\x8e\xeda\x98A\x026\xd4\xcfG\xcb\xac\xf5\xfb\x8a|Ot\xac\xa50j*\xca\xba\xb1\xf3\xcd\xa7\x8f\x15\xeaLR\\F\x8b[y\xf6\xd7\xef\xfc\x9a\x08\xa7\xf3;\xa5~\xa1\x0cquS\x89#~\x92t\'^5+\x86\xad\x86{\xaa\x97\xb5\x88\x81Nw\x99!\x9f\x05\xb5*J\x84\xd7y\xb6\t\xea\xb8!(\xd1\xf3\xeb\xd6\xf1\xfb/\xe8\xdc\x90^\x81\xcf\xf1\x9e\x05n\xdbD\x13\xc6C\x96\x81\xdaxfk\x13\x94\xde\xd7\x00\x0f\xd3\xd8\x06\x8f\x00r\xf7\x16JO\x92\x15\xbaYo\xfa\xf2\xcb\x90(YA\xc9\r\x1036+\xdd".\xc4D\xcc\xd0&\x17 s\x80\xc1\xc1\xbf\xd7f\x80a\xfd`\xea\xd6\xb6\x7f\xed}\xeey\r\xe4a\xffg}+\xd5\x9a\xa4\t\x1b\xb1\xfe\x01\xe0\xa0\xaf,1\x93\x05\xbd=\x82\xdb\xf6\x9e\xe9\xabGB\xad\xc1\xac \x19\x04\x91\x90$\x85\xbc\xd8\xe1\x15(\t\x81^nm{\xe8\xad5\xea\x05If\xd1 \x1e,"t"|Dk\xfd\xcb\x01\xa3\x0f%\xb7\x88\xdcN^\xb0}\x1f\xf8\x97\xc8\xae\xe3\xc1!k\xbc\x16\xc2\xf4\x15:H\xebJ\xe7\xbf\\2\xff\x0c\x9f\xc8\xf3&\xb9\x0fD\xe4!~\xb6\x1f\x84\xcf\xc6\x064j`0\xafY\xce}\xa1\xbe\xad\xa6}\xd0U\xec\x90\xf0aB<\xc7\xaa\xa1\x18 \xa8\xb7D\xa7\xe9^\xfb"\x10\xdf\xe4\xe7\xcd\xaeP\xf0\xdd\x98VY\x928&U\x9f=\xce\xfe\xbcP\x81\xc1_7\x97\xcf\xa3\n#\xbb\xee\xb7\xd8\x0c\xb2\x9a\x8b\x16W\xef:\x1bFk\x05\x811a\x07\xd3(\xb3\xd9\xa2\xad\x12\xd6\x9a\xb0\xc2\xea\xa0\xbe\xbc\xe5\x97\xa1\xc2D\xbfE\x923\xcc\xe3\x00\xa3\x11F\xc8I\xecO\x90a\x12z\x86K\xad\xbc\xbb\x99d9F\xaf\x00\x94\xf7wI\x9cvs\xee\xcfd\xdf\xdc\r\xdc\xf6\xde`\xb9=aa\x9f\xcdt\xa8\x14\xb2\xb6\x01\x96\xc1\x18\xb3J0n\xd9NM+\xe7\xd32v \xee\xe0G\xdb\x1dC\xeb\xe7\x13u\x7f\xd54\x92\xb3\x01,z\x0c\x00\xbb\xb5\xb0\x88\xce>_\xa90\xd1\xd9d\xfa\xbb~\x13\xc94\xd5\x06\xa8_\x94B\xf4G\xcc\x19\xf0\xfe?IK\x1c1G:\xe1\x98\xde\xdaF\xef\x94F\xf1\xa0uh\xc4k\x8cz\xcb\xd2\xe4}\x81=\xedg\x8bY*U\xca\xacY0\xed\xa3T\xf5\x88\x93\x95\x0c\xe7^\xe3\xaa\x00#\xa25\'\x9a^\xf7~:/\x17\x88\x04\xd6\x1f\xb2\xd1\x871\xff\xdc[{\'bw/\x04DU\xff|Y-\xca?CGc\xd3\x8f\x14\x95\xe2WWIgbbl\x84:\xc6\x97Y\x83\xdd\x0f\x8a7,D\x8d\r\xd4\x9f\xe2\xc1$A\xa2\x1a\xc2\x89\x11\x10\xb1\x00S\x85a"\x15m\x99\x93\x07\xa0-\x12\x9c]A\xe8\xc6\x90*Oo\xc3\x9fz\x90\xd9{m\x83V\x13wK\xf3\xda\x99\xc4 \xc09\xd5\x85\xf7\xe0V\'=F[\'T\x1cL\xff\xc7."\x036qs7\xcbT\x1f\xa0\x98\x0b#\xb0\xa24\xd2\xc5\x01\xb4\xb1\x18 [\xe5jX\xd6\xd2\xce\x03\xd2\x99\x8b\x98\xb0\x15\x06L\xebzU}\xedq\xdb\xa4\xed\xad8L\x02\xa7J\x1eK\xc5\x90\xa7\xc7\x85b$U\xbd+\xb8 \xc5&\xba%\xe2\xa2%\xa8\xcb\xcc0q\xc5\xfe}q{\n\x07\xd2t\x08C\xfb\x13\xab\x90\xf5^\x98\x88!\x12\tI\xd9\xd6\xb30J\xcb\xf7\x1d\xab\xd5GF\x9a\xd0\x02\x0e\xa2\x19\x10\x06\xac\xf3<\x1a\xcdW8\x8d\xabe@\x1f\xfe\xf6\xf6\xe9U(\xd5\xae\x0c\xb5|\xd2>\x93\x928!#\xee\x84\xccrmx+\x8e2\xe3\xc4I\xafth\x0ceS\x84y\xa2\x91%\xd2{\x95]mJ\x14C\x89\xe6C\xf3\xdbl\xb5od4\xeeg\xdd\x1e\x86\xfat\x93\xcf\x8d\n4o\xf1)\x19\xd8\xb6\xef\x96c\x0c/\x95\x7f\x0f\x99\x98\xa7\x92:\xda\x1b\\\xc8\x82?\xd5\xce\xbd\x13\x98S!(>\x8d_\x12\xf8\x07R\xcf\r\xf16-\xa2U\xdd0\x06r*7=\xfe\x025Q\x1dgO\xe3\x01PoY2\x98\xb6_G\xc9\x1f\x92\xe8Qa\xb3\xc5\xbe\xde\x07"uZ\xa0`\x1b9tl\x9c\x81\r\x02f\x9f\xa1\xa1\x05w\x81\x9e\xac\x96h|\xa9~Do\xd7\xf5\x8c\t]\xa2\xaa\xc58\xffn\x855\x95\\\xd5\xc9\xb9S:\xfe\x13\xc5\xe1\xb6\xf5n\x0b\xa1M\xe0\xae\n\xf1\xbfJ\xe0\xc9\x02\x97)\xc6\x0f\x83\x8c8R\xd2]\xc9IuU\x9c\xba\xf0q\x9c\x9c\xed\x05\xa1\xf4t\xdaz"\xf5\x7f\xd7R~~\x14\xf7\xa3\xe3\xc33\xea%|\xa0\xda\xddX\xb2\xdf2)\xd8\xc8\x87zO\xdf\x18 NVb\xeaI\x03N\x13\xc9\xf5\x0b\xe6i)j\xf7\'~k\xaf\x81\xe0n\xff\xf5R\xf8\r\x9d{\xaa\xfb\x07o5\xf2\xf4\xd2t:Y+7\xb4\x14\xa1j\xf9\x83\xac6\xb1:\xb7\'Y\xba\xd6\xe3\x1e\xcf\x1f\xb8\tZ\x15\t\xe8CT\tz\x1fx\x0fPO\xd0s\xa5\x91\x07`x\xac-\xbaz\xc0\xc8\xa1}[\xe1\xf7z\xde\xe7\xb89\xae<\xe9I&\x06\xd9o\xe6\x8fga3\x0f\xd5\xb2o\xfd}\xb8?H\xdd\x8b\x1c\xeff\x1co\x83\x8aE\x84\xd5\xf1\xa6\xdd\xf0\xb4\x19\x00\x8a\xb6\x9b\xfc4\xbc0V\xd4\xadd\xdef\xd7F\xfa\xc6KV\x94~4\xf7>\x8f\xa0\xeeimVr\'Y\xfc\xbcl3>@\xd3^%\xa9\x85\xe4q-#\x16\x1d:\x11\x08!D\x9ai^%\x96\xff/\x8bu4\xf9\xe1\xb3\x93\xde\x06\x9d`\xe1\xe6$\xc2\x88\x86\xf9z\xaf\xe9J\xe5\x82T4\xd2\xafR\x90\xc2\xc0\x06[\x9d\x04pM\x85R\x1e\xa0h,u\xf6\xc3O/\x15\x01c`\x1b\x92\xf5\x07\xe0\x89\xa6\xd0\x84\xbc\xd7\xbb\xb5\xdd\x8fZ\xdd\x0f\xc8\x14\x16e\x0e\xfc\xfe\x19T\xc2N\x07J\x05\xc8+\x17\xda\xd8\xaa\xe8\xff\xa2\xbd\xb2\x93!"@\xec\xd3\x8f\xea\x84\x9bI\xd7m\xda\xfeRn\xdaKK\xedW\x99D\xac\xb9H\x14\xaf.d\xb0\x13$E\x0bP\xe4n\xb0\xdb\xe20\xe0\xa7\xf1+\xd8\xde:x\xd8\xb1\xe9\xce\x90X\xbb\xa4ZMf[\x8f\x8c\xe0\xab\x99yYO~8\xe8\x92}\xdb\x99a9gM\x15\xac\xaa\xd5\xdch{\xe5;=\xfc[\xd0\\\x92\xc0Q;\x88\xcc\x9cs6Q\xcd4O\xdd\xadu\x9a&\x8ep\x16~\x94}\x9cI\xd2\xbc-"r\x18\xe0\xdbGI0\xd1]:\x97\x9f\xbcF`\x8c\x16;\xebA\xca\xb2M\xba&\xfc\xaf\xd8\xd48\xd6\xe9=\x84\xc9\xf2I\xf4\xb1\x18\x84\xddH\xe2\xbe\xb4+1\x11\xb8O\xb0\xc896P`=*\xe3\x198@\xb7p\xfa&c {s\xff9\x85@\x87\r\tG\xdf-\xee\xb0\xd7\x16\xfa\x84\xeb\rH\x87Q\x84\x9d\x9c\xba\xc2{\xcf\xff\xd1\xd4s\xe7\x8d/S\xa7\xaf\x83\xb99\xe9\xc7i\xf3*\x0c\xbfqK/\x15\x97\xc5>cv\xb9\x19e\xa1\xdc\xde"\xdb>\x82\xe3g%\xceK\x9a\x0b\xfc\xb9\xe4\xc8\x13dU$7\x9c.F\x82\x00\x18!k\xf0\xb5@5H\x88ubM\xa1\x82\xa1\x10\xc4\x18\xb7\x19\xc7\x0e\xad\x97\\/P\xabQ/\x9f 8EP\xd9\xac\xf0\x03\x02`\xa0\xca\x7f\xac\xe1d\xc2\\\x84\x019\x99C\xa0o\x13\xb6\xf0\x87\xcd{\\Z\xe6\xbe\xb7|T\xaf\xd9\xe2\x06\xd3\x9c\xc8\x1f\xb3?\x01\x96{\xef\x01^\xe4\x93=\xc4\x94\x85C\xa3\x00n\x12\xac\xce[%<\x99pqP\xc0\xf1\x8c\xbdO\xf7\xfb1\x08\xc0\x17u\x1e\xb9\\;\xa9\xc4\xfb\x9f~\x9c\x94\xa8\xd4\x15\xa2\xfc\x8bS\x1b\x91\x8fe\xdc\x9e!\xdd\xbd\xc9\x98\x8f\xb9\x94\x86\x08\xa4iP<\xc2\xd71\xf8L\x9b\xfei?\x93 \xd0\x1d\xf0c\xb8\xd6\r\x0b(\xcb\xffxy\xf3\xc6=;\xd0;B\xe5I\xc8w&g\xb6\x81\xb6Q\xe0\x9b)\xef\xf8\xa6\xd86\x04\xf6\x97N\xd2\x1f\xf3\xb7.I^\xa7\x12\x9b\x81\x17\xb9\xbea1\x1e\x8f\xb4D\xaf\xda\xdb\xcd5,\xb4iv,\x88\xa1ki\x89\x97j\xc2B\x99\x92\xdb\x06Ai\xbd\xf7t1!\x03A\xc2\xa7\x9e\x02GR|\t\xc2\xaaS\x9d\xb2\xfb\x1b\x0f\x08\xd1\xa5l\xd5pl\xed\x1bF\xdeu\xf6\xb8\x1a\xaa<\x16\xd9\x9cc\xb7\x95\xa8>\\\xc9P#;\xe5\x1f\x82g)\xdahl\x0e2\x03\x14ox\x18\r\n:\xccREvt\x06\x15@\x194\xe4\xfa\x85\x87\xa9\x96\xf8\xbc\x11\x91\x8f\x8d\x9cC\xbe2L\x0ct\x90[\xc1\x01-}\xf6\x8bM3\xf7\xa0p\xfa\x99%\\\xf0,\xa3`r\xbao\x9e\x95\xf8\xa1\x82\xa9V\xe5\x01B\xa1\x7fhCnz\xbc\x87\xc4@\x95\xee\xe5\xa8\xfc\xf5\xf2\x95/Bi=>\xb6\xa3\xf4\xd9k\xc3\xc1\xdfvm\xbc\xe5\xd1<;\x97\xe4_\x83o(\xdf\xe3x\x1d\xf5\x16\x81p\xe7pU#\xb5q\x9a|\xf3\x91|\xc1\x9eE\x13%\x8c\x1e\xd6E#\xe3\xf8\x8c8\xaae\xd3\x07\xdb\x10\x8a:w\xdf\xbbIqa\xe1\xa1\x94\xca\xd5\xc0\x1e\x87D\x95\xc5\x15\xb8\x07\xe41\x83\x1e\xccl\' \xba\x02\xf0\x15\xc9\xaeN\xb6r\xc7&\x91fU\xb7?_u\xe5\xbe;\x8cg\xa4;/\xddN\xb2\x1e\xf1\xab\xc3\xe9G\x16C\x16\x11\xd3O\xb5I\xda&\xe9s&\xc2e\xa7A\x81\xfb6i\xa5\x9b\xee=m\xdd\xbc.\xfe\xcbN>\xb2\x7f\x82\x98R\r>\xcc\x14\xc33\xaaA5\xf5\xdb\xecr\xf4w\x16\xf9w!\xff\x1ec#2=\xab\x90)\xc5\x96Ni\xa6\xa2\xbfs\xe3]\xce\xde\xee\x9e;\xf1b\x18[\xfd\xe3X\xcc\xd9\xab<\x9d\x08Y\xcb\x04\xab\xd0\xcf\x99:/\xc1@h\xb3@\xfc\xad5J6\xb9\xa33/\xc7"\xf5\r\xc0\xa1\xff\xd9\xe9\xb6I\xb2\xc7\xa6\xbe\xd4\xf5\x90>g\xb1=\x8f\xaf@\xe6\x84\xa8\n\xfeIG\xe9r\xd5\x07%\x98\xe3\\05\xc0WV\x8a\x908&\x1a\xa5\xf5V}Xq\xe3\x18;2\x90\xac\x16\xce7\xb8\x8e\xa5\xd3\xa43\x96\x94EV=\n\xe0\xfd\xeb\xd2\x85+\x99KEt;\xd2(\xb3\xfem\xebw\x7feji\xee\x917\xb5\'\'(\x8b\x1f\x06\x15\xe5\xc4\x8f\xfc\x0b\x83\xa9\xe59\n\xd9\x15\xa6\xc9\xbf\xb8\xb9)\xf4\xe8\x0e0P\xee\x8e\xcc#\xb1\xd2\xfc5\x17\xac\xce\xbf\xc8\xac&\xa0\x1d\xfa\xd4\xaf\xae\x0f\xd4\x82 \x92k\x076\xfa\xaf\x93\xf7\xc3\x0f<\xc8\xd4\x05\xf8L\xcdc\xb0\xc2,\x0e\x93\xa1\xeb\x03\xfcZj\xa7\xbcD+\xf1\x1eT\xb0\xba\x8f\xdd-\xf2\xc3\x8b\xc1)\x95\x15\xf9i~\xb7\xcf\x9cS;\x90m\x93W)wg\x98\xf4\x14\xed:\xaf\x01P\x16\xd8|\xaf\x05\xf3(\xe4\xc7\xae\xa1p\xfc\xc4\xb0\xde\xcc\x11\x99\xec\xc4h\xc8kJ\xeb\x9c\x16\xff[N\xe7\xa8+\xad\xa6pu 1\x01\xcf\xee\xd2\xce\x94LR\xfc\x94\xe0\x97jA\xb8\xfa\x06\x1b\xa46,\xedP\x8e+(\x8c\x8a\x90\xc4%\xfd\x13\'t&\rY\xb2^\x85(\x07\xcd\xc9\xd3g\x9ee\x86X\x8a4Z\xad\x8b\xcf\xb5\xcc\xe0L@\\\x08\xbc\x1c\xb7\x8f\x9d\x1a\x9d^Rsy^}\x0ce\t\xdad*`B\xc0\xaf5\xd0.\xd8\xa0/\x12\xee\xbd\x08\x8a\xa8Z\xf1\xffm\xebI\xb8\xdfi>\xb3\xc4H\xee\x1c\\\t:!l\xbe&\xb5\xf2\x0e%oK\\\xdc\xdd\xa6j\xbe\xb2\x11\x9f\x8e\t\xd3k\x0e\x13\xb7\xef\x14\x91Ug\xeeo\xf5\xe3\xe6\xd2S\x95\xdb\xe7g \xe4\xb7\x12\x12\xd0\x95\x14\x17s`\xee\xf8\xd6;\xa1\n\xd4\r\x92\x0c\xc3P\xb1\x01\xce\xc9D\xb7\xae}\xfby\x01E\xf4<\xe5Hg\x0b\xc6\xde\x91\x07\xcd\x9by\xa7\xf9\x17\xd0\x965\xccY\xef \xbf=OC\xa3\tUa*\x16\xff\xee\\\x86\x83\xc2$\xb7\xf6\xde\xc4}2\xd5\xaa\x1ag\x8bM\xe3\x80\x160\xde\x00bT\x1a\x1c\x9d\x96\xca\x85\xaf\x0e\xff\x0eB\xa0\x1b\x9f1\xc0 \x8a)\x1a\xe6\xd2\xe72\xf8\x81\x9e\x01\xce\x01\xd7\x9a\xf7L\x0bT\x8cf\x8aa\xfc\x87\xf3;`\xf02a\xa1\x86E\xeb\x8a\x8bPRW\xf3\xe7\xc8\xbb\xdb\x9e\xad\xbf1\xa3\x9d\xaa5\x19>h\xa8Q\xd8\xf2\x07D:\xb1\'\xf8\x10\xd7\xb1\xea`;\xbfs\xc4\x0e\x0ecH\xe0\x0e\xec-\xe7$\xb4mZ\x1d\x16\xaf\xcbu\x88!\xa4\xe5;\xed|.\xf4\xd45\x90\x89J\x8c\x133\x07\x10-%vodi\x88\x02\xa0\x16\x16\xd8X\xa6\xf6\x10\xef\x8b\x85y5\xb7k\xfd5>\x11\x08\x0c\xdfP\x11o\xdfR7\x85\xfc\xc7\xf3\x0c\xca`(\x85\x9f\xaaDxF_@9S\x817\x9dK\xe5\xc8\x12Vp,?\x7f\x80f- \xcca\xe8\x97\xc2Y^\xd0\x1d\xd3oR\xae)\x95\xf6\x07\x9a\xea\x03\x01i\xf1\xfe}\xb8\xc1\x0c\xa71\xbfm\xf8\xc9\n`\xb8\x1et#*\xd2\xcc%\x0c\x1f,\x03)\xd2)\xae\x12"\xb3roOu\xda*#*\xac@\x8e\x16):\x9fy2i\x9a/\x18\xdb\xea\x9c\x00d\xea\xe9\xea\x16<m\x0ft\xbf\xec7\xb5o\x10l\x1e\xc8,\xe0\xd2\x91\x8c\xd4\x88=1;K\x81\xca\x91\xed\x81\xc6\xe45:\xee\x93\xb9\xde\xf4\xe2?\xab\x1a\xee\xe0\xa1\xf2\x7f3\x03\xdb^\x9b\x80\x1b\\\xf5\xc8}H\x9c.\x80\x07"\x8b\xc4^\xa0\xa0\xb5iZ\x1c\x13a\x96\xbd\xa9*\xa19\x01\x97\xd6\xeb\xff#\rpP\xdc\xb3\xd2\x95\xcc\x88T7\xac \xf3EAQ!\x14\xedI\xe5\xba\x10\xd2\xc8X&M\x06\x04\xab\x05\xc7gQ\x881\xecv\x9c\x13\xae\xdf\x92bc_\xe7\x05\xf1\xe1E\xea|\xdf\xaf\x85\r\x07\x97=A\xc6\x0c/#V\xd9H&S\xf6\xd0\xd3\xab@\x95\xaf\xb6\x02\x17]\xc7\xd5F\x05_4:\xc1\x96\xef\x10\xfc\x86d \xbc\xdb\x94J\xa1\x94E\xca|Z\x9fn}\xd2ep\x85%s\xfe\xa4\x9a\x9b\x05"\xb6\x938\xd9\x14M\xd2\xff<\xd7E\xe9\xbe\xd8Zb(}5W~,\xe4\xf0\xbc\xa3\xa1\xe4\xc9\x82\x89-K\x0c\xa9\t1\xe1\x1a\xdc\xfc\x95\x0f\xb39\xa8\t\xdev\x16\xf4\x8f\xd4\x19\x19\xe9!(h\xad\x8a\xaeY\x9f\x89\xf76k~\xc5\x8d\xd0\xfd\xdfM\x10!1\xb9\x98a\x1aA\xfb\xc5)\x0f(r\xa2\xbb~(\xb5\xe0C\x19\x8d\xa8\xca]@\xba\xc8\xb6\xcd}\x9f^\xe6\xa3\x7f\x14\x10\nC\x86,vE\xe2\x1c\x84\xfb\x8a@\x19\xa0\xfd\xbc\xf9(!\xf0\xf0\xddm\xbc\x98N\x87 m\x84&\x8d\xcd\xf6G\x12\xc5\xe3\xd5L\xe7`\xcd\n\x89\x88\x85x%\xc1\xfd\xe1\x7f\xf6\xad\xfb\x16\x12\xf1\xc6\xb8*\x10\xa3qhI\xe0\xf3\xb9O\x08cr\xcc\x87I\xb9\xb0C\xa9V#\xa3\xce\x0b\xee\xb4\xb6\x07\xab\xa3H&\xf6\x8c4\xebiN\x9c\xe0\x84\xc4<\x9a [J\xdeY\xf7:\x9c\xa4\xdb\x1e\xfb\x90R\x13Ab\xee\x8f[\x15"N\xden\x02%%\xfa\x9c\x88\xc2e\'\xfe\x89\x9b\xdd\x86\x82\x0f\xcfv1\xec\xb8\xf5\x1c/\x971g\xc6\xceg\x10\x0fR\xce\xa6\xae4\xa0"|\x14\x9b\x05g\xae"\x1eU72\xe8\xae#\x19\xf7T4\xf4\xe6\x96\x84\xa9\xb6\xd8\x94U\x1a\xa4\x1cOc\xb9Y\x1e0\x17\xdf\x84xe\xd6:\xf2=\x1a\'\x96\x96N9I&]\xf5\x9f\xc8;h\x91_\x91;g)t\xf5\x01*\x9b\x16\xb8\\e\xc8\xbfz'
=== removed file 'Vertex/vertex/test/test_bits.py'
--- Vertex/vertex/test/test_bits.py 2005-08-05 06:02:56 +0000
+++ Vertex/vertex/test/test_bits.py 1970-01-01 00:00:00 +0000
@@ -1,68 +0,0 @@
-# Copyright 2005 Divmod, Inc. See LICENSE file for details
-
-import array
-from vertex.bits import BitArray
-
-from twisted.trial import unittest
-
-bitResult = [
- 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2,
- 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3,
- 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3,
- 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4,
- 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5,
- 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4,
- 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5,
- 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5,
- 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3,
- 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6,
- 6, 7, 6, 7, 7, 8 ]
-
-
-
-class BitArrayTest(unittest.TestCase):
-
- def testBasicBits(self):
- prev = BitArray(size=3)
- prev[0] = 1
- prev[1] = 1
- prev[2] = 1
- for size in (5, 6, 8, 12, 14, 15):
- ba = BitArray(size=size)
- ba[0] = 1
- ba[2] = 1
- ba[-1] = 1
- assert ba.countbits() == 3, str(ba.countbits())
- xo = (prev ^ ba)
- cb = xo.countbits()
- assert cb == 2, cb
- prev = ba
-
- def testPositions(self):
- SIZE = 25
- bitz = BitArray(size=SIZE)
- self.assertEquals(list(bitz.positions(0)), range(SIZE))
- self.assertEquals(list(bitz.positions(1)), [])
- rs = range(SIZE)
- rs.remove(7)
- bitz[7] = 1
- self.assertEquals(list(bitz.positions(0)), rs)
- self.assertEquals(list(bitz.positions(1)), [7])
-
- def testDefaultBit(self):
- a = BitArray(size=100, default=0)
- b = BitArray(size=100, default=1)
- self.assertEquals(list(a), [0] * 100)
- self.assertEquals(list(b), [1] * 100)
-
- def testCalculateOnBits(self):
- calc = []
- for x in range(256):
- c = 0
- a = array.array('B')
- a.append(x)
- for n in BitArray(a):
- c += n
- calc.append(c)
- self.assertEquals(calc, bitResult)
-
=== removed file 'Vertex/vertex/test/test_client.py'
--- Vertex/vertex/test/test_client.py 2006-06-27 13:34:49 +0000
+++ Vertex/vertex/test/test_client.py 1970-01-01 00:00:00 +0000
@@ -1,30 +0,0 @@
-
-from twisted.trial import unittest
-from vertex import q2qclient
-from twisted.python.usage import UsageError
-import sys
-from StringIO import StringIO
-
-class TimeoutTestCase(unittest.TestCase):
- def testNoUsage(self):
- """
- When the vertex Q2QClientProgram is run without any arguments, it
- should print a usage error and exit.
- """
- cp = q2qclient.Q2QClientProgram()
-
- # smash stdout for the duration of the test.
- sys.stdout, realout = StringIO(), sys.stdout
- try:
- # the act of showing the help will cause a sys.exit(0), catch that
- # exception.
- self.assertRaises(SystemExit, cp.parseOptions, [])
-
- # check that the usage string was (roughly) output.
- output = sys.stdout.getvalue()
- self.assertIn('Usage:', output)
- self.assertIn('Options:', output)
- self.assertIn('Commands:', output)
- finally:
- # always restore stdout.
- sys.stdout = realout
=== removed file 'Vertex/vertex/test/test_dependencyservice.py'
--- Vertex/vertex/test/test_dependencyservice.py 2005-08-05 06:02:56 +0000
+++ Vertex/vertex/test/test_dependencyservice.py 1970-01-01 00:00:00 +0000
@@ -1,69 +0,0 @@
-# Copyright 2005 Divmod, Inc. See LICENSE file for details
-from twisted.trial import unittest
-
-from vertex import depserv
-
-
-class Serv(depserv.DependencyService):
- requiredServices = ['one']
-
- def __init__(self, **kw):
- self.initialized = []
- depserv.DependencyService.__init__(self, **kw)
-
- def setup_ONE(self):
- self.initialized.append('ONE')
-
- def setup_TWO(self):
- self.initialized.append('TWO')
-
- def setup_THREE(self):
- self.initialized.append('THREE')
-
-
-class TestDependencyService(unittest.TestCase):
-
- def test_depends(self):
- class One(Serv):
- def depends_TWO(self):
- return ['three']
-
- class Two(Serv):
- def depends_THREE(self):
- return ['two']
-
- args = dict(one={}, two={}, three={})
-
- one = One(**args)
- self.assert_(one.initialized == ['ONE', 'THREE', 'TWO'])
-
- two = Two(**args)
- self.assert_(two.initialized == ['ONE', 'TWO', 'THREE'])
-
-
- def test_circularDepends(self):
- class One(Serv):
- def depends_THREE(self):
- return ['two']
- def depends_TWO(self):
- return ['three']
- try:
- One(one={}, two={}, three={})
- except depserv.StartupError:
- pass
- else:
- raise unittest.FailTest, 'circular dependencies did not raise an error'
-
-
- def test_requiredWithDependency(self):
- """A service is required but has dependencies"""
-
- class One(Serv):
- def depends_ONE(self):
- return ['three']
- try:
- One(one={}, two={}, three={})
- except depserv.StartupError:
- pass
- else:
- raise unittest.FailTest, 'unsatisfied dependencies did not raise an error'
=== removed file 'Vertex/vertex/test/test_ptcp.py'
--- Vertex/vertex/test/test_ptcp.py 2008-08-11 13:49:13 +0000
+++ Vertex/vertex/test/test_ptcp.py 1970-01-01 00:00:00 +0000
@@ -1,365 +0,0 @@
-# -*- test-case-name: vertex.test.test_ptcp -*-
-
-import random, os
-
-from twisted.internet import reactor, protocol, defer, error
-from twisted.trial import unittest
-
-from vertex import ptcp
-
-def reallyLossy(method):
- r = random.Random()
- r.seed(42)
- def worseMethod(*a, **kw):
- if r.choice([True, True, False]):
- method(*a, **kw)
- return worseMethod
-
-def insufficientTransmitter(method, mtu):
- def worseMethod(bytes, addr):
- method(bytes[:mtu], addr)
- return worseMethod
-
-
-class TestProtocol(protocol.Protocol):
- buffer = None
- def __init__(self):
- self.onConnect = defer.Deferred()
- self.onDisconn = defer.Deferred()
- self._waiting = None
- self.buffer = []
-
- def connectionMade(self):
- self.onConnect.callback(None)
-
- def connectionLost(self, reason):
- self.onDisconn.callback(None)
-
- def gotBytes(self, bytes):
- assert self._waiting is None
- if ''.join(self.buffer) == bytes:
- return defer.succeed(None)
- self._waiting = (defer.Deferred(), bytes)
- return self._waiting[0]
-
- def dataReceived(self, bytes):
- self.buffer.append(bytes)
- if self._waiting is not None:
- bytes = ''.join(self.buffer)
- if not self._waiting[1].startswith(bytes):
- x = len(os.path.commonprefix([bytes, self._waiting[1]]))
- print x
- print 'it goes wrong starting with', repr(bytes[x:x+100]), repr(self._waiting[1][x:x+100])
- if bytes == self._waiting[1]:
- self._waiting[0].callback(None)
- self._waiting = None
-
-class Django(protocol.ClientFactory):
- def __init__(self):
- self.onConnect = defer.Deferred()
-
- def buildProtocol(self, addr):
- p = protocol.ClientFactory.buildProtocol(self, addr)
- self.onConnect.callback(p)
- return p
-
- def clientConnectionFailed(self, conn, err):
- self.onConnect.errback(err)
-
-class ConnectedPTCPMixin:
- serverPort = None
-
- def setUpForATest(self,
- ServerProtocol=TestProtocol, ClientProtocol=TestProtocol):
- serverProto = ServerProtocol()
- clientProto = ClientProtocol()
-
-
- self.serverProto = serverProto
- self.clientProto = clientProto
-
- sf = protocol.ServerFactory()
- sf.protocol = lambda: serverProto
-
- cf = Django()
- cf.protocol = lambda: clientProto
-
- serverTransport = ptcp.PTCP(sf)
- clientTransport = ptcp.PTCP(None)
-
- self.serverTransport = serverTransport
- self.clientTransport = clientTransport
-
- serverPort = reactor.listenUDP(0, serverTransport)
- clientPort = reactor.listenUDP(0, clientTransport)
-
- self.clientPort = clientPort
- self.serverPort = serverPort
-
- return (
- serverProto, clientProto,
- sf, cf,
- serverTransport, clientTransport,
- serverPort, clientPort
- )
-
- def tearDown(self):
- td = []
-
- for ptcp in (self.serverTransport, self.clientTransport):
- td.append(ptcp.waitForAllConnectionsToClose())
- d = defer.DeferredList(td)
- return d
-
-
-class TestProducerProtocol(protocol.Protocol):
- NUM_WRITES = 32
- WRITE_SIZE = 32
-
- def __init__(self):
- self.onConnect = defer.Deferred()
- self.onPaused = defer.Deferred()
-
- def connectionMade(self):
- self.onConnect.callback(None)
- self.count = -1
- self.transport.registerProducer(self, False)
-
- def pauseProducing(self):
- if self.onPaused is not None:
- self.onPaused.callback(None)
- self.onPaused = None
-
- def resumeProducing(self):
- self.count += 1
- if self.count < self.NUM_WRITES:
- bytes = chr(self.count) * self.WRITE_SIZE
- # print 'Issuing a write', len(bytes)
- self.transport.write(bytes)
- if self.count == self.NUM_WRITES - 1:
- # Last time through, intentionally drop the connection before
- # the buffer is empty to ensure we handle this case properly.
- # print 'Disconnecting'
- self.transport.loseConnection()
- else:
- # print 'Unregistering'
- self.transport.unregisterProducer()
-
-class PTCPTransportTestCase(ConnectedPTCPMixin, unittest.TestCase):
- def setUp(self):
- """
- I have no idea why one of these values is divided by 10 and the
- other is multiplied by 10. -exarkun
- """
- self.patch(
- ptcp.PTCPConnection, '_retransmitTimeout',
- ptcp.PTCPConnection._retransmitTimeout / 10)
- self.patch(
- ptcp.PTCPPacket, 'retransmitCount',
- ptcp.PTCPPacket.retransmitCount * 10)
-
-
- def xtestWhoAmI(self):
- (serverProto, clientProto,
- sf, cf,
- serverTransport, clientTransport,
- serverPort, clientPort) = self.setUpForATest()
-
- def gotAddress(results):
- (serverSuccess, serverAddress), (clientSuccess, clientAddress) = results
- self.failUnless(serverSuccess)
- self.failUnless(clientSuccess)
-
- self.assertEquals(serverAddress[1], serverPort.getHost().port)
- self.assertEquals(clientAddress[1], clientPort.getHost().port)
-
- def connectionsMade(ignored):
- return defer.DeferredList([serverProto.transport.whoami(), clientProto.transport.whoami()]).addCallback(gotAddress)
-
- clientConnID = clientTransport.connect(cf, '127.0.0.1', serverPort.getHost().port)
-
- return defer.DeferredList([serverProto.onConnect, clientProto.onConnect]).addCallback(connectionsMade)
-
- #testWhoAmI.skip = 'arglebargle'
-
- def testVerySimpleConnection(self):
- (serverProto, clientProto,
- sf, cf,
- serverTransport, clientTransport,
- serverPort, clientPort) = self.setUpForATest()
-
-
- clientConnID = clientTransport.connect(cf, '127.0.0.1', serverPort.getHost().port)
-
- def sendSomeBytes(ignored, n=10, server=False):
- if n:
- bytes = 'not a lot of bytes' * 1000
- if server:
- serverProto.transport.write(bytes)
- else:
- clientProto.transport.write(bytes)
- if server:
- clientProto.buffer = []
- d = clientProto.gotBytes(bytes)
- else:
- serverProto.buffer = []
- d = serverProto.gotBytes(bytes)
- return d.addCallback(sendSomeBytes, n - 1, not server)
-
- def loseConnections(ignored):
- serverProto.transport.loseConnection()
- clientProto.transport.loseConnection()
- return defer.DeferredList([
- serverProto.onDisconn,
- clientProto.onDisconn
- ])
-
- dl = defer.DeferredList([serverProto.onConnect, clientProto.onConnect])
- dl.addCallback(sendSomeBytes)
- dl.addCallback(loseConnections)
- return dl
-
-
- def testProducerConsumer(self):
- (serverProto, clientProto,
- sf, cf,
- serverTransport, clientTransport,
- serverPort, clientPort) = self.setUpForATest(
- ServerProtocol=TestProducerProtocol)
-
- def disconnected(ignored):
- self.assertEquals(
- ''.join(clientProto.buffer),
- ''.join([chr(n) * serverProto.WRITE_SIZE
- for n in range(serverProto.NUM_WRITES)]))
-
- clientConnID = clientTransport.connect(cf, '127.0.0.1', serverPort.getHost().port)
- return clientProto.onDisconn.addCallback(disconnected)
-
-
- def testTransportProducer(self):
- (serverProto, clientProto,
- sf, cf,
- serverTransport, clientTransport,
- serverPort, clientPort) = self.setUpForATest()
-
- resumed = []
- def resumeProducing():
- resumed.append(True)
- clientProto.transport.resumeProducing()
-
- def cbBytes(ignored):
- self.failUnless(resumed)
- clientProto.transport.loseConnection()
-
- def cbConnect(ignored):
- BYTES = 'Here are bytes'
- clientProto.transport.pauseProducing()
- serverProto.transport.write(BYTES)
- reactor.callLater(2, resumeProducing)
- return clientProto.gotBytes(BYTES).addCallback(cbBytes)
-
-
- clientConnID = clientTransport.connect(cf, '127.0.0.1', serverPort.getHost().port)
- connD = defer.DeferredList([clientProto.onConnect, serverProto.onConnect])
- connD.addCallback(cbConnect)
- return connD
-
- def testTransportProducerProtocolProducer(self):
- (serverProto, clientProto,
- sf, cf,
- serverTransport, clientTransport,
- serverPort, clientPort) = self.setUpForATest(
- ServerProtocol=TestProducerProtocol)
-
- paused = []
- def cbPaused(ignored):
- # print 'Paused'
- paused.append(True)
- # print 'RESUMING', clientProto, clientTransport, clientPort
- clientProto.transport.resumeProducing()
- serverProto.onPaused.addCallback(cbPaused)
-
- def cbBytes(ignored):
- # print 'Disconnected'
- self.assertEquals(
- ''.join(clientProto.buffer),
- ''.join([chr(n) * serverProto.WRITE_SIZE
- for n in range(serverProto.NUM_WRITES)]))
-
- def cbConnect(ignored):
- # The server must write enough to completely fill the outgoing buffer,
- # since our peer isn't ACKing /anything/ and our server waits for
- # writes to be acked before proceeding.
- serverProto.WRITE_SIZE = serverProto.transport.sendWindow * 5
-
- # print 'Connected'
- # print 'PAUSING CLIENT PROTO', clientProto, clientTransport, clientPort
- clientProto.transport.pauseProducing()
- return clientProto.onDisconn.addCallback(cbBytes)
-
- clientConnID = clientTransport.connect(cf, '127.0.0.1', serverPort.getHost().port)
- connD = defer.DeferredList([clientProto.onConnect, serverProto.onConnect])
- connD.addCallback(cbConnect)
- return connD
-
-
-class LossyTransportTestCase(PTCPTransportTestCase):
- def setUpForATest(self, *a, **kw):
- results = PTCPTransportTestCase.setUpForATest(self, *a, **kw)
- results[-2].write = reallyLossy(results[-2].write)
- results[-1].write = reallyLossy(results[-1].write)
- return results
-
-
-class SmallMTUTransportTestCase(PTCPTransportTestCase):
- def setUpForATest(self, *a, **kw):
- results = PTCPTransportTestCase.setUpForATest(self, *a, **kw)
- results[-2].write = insufficientTransmitter(results[-2].write, 128)
- results[-1].write = insufficientTransmitter(results[-1].write, 128)
- return results
-
-
-
-class TimeoutTestCase(ConnectedPTCPMixin, unittest.TestCase):
- def setUp(self):
- """
- Shorten the retransmit timeout so that tests finish more quickly.
- """
- self.patch(
- ptcp.PTCPConnection, '_retransmitTimeout',
- ptcp.PTCPConnection._retransmitTimeout / 10)
-
-
- def testConnectTimeout(self):
- (serverProto, clientProto,
- sf, cf,
- serverTransport, clientTransport,
- serverPort, clientPort) = self.setUpForATest()
-
- clientTransport.sendPacket = lambda *a, **kw: None
- clientConnID = clientTransport.connect(cf, '127.0.0.1', serverPort.getHost().port)
- return cf.onConnect.addBoth(lambda result: result.trap(error.TimeoutError) and None)
-
- def testDataTimeout(self):
- (serverProto, clientProto,
- sf, cf,
- serverTransport, clientTransport,
- serverPort, clientPort) = self.setUpForATest()
-
- def cbConnected(ignored):
- serverProto.transport.ptcp.sendPacket = lambda *a, **kw: None
- clientProto.transport.write('Receive this data.')
- serverProto.transport.write('Send this data.') # have to send data
- # or the server will
- # never time out:
- # need a
- # SO_KEEPALIVE
- # option somewhere
- return clientProto.onDisconn
-
- clientConnID = clientTransport.connect(cf, '127.0.0.1', serverPort.getHost().port)
-
- d = defer.DeferredList([serverProto.onConnect, clientProto.onConnect])
- d.addCallback(cbConnected)
- return d
=== removed file 'Vertex/vertex/test/test_q2q.py'
--- Vertex/vertex/test/test_q2q.py 2012-03-12 18:30:09 +0000
+++ Vertex/vertex/test/test_q2q.py 1970-01-01 00:00:00 +0000
@@ -1,712 +0,0 @@
-# Copyright 2005-2008 Divmod, Inc. See LICENSE file for details
-# -*- vertex.test.test_q2q.UDPConnection -*-
-
-"""
-Tests for L{vertex.q2q}.
-"""
-
-from cStringIO import StringIO
-
-from twisted.trial import unittest
-from twisted.application import service
-from twisted.internet import reactor, protocol, defer
-from twisted.internet.task import deferLater
-from twisted.internet.ssl import DistinguishedName, PrivateCertificate, KeyPair
-from twisted.protocols import basic
-from twisted.python import log
-from twisted.python import failure
-from twisted.internet.error import ConnectionDone
-# from twisted.internet.main import CONNECTION_DONE
-
-from zope.interface import implements
-from twisted.internet.interfaces import IResolverSimple
-
-from twisted.protocols.amp import (
- UnhandledCommand, UnknownRemoteError, QuitBox, Command, AMP)
-
-from vertex import q2q
-
-
-def noResources(*a):
- return []
-
-class FakeConnectTCP:
- implements(IResolverSimple)
-
- def __init__(self, connectTCP):
- self._connectTCP = connectTCP
- self.hostPortToHostPort = {}
- self.hostToLocalHost = {}
- self.counter = 1
-
- def addHostPort(self, hostname, fakePortNumber, realPortNumber):
- if hostname in self.hostToLocalHost:
- localIP = self.hostToLocalHost[hostname]
- else:
- localIP = '127.0.0.%d' % (self.counter,)
- self.counter += 1
- self.hostToLocalHost[hostname] = localIP
-
- self.hostPortToHostPort[(localIP, fakePortNumber)] = (localIP, realPortNumber)
- self.hostPortToHostPort[(hostname, fakePortNumber)] = (hostname, realPortNumber)
-
- def connectTCP(self, host, port, *args, **kw):
- localhost, localport = self.hostPortToHostPort.get((host,port), (host, port))
- return self._connectTCP(localhost, localport, *args, **kw)
-
- def getHostSync(self,name):
- result = self.hostToLocalHost[name]
- return result
-
- def getHostByName(self, name, timeout):
- return defer.maybeDeferred(self.getHostSync, name)
-
-def runOneDeferred(d):
- L = []
- d.addBoth(L.append)
- reactor.callLater(0, d.addCallback, lambda ign: reactor.crash())
- reactor.run()
- if L:
- if isinstance(L[0], failure.Failure):
- L[0].trap()
- return L[0]
- raise unittest.FailTest("Keyboard Interrupt")
-
-class Utility(unittest.TestCase):
- def testServiceInitialization(self):
- svc = q2q.Q2QService(noResources)
- svc.certificateStorage.addPrivateCertificate("test.domain")
-
- cert = svc.certificateStorage.getPrivateCertificate("test.domain")
- self.failUnless(cert.getPublicKey().matches(cert.privateKey))
-
-class OneTrickPony(AMP):
- def amp_TRICK(self, box):
- return QuitBox(tricked='True')
-
-class OneTrickPonyServerFactory(protocol.ServerFactory):
- protocol = OneTrickPony
-
-class OneTrickPonyClient(AMP):
- def connectionMade(self):
- self.callRemoteString('trick').chainDeferred(self.factory.ponged)
-
-class OneTrickPonyClientFactory(protocol.ClientFactory):
- protocol = OneTrickPonyClient
-
- def __init__(self, ponged):
- self.ponged = ponged
-
- def buildProtocol(self, addr):
- result = protocol.ClientFactory.buildProtocol(self, addr)
- self.proto = result
- return result
-
- def clientConnectionFailed(self, connector, reason):
- self.ponged.errback(reason)
-
-
-class DataEater(protocol.Protocol):
- def __init__(self):
- self.waiters = []
- self.data = []
- self.count = 0
-
- def dataReceived(self, data):
- if not data:
- raise RuntimeError("Empty string delivered to DataEater")
- self.data.append(data)
- self.count += len(data)
- for count, waiter in self.waiters[:]:
- if self.count >= count:
- waiter.callback(self.count)
-
- def removeD(self, result, size, d):
- # XXX done as a callback because 1.3 util.wait actually calls a
- # callback on the deferred
- self.waiters.remove((size, d))
- return result
-
- def waitForCount(self, size):
- D = defer.Deferred()
- self.waiters.append((size, D))
- self.waiters.sort()
- self.waiters.reverse()
- return D.addBoth(self.removeD, size, D)
-
- def buildProtocol(self, addr):
- return self
-
-class DataFeeder(protocol.Protocol):
- def __init__(self, fobj):
- self.fobj = fobj
-
- def clientConnectionFailed(self, connector, reason):
- log.msg("DataFeeder client connection failed:")
- log.err(reason)
-
- def clientConnectionLost(self, connector, reason):
- pass
-
- def connectionMade(self):
- basic.FileSender().beginFileTransfer(self.fobj, self.transport)
-
- def buildProtocol(self, addr):
- return self
-
-class StreamingDataFeeder(protocol.Protocol):
- DELAY = 0.01
- CHUNK = 1024
- paused = False
- pauseCount = 0
- resumeCount = 0
- stopCount = 0
- outCount = 0
- call = None
-
- def __init__(self, infile):
- self.file = infile
-
- def clientConnectionFailed(sef, connector, reason):
- log.msg("StreamingDataFeeder client connection failed:")
- log.err(reason)
-
- def clientConnectionLost(self, connector, reason):
- pass
-
- def connectionMade(self):
- self.nextChunk = self.file.read(self.CHUNK)
- self.transport.registerProducer(self, True)
- self.call = reactor.callLater(self.DELAY, self._keepGoing)
-
- def _keepGoing(self):
- self.call = None
- if self.paused:
- return
- chunk = self.nextChunk
- self.nextChunk = self.file.read(self.CHUNK)
- self.outCount += len(chunk)
- if chunk:
- self.transport.write(chunk)
- if self.nextChunk:
- self.call = reactor.callLater(self.DELAY, self._keepGoing)
-
-
- def pauseProducing(self):
- self.paused = True
- self.pauseCount += 1
- if self.call is not None:
- self.cancelMe()
-
- def resumeProducing(self):
- self.paused = False
- if self.call is not None:
- self.cancelMe()
- self.call = reactor.callLater(self.DELAY, self._keepGoing)
- self.resumeCount += 1
-
- def cancelMe(self):
- self.call.cancel()
- self.call = None
-
- def stopProducing(self):
- self.paused = True
- self.stopCount += 1
- if self.call is not None:
- self.cancelMe()
-
- def buildProtocol(self, addr):
- return self
-
-
-class ErroneousClientError(Exception):
- pass
-
-class EngenderError(Command):
- commandName = 'Engender-Error'
-
-class Break(Command):
- commandName = 'Break'
-
-class Flag(Command):
- commandName = 'Flag'
-
-class FatalError(Exception):
- pass
-
-class Fatal(Command):
- fatalErrors = {FatalError: "quite bad"}
-
-class Erroneous(AMP):
- def _fatal(self):
- raise FatalError("This is fatal.")
- Fatal.responder(_fatal)
-
- flag = False
- def _break(self):
- raise ErroneousClientError("Zoop")
- Break.responder(_break)
-
- def _engenderError(self):
- def ebBroken(err):
- err.trap(ConnectionDone)
- # This connection is dead. Avoid having an error logged by turning
- # this into success; the result can't possibly get to the other
- # side, anyway. -exarkun
- return {}
- return self.callRemote(Break).addErrback(ebBroken)
- EngenderError.responder(_engenderError)
-
- def _flag(self):
- self.flag = True
- return {}
- Flag.responder(_flag)
-
-class ErroneousServerFactory(protocol.ServerFactory):
- protocol = Erroneous
-
-class ErroneousClientFactory(protocol.ClientFactory):
- protocol = Erroneous
-
-class Greet(Command):
- commandName = 'Greet'
-
-class Greeter(AMP, protocol.ServerFactory, protocol.ClientFactory):
- def __init__(self, isServer, startupD):
- self.isServer = isServer
- AMP.__init__(self)
- self.startupD = startupD
-
- def buildProtocol(self, addr):
- return self
-
- def connectionMade(self):
- self.callRemote(Greet).chainDeferred(self.startupD)
-
- def _greet(self):
- self.greeted = True
- return dict()
- Greet.responder(_greet)
-
-class Q2QConnectionTestCase(unittest.TestCase):
- streamer = None
-
- fromResource = 'clientResource'
- toResource = 'serverResource'
-
- fromDomain = 'origin.domain.example.com'
- fromIP = '127.0.0.1'
- spoofedDomain = 'spoofed.domain.example.com'
- toDomain = 'destination.domain.example.org'
- toIP = '127.0.0.2'
-
- userReverseDNS = 'i.watch.too.much.tv'
- inboundTCPPortnum = 0
- udpEnabled = False
- virtualEnabled = False
-
- def _makeQ2QService(self, certificateEntity, publicIP, pff=None):
- svc = q2q.Q2QService(pff, q2qPortnum=0,
- inboundTCPPortnum=self.inboundTCPPortnum,
- publicIP=publicIP)
- svc.udpEnabled = self.udpEnabled
- svc.virtualEnabled = self.virtualEnabled
- if '@' not in certificateEntity:
- svc.certificateStorage.addPrivateCertificate(certificateEntity)
- svc.debugName = certificateEntity
- return svc
-
-
- def _addQ2QProtocol(self, name, factory):
- resourceKey = (self.fromAddress,
- self.toAddress, name)
- self.resourceMap[resourceKey] = factory
-
- def protocolFactoryLookup(self, *key):
- if key in self.resourceMap:
- return [(self.resourceMap[key], 'test-description')]
- return []
-
-
- def setUp(self):
- self.fromAddress = q2q.Q2QAddress(self.fromDomain, self.fromResource)
- self.toAddress = q2q.Q2QAddress(self.toDomain, self.toResource)
-
- # A mapping of host names to port numbers Our connectTCP will always
- # connect to 127.0.0.1 and on a port which is a value in this
- # dictionary.
- fakeDNS = FakeConnectTCP(reactor.connectTCP)
- reactor.connectTCP = fakeDNS.connectTCP
-
- # ALSO WE MUST DO OTHER SIMILAR THINGS
- self._oldResolver = reactor.resolver
- reactor.installResolver(fakeDNS)
-
- # Set up a know-nothing service object for the client half of the
- # conversation.
- self.serverService2 = self._makeQ2QService(self.fromDomain, self.fromIP, noResources)
-
- # Do likewise for the server half of the conversation. Also, allow
- # test methods to set up some trivial resources which we can attempt to
- # access from the client.
- self.resourceMap = {}
- self.serverService = self._makeQ2QService(self.toDomain, self.toIP,
- self.protocolFactoryLookup)
-
- self.msvc = service.MultiService()
- self.serverService2.setServiceParent(self.msvc)
- self.serverService.setServiceParent(self.msvc)
-
- # Let the kernel allocate a random port for each of these service's listeners
- self.msvc.startService()
-
- fakeDNS.addHostPort(
- self.fromDomain, 8788,
- self.serverService2.q2qPort.getHost().port)
-
- fakeDNS.addHostPort(
- self.toDomain, 8788,
- self.serverService.q2qPort.getHost().port)
-
- self._addQ2QProtocol('pony', OneTrickPonyServerFactory())
-
- self.dataEater = DataEater()
- self._addQ2QProtocol('eat', self.dataEater)
-
- self._addQ2QProtocol('error', ErroneousServerFactory())
-
- def tearDown(self):
- reactor.installResolver(self._oldResolver)
- del reactor.connectTCP
- return self.msvc.stopService()
-
-
-
-class ConnectionTestMixin:
-
- def testConnectWithIntroduction(self):
- ponged = defer.Deferred()
- self.serverService2.connectQ2Q(self.fromAddress,
- self.toAddress,
- 'pony',
- OneTrickPonyClientFactory(ponged))
- return ponged.addCallback(lambda answerBox: self.failUnless('tricked' in answerBox))
-
- def addClientService(self, toAddress, secret, serverService):
- return self._addClientService(
- toAddress.resource, secret, serverService, toAddress.domain)
-
- def _addClientService(self, username,
- privateSecret, serverService,
- serverDomain):
- svc = self._makeQ2QService(username + '@' + serverDomain, None)
- serverService.certificateStorage.addUser(serverDomain,
- username,
- privateSecret)
- svc.setServiceParent(self.msvc)
- return svc.authorize(q2q.Q2QAddress(serverDomain, username),
- privateSecret).addCallback(lambda x: svc)
-
-
- def testListening(self):
- _1 = self.addClientService(self.toAddress, 'aaaa', self.serverService)
- def _1c(_1result):
- self.clientServerService = _1result
- ponyFactory = OneTrickPonyServerFactory()
- _2 = self.clientServerService.listenQ2Q(self.toAddress,
- {'pony2': ponyFactory},
- 'ponies suck')
-
- def _2c(ignored):
- _3 = self.addClientService(
- self.fromAddress, 'bbbb', self.serverService2)
- def _3c(_3result):
- self.clientClientService = _3result
-
- _4 = defer.Deferred()
- otpcf = OneTrickPonyClientFactory(_4)
- self.clientClientService.connectQ2Q(self.fromAddress,
- self.toAddress,
- 'pony2',
- otpcf)
- def _4c(answerBox):
- T = otpcf.proto.transport
- self.assertEquals(T.getQ2QPeer(), self.toAddress)
- self.assertEquals(T.getQ2QHost(), self.fromAddress)
- self.failUnless('tricked' in answerBox)
-
- return _4.addCallback(_4c)
- return _3.addCallback(_3c)
- return _2.addCallback(_2c)
- return _1.addCallback(_1c)
-
- def testChooserGetsThreeChoices(self):
-
- def actualTest(ign):
- ponyFactory = OneTrickPonyServerFactory()
- _1 = self.addClientService(
- self.toAddress, 'aaaa', self.serverService)
- def _1c(_1result):
- self.clientServerService2 = _1result
- # print 'ultra frack'
-
- _2 = self.clientServerService2.listenQ2Q(self.toAddress,
- {'pony': ponyFactory},
- 'ponies are weird')
- def _2c(ign):
- _3 = self.clientServerService.listenQ2Q(self.toAddress,
- {'pony': ponyFactory},
- 'ponies rule')
- def _3c(ign):
- expectedList = ['ponies rule', 'ponies are weird', 'test-description']
- def chooser(servers):
- self.failUnlessEqual(len(servers), 3)
- for server in servers:
- expectedList.remove(server['description'])
- if server['description'] == 'ponies rule':
- self.assertEquals(
- self.clientServerService.certificateStorage.getPrivateCertificate(str(self.toAddress)),
- server['certificate'])
- yield server
-
- factory = protocol.ClientFactory()
- factory.protocol = AMP
- _4 = self.clientClientService.connectQ2Q(
- self.fromAddress,
- self.toAddress,
- 'pony',
- factory,
- chooser=chooser)
- def _4c(ign):
- self.failUnlessEqual(expectedList, [])
- return _4.addCallback(_4c)
- return _3.addCallback(_3c)
- return _2.addCallback(_2c)
- return _1.addCallback(_1c)
- return self.testListening().addCallback(actualTest)
-
- # print 'dang yo'
-
-
- def testTwoGreetings(self):
- d1 = defer.Deferred()
- d2 = defer.Deferred()
- client = Greeter(False, d1)
- server = Greeter(True, d2)
- self._addQ2QProtocol('greet', server)
- self.serverService2.connectQ2Q(self.fromAddress,
- self.toAddress,
- 'greet',
- client)
- def _(x):
- self.failUnless(client.greeted)
- self.failUnless(server.greeted)
- return defer.DeferredList([d1, d2]).addCallback(_)
-
-
- def testSendingFiles(self):
- SIZE = 1024 * 500
- self.streamer = StreamingDataFeeder(StringIO('x' * SIZE))
- self.streamer.CHUNK = 8192
- a = self.serverService2.connectQ2Q(self.fromAddress,
- self.toAddress, 'eat',
- DataFeeder(StringIO('y' * SIZE)))
- b = self.serverService2.connectQ2Q(self.fromAddress,
- self.toAddress, 'eat',
- self.streamer)
-
- def dotest(ign):
- # self.assertEquals( len(self.serverService.liveConnections), 1)
- # XXX currently there are 2 connections but there should only be 1: the
- # connection cache is busted, need a separate test for that
- for liveConnection in self.serverService.iterconnections():
- liveConnection.transport.pauseProducing()
- wfc = self.dataEater.waitForCount(SIZE * 2)
- resumed = [False]
- def shouldntHappen(x):
- if resumed[0]:
- return x
- else:
- self.fail("wfc fired with: " + repr(x))
- wfc.addBoth(shouldntHappen)
- def keepGoing(ign):
- resumed[0] = True
- for liveConnection in self.serverService.iterconnections():
- liveConnection.transport.resumeProducing()
- def assertSomeStuff(ign):
- self.failUnless(self.streamer.pauseCount > 0)
- self.failUnless(self.streamer.resumeCount > 0)
- return self.dataEater.waitForCount(SIZE * 2).addCallback(assertSomeStuff)
- return deferLater(reactor, 3, lambda: None).addCallback(keepGoing)
- return defer.DeferredList([a, b]).addCallback(dotest)
- testSendingFiles.skip = "hangs forever"
-
- def testBadIssuerOnSelfSignedCert(self):
- x = self.testConnectWithIntroduction()
- def actualTest(result):
- ponged = defer.Deferred()
- signer = self.serverService2.certificateStorage.getPrivateCertificate(
- self.fromDomain).privateKey
- req = signer.requestObject(DistinguishedName(commonName=self.toDomain))
- sreq = signer.signRequestObject(
- DistinguishedName(commonName=self.fromDomain), req, 12345)
- selfSignedLie = PrivateCertificate.fromCertificateAndKeyPair(
- sreq, signer)
- self.serverService2.connectQ2Q(self.fromAddress,
- self.toAddress,
- 'pony',
- OneTrickPonyClientFactory(ponged),
- selfSignedLie,
- fakeFromDomain=self.toDomain).addErrback(
- lambda e: e.trap(q2q.VerifyError))
-
- return self.assertFailure(ponged, q2q.VerifyError)
- return x.addCallback(actualTest)
-
-
- def testBadCertRequestSubject(self):
- kp = KeyPair.generate()
- subject = DistinguishedName(commonName='HACKERX',
- localityName='INTERNETANIA')
- reqobj = kp.requestObject(subject)
-
- fakereq = kp.requestObject(subject)
- ssigned = kp.signRequestObject(subject, fakereq, 1)
- certpair = PrivateCertificate.fromCertificateAndKeyPair
- fakecert = certpair(ssigned, kp)
- apc = self.serverService2.certificateStorage.addPrivateCertificate
-
- def _2(secured):
- D = secured.callRemote(
- q2q.Sign,
- certificate_request=reqobj,
- password='itdoesntmatter')
- def _1(dcert):
- cert = dcert['certificate']
- privcert = certpair(cert, kp)
- apc(str(self.fromAddress), privcert)
- return D.addCallback(_1)
-
- d = self.serverService2.getSecureConnection(
- self.fromAddress, self.fromAddress.domainAddress(), authorize=False,
- usePrivateCertificate=fakecert,
- ).addCallback(_2)
-
- def unexpectedSuccess(result):
- self.fail("Expected BadCertificateRequest, got %r" % (result,))
- def expectedFailure(err):
- err.trap(q2q.BadCertificateRequest)
- d.addCallbacks(unexpectedSuccess, expectedFailure)
- return d
-
- def testClientSideUnhandledException(self):
- d = self.serverService2.connectQ2Q(
- self.fromAddress, self.toAddress, 'error',
- ErroneousClientFactory())
- def connected(proto):
- return proto.callRemote(EngenderError)
- d.addCallback(connected)
- # The unhandled, undeclared error causes the connection to be closed
- # from the other side.
- d = self.assertFailure(d, ConnectionDone, UnknownRemoteError)
- def cbDisconnected(err):
- self.assertEqual(
- len(self.flushLoggedErrors(ErroneousClientError)),
- 1)
- d.addCallback(cbDisconnected)
- return d
-
- def successIsFailure(self, success):
- self.fail()
-
- def testTwoBadWrites(self):
- d = self.serverService2.connectQ2Q(
- self.fromAddress, self.toAddress, 'error',
- ErroneousClientFactory())
-
- def connected(proto):
- d1 = self.assertFailure(proto.callRemote(Fatal), FatalError)
- def noMoreCalls(_):
- self.assertFailure(proto.callRemote(Flag),
- ConnectionDone)
- d1.addCallback(noMoreCalls)
- return d1
- d.addCallback(connected)
- return d
-
-
-
-
-class VirtualConnection(Q2QConnectionTestCase, ConnectionTestMixin):
- inboundTCPPortnum = None
- udpEnabled = False
- virtualEnabled = True
-
- def testListening(self):
- pass
-
- def testChooserGetsThreeChoices(self):
- pass
-
- testListening.skip = 'virtual port forwarding not implemented'
- testChooserGetsThreeChoices.skip = 'cant do this without testListening'
-
-class UDPConnection(Q2QConnectionTestCase, ConnectionTestMixin):
- # skip = 'yep'
- inboundTCPPortnum = None
- udpEnabled = True
- virtualEnabled = False
-
-class TCPConnection(Q2QConnectionTestCase, ConnectionTestMixin):
- inboundTCPPortnum = 0
- udpEnabled = False
- virtualEnabled = False
-
-# class LiveServerMixin:
-# serverDomain = 'test.domain.example.com'
-
-# def jackDNS(self, *info):
-# self.fakeDNS = FakeConnectTCP(reactor.connectTCP)
-# reactor.connectTCP = self.fakeDNS.connectTCP
-# self._oldResolver = reactor.resolver
-# reactor.installResolver(self.fakeDNS)
-
-# for (hostname, oldport, newport) in info:
-# self.fakeDNS.addHostPort(hostname, oldport, newport)
-
-# def unjackDNS(self):
-# del reactor.connectTCP
-# reactor.installResolver(self._oldResolver)
-
-# class AuthorizeTestCase(unittest.TestCase, LiveServerMixin):
-# authUser = 'authtestuser'
-# authPass = 'p4ssw0rd'
-
-# def setUp(self):
-# self.testDir, serverService = self.deploy()
-# self.serverService = service.IServiceCollection(serverService)
-# self.serverService.startService()
-# self.clientDir = os.path.join(self.testDir, 'client')
-# self.clientService = q2qclient.ClientQ2QService(self.clientDir)
-
-# q2qPort = self.getQ2QService().q2qPort.getHost().port
-# self.jackDNS((self.serverDomain, 8788, q2qPort))
-
-# def tearDown(self):
-# self.unjackDNS()
-# util.wait(self.serverService.stopService())
-# util.wait(self.clientService.stopService())
-
-# def testAuthorize(self):
-# self.createUser(self.authUser, self.authPass)
-
-# d = self.clientService.authorize(
-# q2q.Q2QAddress(self.serverDomain, self.authUser),
-# self.authPass)
-
-# result = runOneDeferred(d)
-
-# self.failUnless(os.path.exists(os.path.join(self.clientDir, 'public')))
-# self.failUnless(os.path.exists(os.path.join(self.clientDir, 'public', self.serverDomain + '.pem')))
-# self.failUnless(os.path.exists(os.path.join(self.clientDir, 'private')))
-# self.failUnless(os.path.exists(os.path.join(self.clientDir, 'private', self.authUser + '@' + self.serverDomain + '.pem')))
=== removed file 'Vertex/vertex/test/test_sigma.py'
--- Vertex/vertex/test/test_sigma.py 2012-03-14 16:23:22 +0000
+++ Vertex/vertex/test/test_sigma.py 1970-01-01 00:00:00 +0000
@@ -1,210 +0,0 @@
-# Copyright 2005 Divmod, Inc. See LICENSE file for details
-
-from cStringIO import StringIO
-
-from twisted.internet.protocol import FileWrapper
-from twisted.internet import defer
-from twisted.python.failure import Failure
-from twisted.python.filepath import FilePath
-
-from twisted.trial import unittest
-
-from twisted.test.iosim import connectedServerAndClient, FakeTransport
-
-from vertex.q2q import Q2QAddress
-from vertex import sigma
-
-from vertex.test.mock_data import data as TEST_DATA
-
-class FakeQ2QTransport(FakeTransport):
-
- def __init__(self, q2qhost, q2qpeer):
- FakeTransport.__init__(self)
- self.q2qhost = q2qhost
- self.q2qpeer = q2qpeer
-
- def getQ2QPeer(self):
- return self.q2qpeer
-
- def getQ2QHost(self):
- return self.q2qhost
-
-class FakeDelayedCall:
- def __init__(self, fqs, tup):
- self.fqs = fqs
- self.tup = tup
-
- def cancel(self):
- self.fqs.calls.remove(self.tup)
-
-class FakeQ2QService:
- # XXX TODO: move this into test_q2q and make sure that all the q2q tests
- # run with it in order to verify that the test harness is not broken.
-
- def __init__(self):
- self.listeners = {} # map listening {(q2qid, protocol name):(protocol
- # factory, protocol description)}
- self.pumps = [] # a list of IOPumps that we have to flush
- self.calls = []
- self.time = 0
-
- def callLater(self, s, f, *a, **k):
- # XXX TODO: return canceller
- assert f is not None
- tup = (self.time + s, f, a, k)
- self.calls.append(tup)
- self.calls.sort()
- return FakeDelayedCall(self, tup)
-
- def flush(self, debug=False):
- result = True
- while result:
- self.time += 1
- result = False
- for x in range(2):
- # run twice so that timed functions can interact with I/O
- for pump in self.pumps:
- if pump.flush(debug):
- result = True
- if debug:
- print 'iteration finished. continuing?', result
- c = self.calls
- self.calls = []
- for s, f, a, k in c:
- if debug:
- print 'timed event', s, f, a, k
- f(*a,**k)
- return result
-
- def listenQ2Q(self, fromAddress, protocolsToFactories, serverDescription):
- for pname, pfact in protocolsToFactories.items():
- self.listeners[fromAddress, pname] = pfact, serverDescription
- return defer.succeed(None)
-
- def connectQ2Q(self, fromAddress, toAddress,
- protocolName, protocolFactory,
- chooser=lambda x: x and [x[0]]):
- # XXX update this when q2q is updated to return a connector rather than
- # a Deferred.
-
- # XXX this isn't really dealing with the multiple-connectors use case
- # now. sigma doesn't need this functionality, but we will need to
- # update this class to do it properly before using it to test other Q2Q
- # code.
-
- listener, description = self.listeners.get((toAddress, protocolName))
- if listener is None:
- print 'void listener', fromAddress, toAddress, self.listeners, self.listener
- reason = Failure(KeyError())
- protocolFactory.clientConnectionFailed(None, reason)
- return defer.fail(reason)
- else:
- def makeFakeClient(c):
- ft = FakeQ2QTransport(fromAddress, toAddress)
- ft.isServer = False
- ft.protocol = c
- return ft
-
- def makeFakeServer(s):
- ft = FakeQ2QTransport(toAddress, fromAddress)
- ft.isServer = True
- ft.protocol = s
- return ft
-
- client, server, pump = connectedServerAndClient(
- lambda: listener.buildProtocol(fromAddress),
- lambda: protocolFactory.buildProtocol(toAddress),
- makeFakeClient,
- makeFakeServer)
- self.pumps.append(pump)
-
- return defer.succeed(client)
-
-
-sender = Q2QAddress("sending-data.net", "sender")
-receiver = Q2QAddress("receiving-data.org", "receiver")
-
-class TestBase(unittest.TestCase):
- def setUp(self):
- self.realChunkSize = sigma.CHUNK_SIZE
- sigma.CHUNK_SIZE = 100
- svc = self.service = FakeQ2QService()
- fname = self.mktemp()
-
- sf = self.sfile = FilePath(fname)
- if not sf.parent().isdir():
- sf.parent().makedirs()
- sf.open('w').write(TEST_DATA)
- self.senderNexus = sigma.Nexus(svc, sender,
- sigma.BaseNexusUI(self.mktemp()),
- svc.callLater)
-
- def tearDown(self):
- self.senderNexus.stopService()
- sigma.CHUNK_SIZE = self.realChunkSize
-
-
-class BasicTransferTest(TestBase):
- def setUp(self):
- TestBase.setUp(self)
- self.stoppers = []
- self.receiverNexus = sigma.Nexus(self.service, receiver,
- sigma.BaseNexusUI(self.mktemp()),
- self.service.callLater)
- self.stoppers.append(self.receiverNexus)
-
-
- def tearDown(self):
- TestBase.tearDown(self)
- for stopper in self.stoppers:
- stopper.stopService()
-
-
- def testOneSenderOneRecipient(self):
- self.senderNexus.push(self.sfile, 'TESTtoTEST', [receiver])
- self.service.flush()
- peerThingyoes = childrenOf(self.receiverNexus.ui.basepath)
- self.assertEquals(len(peerThingyoes), 1)
- rfiles = childrenOf(peerThingyoes[0])
- self.assertEquals(len(rfiles), 1)
- rfile = rfiles[0]
- rfdata = rfile.open().read()
- self.assertEquals(len(rfdata),
- len(TEST_DATA))
- self.assertEquals(rfdata, TEST_DATA,
- "file values unequal")
-
- def testOneSenderManyRecipients(self):
- raddresses = [Q2QAddress("receiving-data.org", "receiver%d" % (x,))
- for x in range(10)]
-
- nexi = [sigma.Nexus(self.service,
- radr,
- sigma.BaseNexusUI(self.mktemp()),
- self.service.callLater) for radr in raddresses]
-
- self.stoppers.extend(nexi)
-
- self.senderNexus.push(self.sfile, 'TESTtoTEST', raddresses)
- self.service.flush()
-
- receivedIntroductions = 0
-
- for nexium in nexi:
- receivedIntroductions += nexium.ui.receivedIntroductions
- self.failUnless(receivedIntroductions > 1)
-
- for nexium in nexi:
- peerFiles = childrenOf(nexium.ui.basepath)
- self.assertEquals(len(peerFiles), 1)
- rfiles = childrenOf(peerFiles[0])
- self.assertEquals(len(rfiles), 1, rfiles)
- rfile = rfiles[0]
- self.assertEquals(rfile.open().read(),
- TEST_DATA,
- "file value mismatch")
-
-
-def childrenOf(x):
- # this should be a part of FilePath, but hey
- return map(x.child, x.listdir())
=== removed file 'Vertex/vertex/test/test_statemachine.py'
--- Vertex/vertex/test/test_statemachine.py 2005-08-05 03:13:02 +0000
+++ Vertex/vertex/test/test_statemachine.py 1970-01-01 00:00:00 +0000
@@ -1,67 +0,0 @@
-
-from twisted.trial import unittest
-
-from vertex import statemachine
-
-S_1, S_2 = 'S_1', 'S_2'
-I_1, I_2 = 'I_1', 'I_2'
-O_1, O_2 = 'O_1', 'O_2'
-
-class TestMachine(statemachine.StateMachine):
- states = {
- S_1: {
- I_1: (O_1, S_1),
- I_2: (O_1, S_2),
- },
- S_2: {
- I_1: (O_2, S_2),
- I_2: (O_1, S_2),
- },
- }
-
- initialState = S_1
-
- def _event(self, name):
- def method():
- self.events.append(name)
- return method
-
- def __getattr__(self, name):
- if (name.startswith('exit_') or name.startswith('enter_') or
- name.startswith('transition_') or name.startswith('output_')):
- return self._event(name)
- raise AttributeError(name)
-
-class Transitions(unittest.TestCase):
- def testOutput(self):
- m = TestMachine()
-
- self.assertEquals(m.state, S_1)
-
- m.events = []
- m.input(I_1)
- self.assertEquals(
- m.events,
- ['output_O_1'])
- self.assertEquals(m.state, S_1)
-
- m.events = []
- m.input(I_2)
- self.assertEquals(
- m.events,
- ['exit_S_1', 'enter_S_2', 'transition_S_1_to_S_2', 'output_O_1'])
- self.assertEquals(m.state, S_2)
-
- m.events = []
- m.input(I_1)
- self.assertEquals(
- m.events,
- ['output_O_2'])
- self.assertEquals(m.state, S_2)
-
- m.events = []
- m.input(I_2)
- self.assertEquals(
- m.events,
- ['output_O_1'])
- self.assertEquals(m.state, S_2)
=== removed file 'Vertex/vertex/test/test_subproducer.py'
--- Vertex/vertex/test/test_subproducer.py 2005-08-10 18:51:37 +0000
+++ Vertex/vertex/test/test_subproducer.py 1970-01-01 00:00:00 +0000
@@ -1,173 +0,0 @@
-
-from twisted.trial import unittest
-
-from vertex.subproducer import SuperProducer, SubProducer
-
-class TestSuper(SuperProducer):
- transport = property(lambda self: self)
-
- def registerProducer(self, producer, streaming):
- self.producer = producer
- self.streamingProducer = streaming
-
- def unregisterProducer(self):
- self.producer = None
-
-class TestProducer:
- def __init__(self):
- self.calls = []
-
- def resumeProducing(self):
- self.calls.append('resume')
-
- def pauseProducing(self):
- self.calls.append('pause')
-
- def stopProducing(self):
- self.calls.append('stop')
-
- def clear(self):
- del self.calls[:]
-
-
-class SuperProducerTest(unittest.TestCase):
-
- def testBasicNotification(self):
- sup = TestSuper()
- sub = SubProducer(sup)
-
- tp1 = TestProducer()
- sub.registerProducer(tp1, False)
- self.assertEquals(tp1.calls, ['resume'])
- sub.unregisterProducer()
-
- tp2 = TestProducer()
- sub.registerProducer(tp2, True)
- self.assertEquals(tp2.calls, [])
- sub.unregisterProducer()
-
- def testPauseSuperBeforeRegister(self):
- sup = TestSuper()
- sub1 = SubProducer(sup)
- sub2 = SubProducer(sup)
-
- tp1 = TestProducer()
- tp2 = TestProducer()
-
- sub1.registerProducer(tp1, False)
- sub2.registerProducer(tp2, False)
-
- self.assertEquals(sup.producer, sup) # Make sure it's registered with
- # itself; IOW it has called
- # self.transport.registerProducer(self).
-
- sup.pauseProducing()
- sup.resumeProducing()
-
- self.assertEquals(tp1.calls, ['resume', 'pause', 'resume'])
- self.assertEquals(tp2.calls, ['resume', 'pause', 'resume'])
-
- sup.stopProducing()
- self.assertEquals(tp1.calls, ['resume', 'pause', 'resume', 'stop'])
- self.assertEquals(tp2.calls, ['resume', 'pause', 'resume', 'stop'])
-
-
- def testNonStreamingChoke(self):
- sup = TestSuper()
- sub1 = SubProducer(sup)
- sub2 = SubProducer(sup)
-
- tp1 = TestProducer()
- tp2 = TestProducer()
-
- sub1.registerProducer(tp1, False)
- sub2.registerProducer(tp2, False)
-
- self.assertEquals(tp1.calls, ['resume'])
- self.assertEquals(tp2.calls, ['resume'])
-
- tp1.clear()
- tp2.clear()
-
- self.assertEquals(sup.producer, sup)
-
- sub1.choke()
- self.assertEquals(tp1.calls, ['pause'])
- self.assertEquals(tp2.calls, [])
-
- sup.pauseProducing()
- self.assertEquals(tp1.calls, ['pause'])
- self.assertEquals(tp2.calls, ['pause'])
-
- sup.resumeProducing()
- self.assertEquals(tp1.calls, ['pause'])
- self.assertEquals(tp2.calls, ['pause', 'resume'])
-
- sup.pauseProducing()
- sup.resumeProducing()
- self.assertEquals(tp1.calls, ['pause'])
- self.assertEquals(tp2.calls, ['pause', 'resume', 'pause', 'resume'])
- sub1.unchoke()
-
- self.assertEquals(tp1.calls, ['pause', 'resume'])
- self.assertEquals(tp2.calls, ['pause', 'resume', 'pause', 'resume'])
-
- sup.pauseProducing()
- sub1.choke()
- sub1.choke()
- sub1.choke()
- self.assertEquals(tp1.calls, ['pause', 'resume', 'pause'])
- self.assertEquals(tp2.calls, ['pause', 'resume', 'pause', 'resume',
- 'pause'])
-
- sub1.unchoke()
- self.assertEquals(tp1.calls, ['pause', 'resume', 'pause'])
- self.assertEquals(tp2.calls, ['pause', 'resume', 'pause', 'resume',
- 'pause'])
-
- sup.resumeProducing()
- self.assertEquals(tp1.calls, ['pause', 'resume', 'pause', 'resume'])
- self.assertEquals(tp2.calls, ['pause', 'resume', 'pause', 'resume',
- 'pause', 'resume'])
- tp1.clear()
- tp2.clear()
- sup.stopProducing()
-
- self.assertEquals(tp1.calls, ['stop'])
- self.assertEquals(tp2.calls, ['stop'])
-
- def testStreamingChoke(self):
- sup = TestSuper()
- sub1 = SubProducer(sup)
- sub2 = SubProducer(sup)
-
- tp1 = TestProducer()
- tp2 = TestProducer()
-
- sub1.registerProducer(tp1, True)
- sub2.registerProducer(tp2, True)
-
- self.assertEquals(tp1.calls, [])
- self.assertEquals(tp2.calls, [])
-
- sub1.choke()
- self.assertEquals(tp1.calls, ['pause'])
- self.assertEquals(tp2.calls, [])
-
- sup.pauseProducing()
- self.assertEquals(tp1.calls, ['pause'])
- self.assertEquals(tp2.calls, ['pause'])
-
- sup.resumeProducing()
- self.assertEquals(tp1.calls, ['pause'])
- self.assertEquals(tp2.calls, ['pause', 'resume'])
-
- sub1.unchoke()
- self.assertEquals(tp1.calls, ['pause', 'resume'])
- self.assertEquals(tp2.calls, ['pause', 'resume'])
-
- tp1.clear()
- tp2.clear()
- sup.stopProducing()
- self.assertEquals(tp1.calls, ['stop'])
- self.assertEquals(tp2.calls, ['stop'])
Follow ups