Initial import to Tizen
[profile/ivi/python-twisted.git] / twisted / protocols / amp.py
1 # -*- test-case-name: twisted.test.test_amp -*-
2 # Copyright (c) 2005 Divmod, Inc.
3 # Copyright (c) Twisted Matrix Laboratories.
4 # See LICENSE for details.
5
6 """
7 This module implements AMP, the Asynchronous Messaging Protocol.
8
9 AMP is a protocol for sending multiple asynchronous request/response pairs over
10 the same connection.  Requests and responses are both collections of key/value
11 pairs.
12
13 AMP is a very simple protocol which is not an application.  This module is a
14 "protocol construction kit" of sorts; it attempts to be the simplest wire-level
15 implementation of Deferreds.  AMP provides the following base-level features:
16
17     - Asynchronous request/response handling (hence the name)
18
19     - Requests and responses are both key/value pairs
20
21     - Binary transfer of all data: all data is length-prefixed.  Your
22       application will never need to worry about quoting.
23
24     - Command dispatching (like HTTP Verbs): the protocol is extensible, and
25       multiple AMP sub-protocols can be grouped together easily.
26
27 The protocol implementation also provides a few additional features which are
28 not part of the core wire protocol, but are nevertheless very useful:
29
30     - Tight TLS integration, with an included StartTLS command.
31
32     - Handshaking to other protocols: because AMP has well-defined message
33       boundaries and maintains all incoming and outgoing requests for you, you
34       can start a connection over AMP and then switch to another protocol.
35       This makes it ideal for firewall-traversal applications where you may
36       have only one forwarded port but multiple applications that want to use
37       it.
38
39 Using AMP with Twisted is simple.  Each message is a command, with a response.
40 You begin by defining a command type.  Commands specify their input and output
41 in terms of the types that they expect to see in the request and response
42 key-value pairs.  Here's an example of a command that adds two integers, 'a'
43 and 'b'::
44
45     class Sum(amp.Command):
46         arguments = [('a', amp.Integer()),
47                      ('b', amp.Integer())]
48         response = [('total', amp.Integer())]
49
50 Once you have specified a command, you need to make it part of a protocol, and
51 define a responder for it.  Here's a 'JustSum' protocol that includes a
52 responder for our 'Sum' command::
53
54     class JustSum(amp.AMP):
55         def sum(self, a, b):
56             total = a + b
57             print 'Did a sum: %d + %d = %d' % (a, b, total)
58             return {'total': total}
59         Sum.responder(sum)
60
61 Later, when you want to actually do a sum, the following expression will return
62 a L{Deferred} which will fire with the result::
63
64     ClientCreator(reactor, amp.AMP).connectTCP(...).addCallback(
65         lambda p: p.callRemote(Sum, a=13, b=81)).addCallback(
66             lambda result: result['total'])
67
68 Command responders may also return Deferreds, causing the response to be
69 sent only once the Deferred fires::
70
71     class DelayedSum(amp.AMP):
72         def slowSum(self, a, b):
73             total = a + b
74             result = defer.Deferred()
75             reactor.callLater(3, result.callback, {'total': total})
76             return result
77         Sum.responder(slowSum)
78
79 This is transparent to the caller.
80
81 You can also define the propagation of specific errors in AMP.  For example,
82 for the slightly more complicated case of division, we might have to deal with
83 division by zero::
84
85     class Divide(amp.Command):
86         arguments = [('numerator', amp.Integer()),
87                      ('denominator', amp.Integer())]
88         response = [('result', amp.Float())]
89         errors = {ZeroDivisionError: 'ZERO_DIVISION'}
90
91 The 'errors' mapping here tells AMP that if a responder to Divide emits a
92 L{ZeroDivisionError}, then the other side should be informed that an error of
93 the type 'ZERO_DIVISION' has occurred.  Writing a responder which takes
94 advantage of this is very simple - just raise your exception normally::
95
96     class JustDivide(amp.AMP):
97         def divide(self, numerator, denominator):
98             result = numerator / denominator
99             print 'Divided: %d / %d = %d' % (numerator, denominator, total)
100             return {'result': result}
101         Divide.responder(divide)
102
103 On the client side, the errors mapping will be used to determine what the
104 'ZERO_DIVISION' error means, and translated into an asynchronous exception,
105 which can be handled normally as any L{Deferred} would be::
106
107     def trapZero(result):
108         result.trap(ZeroDivisionError)
109         print "Divided by zero: returning INF"
110         return 1e1000
111     ClientCreator(reactor, amp.AMP).connectTCP(...).addCallback(
112         lambda p: p.callRemote(Divide, numerator=1234,
113                                denominator=0)
114         ).addErrback(trapZero)
115
116 For a complete, runnable example of both of these commands, see the files in
117 the Twisted repository::
118
119     doc/core/examples/ampserver.py
120     doc/core/examples/ampclient.py
121
122 On the wire, AMP is a protocol which uses 2-byte lengths to prefix keys and
123 values, and empty keys to separate messages::
124
125     <2-byte length><key><2-byte length><value>
126     <2-byte length><key><2-byte length><value>
127     ...
128     <2-byte length><key><2-byte length><value>
129     <NUL><NUL>                  # Empty Key == End of Message
130
131 And so on.  Because it's tedious to refer to lengths and NULs constantly, the
132 documentation will refer to packets as if they were newline delimited, like
133 so::
134
135     C: _command: sum
136     C: _ask: ef639e5c892ccb54
137     C: a: 13
138     C: b: 81
139
140     S: _answer: ef639e5c892ccb54
141     S: total: 94
142
143 Notes:
144
145 In general, the order of keys is arbitrary.  Specific uses of AMP may impose an
146 ordering requirement, but unless this is specified explicitly, any ordering may
147 be generated and any ordering must be accepted.  This applies to the
148 command-related keys I{_command} and I{_ask} as well as any other keys.
149
150 Values are limited to the maximum encodable size in a 16-bit length, 65535
151 bytes.
152
153 Keys are limited to the maximum encodable size in a 8-bit length, 255 bytes.
154 Note that we still use 2-byte lengths to encode keys.  This small redundancy
155 has several features:
156
157     - If an implementation becomes confused and starts emitting corrupt data,
158       or gets keys confused with values, many common errors will be signalled
159       immediately instead of delivering obviously corrupt packets.
160
161     - A single NUL will separate every key, and a double NUL separates
162       messages.  This provides some redundancy when debugging traffic dumps.
163
164     - NULs will be present at regular intervals along the protocol, providing
165       some padding for otherwise braindead C implementations of the protocol,
166       so that <stdio.h> string functions will see the NUL and stop.
167
168     - This makes it possible to run an AMP server on a port also used by a
169       plain-text protocol, and easily distinguish between non-AMP clients (like
170       web browsers) which issue non-NUL as the first byte, and AMP clients,
171       which always issue NUL as the first byte.
172 """
173
174 __metaclass__ = type
175
176 import types, warnings
177
178 from cStringIO import StringIO
179 from struct import pack
180 import decimal, datetime
181 from itertools import count
182
183 from zope.interface import Interface, implements
184
185 from twisted.python.compat import set
186 from twisted.python.util import unsignedID
187 from twisted.python.reflect import accumulateClassDict
188 from twisted.python.failure import Failure
189 from twisted.python import log, filepath
190
191 from twisted.internet.interfaces import IFileDescriptorReceiver
192 from twisted.internet.main import CONNECTION_LOST
193 from twisted.internet.error import PeerVerifyError, ConnectionLost
194 from twisted.internet.error import ConnectionClosed
195 from twisted.internet.defer import Deferred, maybeDeferred, fail
196 from twisted.protocols.basic import Int16StringReceiver, StatefulStringProtocol
197
198 try:
199     from twisted.internet import ssl
200 except ImportError:
201     ssl = None
202
203 if ssl and not ssl.supported:
204     ssl = None
205
206 if ssl is not None:
207     from twisted.internet.ssl import CertificateOptions, Certificate, DN, KeyPair
208
209 ASK = '_ask'
210 ANSWER = '_answer'
211 COMMAND = '_command'
212 ERROR = '_error'
213 ERROR_CODE = '_error_code'
214 ERROR_DESCRIPTION = '_error_description'
215 UNKNOWN_ERROR_CODE = 'UNKNOWN'
216 UNHANDLED_ERROR_CODE = 'UNHANDLED'
217
218 MAX_KEY_LENGTH = 0xff
219 MAX_VALUE_LENGTH = 0xffff
220
221
222 class IArgumentType(Interface):
223     """
224     An L{IArgumentType} can serialize a Python object into an AMP box and
225     deserialize information from an AMP box back into a Python object.
226
227     @since: 9.0
228     """
229     def fromBox(name, strings, objects, proto):
230         """
231         Given an argument name and an AMP box containing serialized values,
232         extract one or more Python objects and add them to the C{objects}
233         dictionary.
234
235         @param name: The name associated with this argument.  Most commonly,
236             this is the key which can be used to find a serialized value in
237             C{strings} and which should be used as the key in C{objects} to
238             associate with a structured Python object.
239         @type name: C{str}
240
241         @param strings: The AMP box from which to extract one or more
242             values.
243         @type strings: C{dict}
244
245         @param objects: The output dictionary to populate with the value for
246             this argument.
247         @type objects: C{dict}
248
249         @param proto: The protocol instance which received the AMP box being
250             interpreted.  Most likely this is an instance of L{AMP}, but
251             this is not guaranteed.
252
253         @return: C{None}
254         """
255
256
257     def toBox(name, strings, objects, proto):
258         """
259         Given an argument name and a dictionary containing structured Python
260         objects, serialize values into one or more strings and add them to
261         the C{strings} dictionary.
262
263         @param name: The name associated with this argument.  Most commonly,
264             this is the key which can be used to find an object in
265             C{objects} and which should be used as the key in C{strings} to
266             associate with a C{str} giving the serialized form of that
267             object.
268         @type name: C{str}
269
270         @param strings: The AMP box into which to insert one or more
271             strings.
272         @type strings: C{dict}
273
274         @param objects: The input dictionary from which to extract Python
275             objects to serialize.
276         @type objects: C{dict}
277
278         @param proto: The protocol instance which will send the AMP box once
279             it is fully populated.  Most likely this is an instance of
280             L{AMP}, but this is not guaranteed.
281
282         @return: C{None}
283         """
284
285
286
287 class IBoxSender(Interface):
288     """
289     A transport which can send L{AmpBox} objects.
290     """
291
292     def sendBox(box):
293         """
294         Send an L{AmpBox}.
295
296         @raise ProtocolSwitched: if the underlying protocol has been
297         switched.
298
299         @raise ConnectionLost: if the underlying connection has already been
300         lost.
301         """
302
303     def unhandledError(failure):
304         """
305         An unhandled error occurred in response to a box.  Log it
306         appropriately.
307
308         @param failure: a L{Failure} describing the error that occurred.
309         """
310
311
312
313 class IBoxReceiver(Interface):
314     """
315     An application object which can receive L{AmpBox} objects and dispatch them
316     appropriately.
317     """
318
319     def startReceivingBoxes(boxSender):
320         """
321         The L{ampBoxReceived} method will start being called; boxes may be
322         responded to by responding to the given L{IBoxSender}.
323
324         @param boxSender: an L{IBoxSender} provider.
325         """
326
327
328     def ampBoxReceived(box):
329         """
330         A box was received from the transport; dispatch it appropriately.
331         """
332
333
334     def stopReceivingBoxes(reason):
335         """
336         No further boxes will be received on this connection.
337
338         @type reason: L{Failure}
339         """
340
341
342
343 class IResponderLocator(Interface):
344     """
345     An application object which can look up appropriate responder methods for
346     AMP commands.
347     """
348
349     def locateResponder(name):
350         """
351         Locate a responder method appropriate for the named command.
352
353         @param name: the wire-level name (commandName) of the AMP command to be
354         responded to.
355
356         @return: a 1-argument callable that takes an L{AmpBox} with argument
357         values for the given command, and returns an L{AmpBox} containing
358         argument values for the named command, or a L{Deferred} that fires the
359         same.
360         """
361
362
363
364 class AmpError(Exception):
365     """
366     Base class of all Amp-related exceptions.
367     """
368
369
370
371 class ProtocolSwitched(Exception):
372     """
373     Connections which have been switched to other protocols can no longer
374     accept traffic at the AMP level.  This is raised when you try to send it.
375     """
376
377
378
379 class OnlyOneTLS(AmpError):
380     """
381     This is an implementation limitation; TLS may only be started once per
382     connection.
383     """
384
385
386
387 class NoEmptyBoxes(AmpError):
388     """
389     You can't have empty boxes on the connection.  This is raised when you
390     receive or attempt to send one.
391     """
392
393
394
395 class InvalidSignature(AmpError):
396     """
397     You didn't pass all the required arguments.
398     """
399
400
401
402 class TooLong(AmpError):
403     """
404     One of the protocol's length limitations was violated.
405
406     @ivar isKey: true if the string being encoded in a key position, false if
407     it was in a value position.
408
409     @ivar isLocal: Was the string encoded locally, or received too long from
410     the network?  (It's only physically possible to encode "too long" values on
411     the network for keys.)
412
413     @ivar value: The string that was too long.
414
415     @ivar keyName: If the string being encoded was in a value position, what
416     key was it being encoded for?
417     """
418
419     def __init__(self, isKey, isLocal, value, keyName=None):
420         AmpError.__init__(self)
421         self.isKey = isKey
422         self.isLocal = isLocal
423         self.value = value
424         self.keyName = keyName
425
426
427     def __repr__(self):
428         hdr = self.isKey and "key" or "value"
429         if not self.isKey:
430             hdr += ' ' + repr(self.keyName)
431         lcl = self.isLocal and "local" or "remote"
432         return "%s %s too long: %d" % (lcl, hdr, len(self.value))
433
434
435
436 class BadLocalReturn(AmpError):
437     """
438     A bad value was returned from a local command; we were unable to coerce it.
439     """
440     def __init__(self, message, enclosed):
441         AmpError.__init__(self)
442         self.message = message
443         self.enclosed = enclosed
444
445
446     def __repr__(self):
447         return self.message + " " + self.enclosed.getBriefTraceback()
448
449     __str__ = __repr__
450
451
452
453 class RemoteAmpError(AmpError):
454     """
455     This error indicates that something went wrong on the remote end of the
456     connection, and the error was serialized and transmitted to you.
457     """
458     def __init__(self, errorCode, description, fatal=False, local=None):
459         """Create a remote error with an error code and description.
460
461         @param errorCode: the AMP error code of this error.
462
463         @param description: some text to show to the user.
464
465         @param fatal: a boolean, true if this error should terminate the
466         connection.
467
468         @param local: a local Failure, if one exists.
469         """
470         if local:
471             localwhat = ' (local)'
472             othertb = local.getBriefTraceback()
473         else:
474             localwhat = ''
475             othertb = ''
476         Exception.__init__(self, "Code<%s>%s: %s%s" % (
477                 errorCode, localwhat,
478                 description, othertb))
479         self.local = local
480         self.errorCode = errorCode
481         self.description = description
482         self.fatal = fatal
483
484
485
486 class UnknownRemoteError(RemoteAmpError):
487     """
488     This means that an error whose type we can't identify was raised from the
489     other side.
490     """
491     def __init__(self, description):
492         errorCode = UNKNOWN_ERROR_CODE
493         RemoteAmpError.__init__(self, errorCode, description)
494
495
496
497 class MalformedAmpBox(AmpError):
498     """
499     This error indicates that the wire-level protocol was malformed.
500     """
501
502
503
504 class UnhandledCommand(AmpError):
505     """
506     A command received via amp could not be dispatched.
507     """
508
509
510
511 class IncompatibleVersions(AmpError):
512     """
513     It was impossible to negotiate a compatible version of the protocol with
514     the other end of the connection.
515     """
516
517
518 PROTOCOL_ERRORS = {UNHANDLED_ERROR_CODE: UnhandledCommand}
519
520 class AmpBox(dict):
521     """
522     I am a packet in the AMP protocol, much like a regular str:str dictionary.
523     """
524     __slots__ = []              # be like a regular dictionary, don't magically
525                                 # acquire a __dict__...
526
527
528     def copy(self):
529         """
530         Return another AmpBox just like me.
531         """
532         newBox = self.__class__()
533         newBox.update(self)
534         return newBox
535
536
537     def serialize(self):
538         """
539         Convert me into a wire-encoded string.
540
541         @return: a str encoded according to the rules described in the module
542         docstring.
543         """
544         i = self.items()
545         i.sort()
546         L = []
547         w = L.append
548         for k, v in i:
549             if type(k) == unicode:
550                 raise TypeError("Unicode key not allowed: %r" % k)
551             if type(v) == unicode:
552                 raise TypeError(
553                     "Unicode value for key %r not allowed: %r" % (k, v))
554             if len(k) > MAX_KEY_LENGTH:
555                 raise TooLong(True, True, k, None)
556             if len(v) > MAX_VALUE_LENGTH:
557                 raise TooLong(False, True, v, k)
558             for kv in k, v:
559                 w(pack("!H", len(kv)))
560                 w(kv)
561         w(pack("!H", 0))
562         return ''.join(L)
563
564
565     def _sendTo(self, proto):
566         """
567         Serialize and send this box to a Amp instance.  By the time it is being
568         sent, several keys are required.  I must have exactly ONE of::
569
570             _ask
571             _answer
572             _error
573
574         If the '_ask' key is set, then the '_command' key must also be
575         set.
576
577         @param proto: an AMP instance.
578         """
579         proto.sendBox(self)
580
581     def __repr__(self):
582         return 'AmpBox(%s)' % (dict.__repr__(self),)
583
584 # amp.Box => AmpBox
585
586 Box = AmpBox
587
588 class QuitBox(AmpBox):
589     """
590     I am an AmpBox that, upon being sent, terminates the connection.
591     """
592     __slots__ = []
593
594
595     def __repr__(self):
596         return 'QuitBox(**%s)' % (super(QuitBox, self).__repr__(),)
597
598
599     def _sendTo(self, proto):
600         """
601         Immediately call loseConnection after sending.
602         """
603         super(QuitBox, self)._sendTo(proto)
604         proto.transport.loseConnection()
605
606
607
608 class _SwitchBox(AmpBox):
609     """
610     Implementation detail of ProtocolSwitchCommand: I am a AmpBox which sets
611     up state for the protocol to switch.
612     """
613
614     # DON'T set __slots__ here; we do have an attribute.
615
616     def __init__(self, innerProto, **kw):
617         """
618         Create a _SwitchBox with the protocol to switch to after being sent.
619
620         @param innerProto: the protocol instance to switch to.
621         @type innerProto: an IProtocol provider.
622         """
623         super(_SwitchBox, self).__init__(**kw)
624         self.innerProto = innerProto
625
626
627     def __repr__(self):
628         return '_SwitchBox(%r, **%s)' % (self.innerProto,
629                                          dict.__repr__(self),)
630
631
632     def _sendTo(self, proto):
633         """
634         Send me; I am the last box on the connection.  All further traffic will be
635         over the new protocol.
636         """
637         super(_SwitchBox, self)._sendTo(proto)
638         proto._lockForSwitch()
639         proto._switchTo(self.innerProto)
640
641
642
643 class BoxDispatcher:
644     """
645     A L{BoxDispatcher} dispatches '_ask', '_answer', and '_error' L{AmpBox}es,
646     both incoming and outgoing, to their appropriate destinations.
647
648     Outgoing commands are converted into L{Deferred}s and outgoing boxes, and
649     associated tracking state to fire those L{Deferred} when '_answer' boxes
650     come back.  Incoming '_answer' and '_error' boxes are converted into
651     callbacks and errbacks on those L{Deferred}s, respectively.
652
653     Incoming '_ask' boxes are converted into method calls on a supplied method
654     locator.
655
656     @ivar _outstandingRequests: a dictionary mapping request IDs to
657     L{Deferred}s which were returned for those requests.
658
659     @ivar locator: an object with a L{locateResponder} method that locates a
660     responder function that takes a Box and returns a result (either a Box or a
661     Deferred which fires one).
662
663     @ivar boxSender: an object which can send boxes, via the L{_sendBox}
664     method, such as an L{AMP} instance.
665     @type boxSender: L{IBoxSender}
666     """
667
668     implements(IBoxReceiver)
669
670     _failAllReason = None
671     _outstandingRequests = None
672     _counter = 0L
673     boxSender = None
674
675     def __init__(self, locator):
676         self._outstandingRequests = {}
677         self.locator = locator
678
679
680     def startReceivingBoxes(self, boxSender):
681         """
682         The given boxSender is going to start calling boxReceived on this
683         L{BoxDispatcher}.
684
685         @param boxSender: The L{IBoxSender} to send command responses to.
686         """
687         self.boxSender = boxSender
688
689
690     def stopReceivingBoxes(self, reason):
691         """
692         No further boxes will be received here.  Terminate all currently
693         oustanding command deferreds with the given reason.
694         """
695         self.failAllOutgoing(reason)
696
697
698     def failAllOutgoing(self, reason):
699         """
700         Call the errback on all outstanding requests awaiting responses.
701
702         @param reason: the Failure instance to pass to those errbacks.
703         """
704         self._failAllReason = reason
705         OR = self._outstandingRequests.items()
706         self._outstandingRequests = None # we can never send another request
707         for key, value in OR:
708             value.errback(reason)
709
710
711     def _nextTag(self):
712         """
713         Generate protocol-local serial numbers for _ask keys.
714
715         @return: a string that has not yet been used on this connection.
716         """
717         self._counter += 1
718         return '%x' % (self._counter,)
719
720
721     def _sendBoxCommand(self, command, box, requiresAnswer=True):
722         """
723         Send a command across the wire with the given C{amp.Box}.
724
725         Mutate the given box to give it any additional keys (_command, _ask)
726         required for the command and request/response machinery, then send it.
727
728         If requiresAnswer is True, returns a C{Deferred} which fires when a
729         response is received. The C{Deferred} is fired with an C{amp.Box} on
730         success, or with an C{amp.RemoteAmpError} if an error is received.
731
732         If the Deferred fails and the error is not handled by the caller of
733         this method, the failure will be logged and the connection dropped.
734
735         @param command: a str, the name of the command to issue.
736
737         @param box: an AmpBox with the arguments for the command.
738
739         @param requiresAnswer: a boolean.  Defaults to True.  If True, return a
740         Deferred which will fire when the other side responds to this command.
741         If False, return None and do not ask the other side for acknowledgement.
742
743         @return: a Deferred which fires the AmpBox that holds the response to
744         this command, or None, as specified by requiresAnswer.
745
746         @raise ProtocolSwitched: if the protocol has been switched.
747         """
748         if self._failAllReason is not None:
749             return fail(self._failAllReason)
750         box[COMMAND] = command
751         tag = self._nextTag()
752         if requiresAnswer:
753             box[ASK] = tag
754         box._sendTo(self.boxSender)
755         if requiresAnswer:
756             result = self._outstandingRequests[tag] = Deferred()
757         else:
758             result = None
759         return result
760
761
762     def callRemoteString(self, command, requiresAnswer=True, **kw):
763         """
764         This is a low-level API, designed only for optimizing simple messages
765         for which the overhead of parsing is too great.
766
767         @param command: a str naming the command.
768
769         @param kw: arguments to the amp box.
770
771         @param requiresAnswer: a boolean.  Defaults to True.  If True, return a
772         Deferred which will fire when the other side responds to this command.
773         If False, return None and do not ask the other side for acknowledgement.
774
775         @return: a Deferred which fires the AmpBox that holds the response to
776         this command, or None, as specified by requiresAnswer.
777         """
778         box = Box(kw)
779         return self._sendBoxCommand(command, box, requiresAnswer)
780
781
782     def callRemote(self, commandType, *a, **kw):
783         """
784         This is the primary high-level API for sending messages via AMP.  Invoke it
785         with a command and appropriate arguments to send a message to this
786         connection's peer.
787
788         @param commandType: a subclass of Command.
789         @type commandType: L{type}
790
791         @param a: Positional (special) parameters taken by the command.
792         Positional parameters will typically not be sent over the wire.  The
793         only command included with AMP which uses positional parameters is
794         L{ProtocolSwitchCommand}, which takes the protocol that will be
795         switched to as its first argument.
796
797         @param kw: Keyword arguments taken by the command.  These are the
798         arguments declared in the command's 'arguments' attribute.  They will
799         be encoded and sent to the peer as arguments for the L{commandType}.
800
801         @return: If L{commandType} has a C{requiresAnswer} attribute set to
802         L{False}, then return L{None}.  Otherwise, return a L{Deferred} which
803         fires with a dictionary of objects representing the result of this
804         call.  Additionally, this L{Deferred} may fail with an exception
805         representing a connection failure, with L{UnknownRemoteError} if the
806         other end of the connection fails for an unknown reason, or with any
807         error specified as a key in L{commandType}'s C{errors} dictionary.
808         """
809
810         # XXX this takes command subclasses and not command objects on purpose.
811         # There's really no reason to have all this back-and-forth between
812         # command objects and the protocol, and the extra object being created
813         # (the Command instance) is pointless.  Command is kind of like
814         # Interface, and should be more like it.
815
816         # In other words, the fact that commandType is instantiated here is an
817         # implementation detail.  Don't rely on it.
818
819         try:
820             co = commandType(*a, **kw)
821         except:
822             return fail()
823         return co._doCommand(self)
824
825
826     def unhandledError(self, failure):
827         """
828         This is a terminal callback called after application code has had a
829         chance to quash any errors.
830         """
831         return self.boxSender.unhandledError(failure)
832
833
834     def _answerReceived(self, box):
835         """
836         An AMP box was received that answered a command previously sent with
837         L{callRemote}.
838
839         @param box: an AmpBox with a value for its L{ANSWER} key.
840         """
841         question = self._outstandingRequests.pop(box[ANSWER])
842         question.addErrback(self.unhandledError)
843         question.callback(box)
844
845
846     def _errorReceived(self, box):
847         """
848         An AMP box was received that answered a command previously sent with
849         L{callRemote}, with an error.
850
851         @param box: an L{AmpBox} with a value for its L{ERROR}, L{ERROR_CODE},
852         and L{ERROR_DESCRIPTION} keys.
853         """
854         question = self._outstandingRequests.pop(box[ERROR])
855         question.addErrback(self.unhandledError)
856         errorCode = box[ERROR_CODE]
857         description = box[ERROR_DESCRIPTION]
858         if errorCode in PROTOCOL_ERRORS:
859             exc = PROTOCOL_ERRORS[errorCode](errorCode, description)
860         else:
861             exc = RemoteAmpError(errorCode, description)
862         question.errback(Failure(exc))
863
864
865     def _commandReceived(self, box):
866         """
867         @param box: an L{AmpBox} with a value for its L{COMMAND} and L{ASK}
868         keys.
869         """
870         def formatAnswer(answerBox):
871             answerBox[ANSWER] = box[ASK]
872             return answerBox
873         def formatError(error):
874             if error.check(RemoteAmpError):
875                 code = error.value.errorCode
876                 desc = error.value.description
877                 if error.value.fatal:
878                     errorBox = QuitBox()
879                 else:
880                     errorBox = AmpBox()
881             else:
882                 errorBox = QuitBox()
883                 log.err(error) # here is where server-side logging happens
884                                # if the error isn't handled
885                 code = UNKNOWN_ERROR_CODE
886                 desc = "Unknown Error"
887             errorBox[ERROR] = box[ASK]
888             errorBox[ERROR_DESCRIPTION] = desc
889             errorBox[ERROR_CODE] = code
890             return errorBox
891         deferred = self.dispatchCommand(box)
892         if ASK in box:
893             deferred.addCallbacks(formatAnswer, formatError)
894             deferred.addCallback(self._safeEmit)
895         deferred.addErrback(self.unhandledError)
896
897
898     def ampBoxReceived(self, box):
899         """
900         An AmpBox was received, representing a command, or an answer to a
901         previously issued command (either successful or erroneous).  Respond to
902         it according to its contents.
903
904         @param box: an AmpBox
905
906         @raise NoEmptyBoxes: when a box is received that does not contain an
907         '_answer', '_command' / '_ask', or '_error' key; i.e. one which does not
908         fit into the command / response protocol defined by AMP.
909         """
910         if ANSWER in box:
911             self._answerReceived(box)
912         elif ERROR in box:
913             self._errorReceived(box)
914         elif COMMAND in box:
915             self._commandReceived(box)
916         else:
917             raise NoEmptyBoxes(box)
918
919
920     def _safeEmit(self, aBox):
921         """
922         Emit a box, ignoring L{ProtocolSwitched} and L{ConnectionLost} errors
923         which cannot be usefully handled.
924         """
925         try:
926             aBox._sendTo(self.boxSender)
927         except (ProtocolSwitched, ConnectionLost):
928             pass
929
930
931     def dispatchCommand(self, box):
932         """
933         A box with a _command key was received.
934
935         Dispatch it to a local handler call it.
936
937         @param proto: an AMP instance.
938         @param box: an AmpBox to be dispatched.
939         """
940         cmd = box[COMMAND]
941         responder = self.locator.locateResponder(cmd)
942         if responder is None:
943             return fail(RemoteAmpError(
944                     UNHANDLED_ERROR_CODE,
945                     "Unhandled Command: %r" % (cmd,),
946                     False,
947                     local=Failure(UnhandledCommand())))
948         return maybeDeferred(responder, box)
949
950
951
952 class CommandLocator:
953     """
954     A L{CommandLocator} is a collection of responders to AMP L{Command}s, with
955     the help of the L{Command.responder} decorator.
956     """
957
958     class __metaclass__(type):
959         """
960         This metaclass keeps track of all of the Command.responder-decorated
961         methods defined since the last CommandLocator subclass was defined.  It
962         assumes (usually correctly, but unfortunately not necessarily so) that
963         those commands responders were all declared as methods of the class
964         being defined.  Note that this list can be incorrect if users use the
965         Command.responder decorator outside the context of a CommandLocator
966         class declaration.
967
968         Command responders defined on subclasses are given precedence over
969         those inherited from a base class.
970
971         The Command.responder decorator explicitly cooperates with this
972         metaclass.
973         """
974
975         _currentClassCommands = []
976         def __new__(cls, name, bases, attrs):
977             commands = cls._currentClassCommands[:]
978             cls._currentClassCommands[:] = []
979             cd = attrs['_commandDispatch'] = {}
980             subcls = type.__new__(cls, name, bases, attrs)
981             ancestors = list(subcls.__mro__[1:])
982             ancestors.reverse()
983             for ancestor in ancestors:
984                 cd.update(getattr(ancestor, '_commandDispatch', {}))
985             for commandClass, responderFunc in commands:
986                 cd[commandClass.commandName] = (commandClass, responderFunc)
987             if (bases and (
988                     subcls.lookupFunction != CommandLocator.lookupFunction)):
989                 def locateResponder(self, name):
990                     warnings.warn(
991                         "Override locateResponder, not lookupFunction.",
992                         category=PendingDeprecationWarning,
993                         stacklevel=2)
994                     return self.lookupFunction(name)
995                 subcls.locateResponder = locateResponder
996             return subcls
997
998
999     implements(IResponderLocator)
1000
1001
1002     def _wrapWithSerialization(self, aCallable, command):
1003         """
1004         Wrap aCallable with its command's argument de-serialization
1005         and result serialization logic.
1006
1007         @param aCallable: a callable with a 'command' attribute, designed to be
1008         called with keyword arguments.
1009
1010         @param command: the command class whose serialization to use.
1011
1012         @return: a 1-arg callable which, when invoked with an AmpBox, will
1013         deserialize the argument list and invoke appropriate user code for the
1014         callable's command, returning a Deferred which fires with the result or
1015         fails with an error.
1016         """
1017         def doit(box):
1018             kw = command.parseArguments(box, self)
1019             def checkKnownErrors(error):
1020                 key = error.trap(*command.allErrors)
1021                 code = command.allErrors[key]
1022                 desc = str(error.value)
1023                 return Failure(RemoteAmpError(
1024                         code, desc, key in command.fatalErrors, local=error))
1025             def makeResponseFor(objects):
1026                 try:
1027                     return command.makeResponse(objects, self)
1028                 except:
1029                     # let's helpfully log this.
1030                     originalFailure = Failure()
1031                     raise BadLocalReturn(
1032                         "%r returned %r and %r could not serialize it" % (
1033                             aCallable,
1034                             objects,
1035                             command),
1036                         originalFailure)
1037             return maybeDeferred(aCallable, **kw).addCallback(
1038                 makeResponseFor).addErrback(
1039                 checkKnownErrors)
1040         return doit
1041
1042
1043     def lookupFunction(self, name):
1044         """
1045         Deprecated synonym for L{locateResponder}
1046         """
1047         if self.__class__.lookupFunction != CommandLocator.lookupFunction:
1048             return CommandLocator.locateResponder(self, name)
1049         else:
1050             warnings.warn("Call locateResponder, not lookupFunction.",
1051                           category=PendingDeprecationWarning,
1052                           stacklevel=2)
1053         return self.locateResponder(name)
1054
1055
1056     def locateResponder(self, name):
1057         """
1058         Locate a callable to invoke when executing the named command.
1059
1060         @param name: the normalized name (from the wire) of the command.
1061
1062         @return: a 1-argument function that takes a Box and returns a box or a
1063         Deferred which fires a Box, for handling the command identified by the
1064         given name, or None, if no appropriate responder can be found.
1065         """
1066         # Try to find a high-level method to invoke, and if we can't find one,
1067         # fall back to a low-level one.
1068         cd = self._commandDispatch
1069         if name in cd:
1070             commandClass, responderFunc = cd[name]
1071             responderMethod = types.MethodType(
1072                 responderFunc, self, self.__class__)
1073             return self._wrapWithSerialization(responderMethod, commandClass)
1074
1075
1076
1077 class SimpleStringLocator(object):
1078     """
1079     Implement the L{locateResponder} method to do simple, string-based
1080     dispatch.
1081     """
1082
1083     implements(IResponderLocator)
1084
1085     baseDispatchPrefix = 'amp_'
1086
1087     def locateResponder(self, name):
1088         """
1089         Locate a callable to invoke when executing the named command.
1090
1091         @return: a function with the name C{"amp_" + name} on L{self}, or None
1092         if no such function exists.  This function will then be called with the
1093         L{AmpBox} itself as an argument.
1094
1095         @param name: the normalized name (from the wire) of the command.
1096         """
1097         fName = self.baseDispatchPrefix + (name.upper())
1098         return getattr(self, fName, None)
1099
1100
1101
1102 PYTHON_KEYWORDS = [
1103     'and', 'del', 'for', 'is', 'raise', 'assert', 'elif', 'from', 'lambda',
1104     'return', 'break', 'else', 'global', 'not', 'try', 'class', 'except',
1105     'if', 'or', 'while', 'continue', 'exec', 'import', 'pass', 'yield',
1106     'def', 'finally', 'in', 'print']
1107
1108
1109
1110 def _wireNameToPythonIdentifier(key):
1111     """
1112     (Private) Normalize an argument name from the wire for use with Python
1113     code.  If the return value is going to be a python keyword it will be
1114     capitalized.  If it contains any dashes they will be replaced with
1115     underscores.
1116
1117     The rationale behind this method is that AMP should be an inherently
1118     multi-language protocol, so message keys may contain all manner of bizarre
1119     bytes.  This is not a complete solution; there are still forms of arguments
1120     that this implementation will be unable to parse.  However, Python
1121     identifiers share a huge raft of properties with identifiers from many
1122     other languages, so this is a 'good enough' effort for now.  We deal
1123     explicitly with dashes because that is the most likely departure: Lisps
1124     commonly use dashes to separate method names, so protocols initially
1125     implemented in a lisp amp dialect may use dashes in argument or command
1126     names.
1127
1128     @param key: a str, looking something like 'foo-bar-baz' or 'from'
1129
1130     @return: a str which is a valid python identifier, looking something like
1131     'foo_bar_baz' or 'From'.
1132     """
1133     lkey = key.replace("-", "_")
1134     if lkey in PYTHON_KEYWORDS:
1135         return lkey.title()
1136     return lkey
1137
1138
1139
1140 class Argument:
1141     """
1142     Base-class of all objects that take values from Amp packets and convert
1143     them into objects for Python functions.
1144
1145     This implementation of L{IArgumentType} provides several higher-level
1146     hooks for subclasses to override.  See L{toString} and L{fromString}
1147     which will be used to define the behavior of L{IArgumentType.toBox} and
1148     L{IArgumentType.fromBox}, respectively.
1149     """
1150     implements(IArgumentType)
1151
1152     optional = False
1153
1154
1155     def __init__(self, optional=False):
1156         """
1157         Create an Argument.
1158
1159         @param optional: a boolean indicating whether this argument can be
1160         omitted in the protocol.
1161         """
1162         self.optional = optional
1163
1164
1165     def retrieve(self, d, name, proto):
1166         """
1167         Retrieve the given key from the given dictionary, removing it if found.
1168
1169         @param d: a dictionary.
1170
1171         @param name: a key in L{d}.
1172
1173         @param proto: an instance of an AMP.
1174
1175         @raise KeyError: if I am not optional and no value was found.
1176
1177         @return: d[name].
1178         """
1179         if self.optional:
1180             value = d.get(name)
1181             if value is not None:
1182                 del d[name]
1183         else:
1184             value = d.pop(name)
1185         return value
1186
1187
1188     def fromBox(self, name, strings, objects, proto):
1189         """
1190         Populate an 'out' dictionary with mapping names to Python values
1191         decoded from an 'in' AmpBox mapping strings to string values.
1192
1193         @param name: the argument name to retrieve
1194         @type name: str
1195
1196         @param strings: The AmpBox to read string(s) from, a mapping of
1197         argument names to string values.
1198         @type strings: AmpBox
1199
1200         @param objects: The dictionary to write object(s) to, a mapping of
1201         names to Python objects.
1202         @type objects: dict
1203
1204         @param proto: an AMP instance.
1205         """
1206         st = self.retrieve(strings, name, proto)
1207         nk = _wireNameToPythonIdentifier(name)
1208         if self.optional and st is None:
1209             objects[nk] = None
1210         else:
1211             objects[nk] = self.fromStringProto(st, proto)
1212
1213
1214     def toBox(self, name, strings, objects, proto):
1215         """
1216         Populate an 'out' AmpBox with strings encoded from an 'in' dictionary
1217         mapping names to Python values.
1218
1219         @param name: the argument name to retrieve
1220         @type name: str
1221
1222         @param strings: The AmpBox to write string(s) to, a mapping of
1223         argument names to string values.
1224         @type strings: AmpBox
1225
1226         @param objects: The dictionary to read object(s) from, a mapping of
1227         names to Python objects.
1228
1229         @type objects: dict
1230
1231         @param proto: the protocol we are converting for.
1232         @type proto: AMP
1233         """
1234         obj = self.retrieve(objects, _wireNameToPythonIdentifier(name), proto)
1235         if self.optional and obj is None:
1236             # strings[name] = None
1237             pass
1238         else:
1239             strings[name] = self.toStringProto(obj, proto)
1240
1241
1242     def fromStringProto(self, inString, proto):
1243         """
1244         Convert a string to a Python value.
1245
1246         @param inString: the string to convert.
1247
1248         @param proto: the protocol we are converting for.
1249         @type proto: AMP
1250
1251         @return: a Python object.
1252         """
1253         return self.fromString(inString)
1254
1255
1256     def toStringProto(self, inObject, proto):
1257         """
1258         Convert a Python object to a string.
1259
1260         @param inObject: the object to convert.
1261
1262         @param proto: the protocol we are converting for.
1263         @type proto: AMP
1264         """
1265         return self.toString(inObject)
1266
1267
1268     def fromString(self, inString):
1269         """
1270         Convert a string to a Python object.  Subclasses must implement this.
1271
1272         @param inString: the string to convert.
1273         @type inString: str
1274
1275         @return: the decoded value from inString
1276         """
1277
1278
1279     def toString(self, inObject):
1280         """
1281         Convert a Python object into a string for passing over the network.
1282
1283         @param inObject: an object of the type that this Argument is intended
1284         to deal with.
1285
1286         @return: the wire encoding of inObject
1287         @rtype: str
1288         """
1289
1290
1291
1292 class Integer(Argument):
1293     """
1294     Encode any integer values of any size on the wire as the string
1295     representation.
1296
1297     Example: C{123} becomes C{"123"}
1298     """
1299     fromString = int
1300     def toString(self, inObject):
1301         return str(int(inObject))
1302
1303
1304
1305 class String(Argument):
1306     """
1307     Don't do any conversion at all; just pass through 'str'.
1308     """
1309     def toString(self, inObject):
1310         return inObject
1311
1312
1313     def fromString(self, inString):
1314         return inString
1315
1316
1317
1318 class Float(Argument):
1319     """
1320     Encode floating-point values on the wire as their repr.
1321     """
1322     fromString = float
1323     toString = repr
1324
1325
1326
1327 class Boolean(Argument):
1328     """
1329     Encode True or False as "True" or "False" on the wire.
1330     """
1331     def fromString(self, inString):
1332         if inString == 'True':
1333             return True
1334         elif inString == 'False':
1335             return False
1336         else:
1337             raise TypeError("Bad boolean value: %r" % (inString,))
1338
1339
1340     def toString(self, inObject):
1341         if inObject:
1342             return 'True'
1343         else:
1344             return 'False'
1345
1346
1347
1348 class Unicode(String):
1349     """
1350     Encode a unicode string on the wire as UTF-8.
1351     """
1352
1353     def toString(self, inObject):
1354         # assert isinstance(inObject, unicode)
1355         return String.toString(self, inObject.encode('utf-8'))
1356
1357
1358     def fromString(self, inString):
1359         # assert isinstance(inString, str)
1360         return String.fromString(self, inString).decode('utf-8')
1361
1362
1363
1364 class Path(Unicode):
1365     """
1366     Encode and decode L{filepath.FilePath} instances as paths on the wire.
1367
1368     This is really intended for use with subprocess communication tools:
1369     exchanging pathnames on different machines over a network is not generally
1370     meaningful, but neither is it disallowed; you can use this to communicate
1371     about NFS paths, for example.
1372     """
1373     def fromString(self, inString):
1374         return filepath.FilePath(Unicode.fromString(self, inString))
1375
1376
1377     def toString(self, inObject):
1378         return Unicode.toString(self, inObject.path)
1379
1380
1381
1382 class ListOf(Argument):
1383     """
1384     Encode and decode lists of instances of a single other argument type.
1385
1386     For example, if you want to pass::
1387
1388         [3, 7, 9, 15]
1389
1390     You can create an argument like this::
1391
1392         ListOf(Integer())
1393
1394     The serialized form of the entire list is subject to the limit imposed by
1395     L{MAX_VALUE_LENGTH}.  List elements are represented as 16-bit length
1396     prefixed strings.  The argument type passed to the L{ListOf} initializer is
1397     responsible for producing the serialized form of each element.
1398
1399     @ivar elementType: The L{Argument} instance used to encode and decode list
1400         elements (note, not an arbitrary L{IArgument} implementation:
1401         arguments must be implemented using only the C{fromString} and
1402         C{toString} methods, not the C{fromBox} and C{toBox} methods).
1403
1404     @param optional: a boolean indicating whether this argument can be
1405         omitted in the protocol.
1406
1407     @since: 10.0
1408     """
1409     def __init__(self, elementType, optional=False):
1410         self.elementType = elementType
1411         Argument.__init__(self, optional)
1412
1413
1414     def fromString(self, inString):
1415         """
1416         Convert the serialized form of a list of instances of some type back
1417         into that list.
1418         """
1419         strings = []
1420         parser = Int16StringReceiver()
1421         parser.stringReceived = strings.append
1422         parser.dataReceived(inString)
1423         return map(self.elementType.fromString, strings)
1424
1425
1426     def toString(self, inObject):
1427         """
1428         Serialize the given list of objects to a single string.
1429         """
1430         strings = []
1431         for obj in inObject:
1432             serialized = self.elementType.toString(obj)
1433             strings.append(pack('!H', len(serialized)))
1434             strings.append(serialized)
1435         return ''.join(strings)
1436
1437
1438
1439 class AmpList(Argument):
1440     """
1441     Convert a list of dictionaries into a list of AMP boxes on the wire.
1442
1443     For example, if you want to pass::
1444
1445         [{'a': 7, 'b': u'hello'}, {'a': 9, 'b': u'goodbye'}]
1446
1447     You might use an AmpList like this in your arguments or response list::
1448
1449         AmpList([('a', Integer()),
1450                  ('b', Unicode())])
1451     """
1452     def __init__(self, subargs, optional=False):
1453         """
1454         Create an AmpList.
1455
1456         @param subargs: a list of 2-tuples of ('name', argument) describing the
1457         schema of the dictionaries in the sequence of amp boxes.
1458
1459         @param optional: a boolean indicating whether this argument can be
1460         omitted in the protocol.
1461         """
1462         self.subargs = subargs
1463         Argument.__init__(self, optional)
1464
1465
1466     def fromStringProto(self, inString, proto):
1467         boxes = parseString(inString)
1468         values = [_stringsToObjects(box, self.subargs, proto)
1469                   for box in boxes]
1470         return values
1471
1472
1473     def toStringProto(self, inObject, proto):
1474         return ''.join([_objectsToStrings(
1475                     objects, self.subargs, Box(), proto
1476                     ).serialize() for objects in inObject])
1477
1478
1479
1480 class Descriptor(Integer):
1481     """
1482     Encode and decode file descriptors for exchange over a UNIX domain socket.
1483
1484     This argument type requires an AMP connection set up over an
1485     L{IUNIXTransport<twisted.internet.interfaces.IUNIXTransport>} provider (for
1486     example, the kind of connection created by
1487     L{IReactorUNIX.connectUNIX<twisted.internet.interfaces.IReactorUNIX.connectUNIX>}
1488     and L{UNIXClientEndpoint<twisted.internet.endpoints.UNIXClientEndpoint>}).
1489
1490     There is no correspondence between the integer value of the file descriptor
1491     on the sending and receiving sides, therefore an alternate approach is taken
1492     to matching up received descriptors with particular L{Descriptor}
1493     parameters.  The argument is encoded to an ordinal (unique per connection)
1494     for inclusion in the AMP command or response box.  The descriptor itself is
1495     sent using
1496     L{IUNIXTransport.sendFileDescriptor<twisted.internet.interfaces.IUNIXTransport.sendFileDescriptor>}.
1497     The receiver uses the order in which file descriptors are received and the
1498     ordinal value to come up with the received copy of the descriptor.
1499     """
1500     def fromStringProto(self, inString, proto):
1501         """
1502         Take a unique identifier associated with a file descriptor which must
1503         have been received by now and use it to look up that descriptor in a
1504         dictionary where they are kept.
1505
1506         @param inString: The base representation (as a byte string) of an
1507             ordinal indicating which file descriptor corresponds to this usage
1508             of this argument.
1509         @type inString: C{str}
1510
1511         @param proto: The protocol used to receive this descriptor.  This
1512             protocol must be connected via a transport providing
1513             L{IUNIXTransport<twisted.internet.interfaces.IUNIXTransport>}.
1514         @type proto: L{BinaryBoxProtocol}
1515
1516         @return: The file descriptor represented by C{inString}.
1517         @rtype: C{int}
1518         """
1519         return proto._getDescriptor(int(inString))
1520
1521
1522     def toStringProto(self, inObject, proto):
1523         """
1524         Send C{inObject}, an integer file descriptor, over C{proto}'s connection
1525         and return a unique identifier which will allow the receiver to
1526         associate the file descriptor with this argument.
1527
1528         @param inObject: A file descriptor to duplicate over an AMP connection
1529             as the value for this argument.
1530         @type inObject: C{int}
1531
1532         @param proto: The protocol which will be used to send this descriptor.
1533             This protocol must be connected via a transport providing
1534             L{IUNIXTransport<twisted.internet.interfaces.IUNIXTransport>}.
1535
1536         @return: A byte string which can be used by the receiver to reconstruct
1537             the file descriptor.
1538         @type: C{str}
1539         """
1540         identifier = proto._sendFileDescriptor(inObject)
1541         outString = Integer.toStringProto(self, identifier, proto)
1542         return outString
1543
1544
1545
1546 class Command:
1547     """
1548     Subclass me to specify an AMP Command.
1549
1550     @cvar arguments: A list of 2-tuples of (name, Argument-subclass-instance),
1551     specifying the names and values of the parameters which are required for
1552     this command.
1553
1554     @cvar response: A list like L{arguments}, but instead used for the return
1555     value.
1556
1557     @cvar errors: A mapping of subclasses of L{Exception} to wire-protocol tags
1558     for errors represented as L{str}s.  Responders which raise keys from this
1559     dictionary will have the error translated to the corresponding tag on the
1560     wire.  Invokers which receive Deferreds from invoking this command with
1561     L{AMP.callRemote} will potentially receive Failures with keys from this
1562     mapping as their value.  This mapping is inherited; if you declare a
1563     command which handles C{FooError} as 'FOO_ERROR', then subclass it and
1564     specify C{BarError} as 'BAR_ERROR', responders to the subclass may raise
1565     either C{FooError} or C{BarError}, and invokers must be able to deal with
1566     either of those exceptions.
1567
1568     @cvar fatalErrors: like 'errors', but errors in this list will always
1569     terminate the connection, despite being of a recognizable error type.
1570
1571     @cvar commandType: The type of Box used to issue commands; useful only for
1572     protocol-modifying behavior like startTLS or protocol switching.  Defaults
1573     to a plain vanilla L{Box}.
1574
1575     @cvar responseType: The type of Box used to respond to this command; only
1576     useful for protocol-modifying behavior like startTLS or protocol switching.
1577     Defaults to a plain vanilla L{Box}.
1578
1579     @ivar requiresAnswer: a boolean; defaults to True.  Set it to False on your
1580     subclass if you want callRemote to return None.  Note: this is a hint only
1581     to the client side of the protocol.  The return-type of a command responder
1582     method must always be a dictionary adhering to the contract specified by
1583     L{response}, because clients are always free to request a response if they
1584     want one.
1585     """
1586
1587     class __metaclass__(type):
1588         """
1589         Metaclass hack to establish reverse-mappings for 'errors' and
1590         'fatalErrors' as class vars.
1591         """
1592         def __new__(cls, name, bases, attrs):
1593             reverseErrors = attrs['reverseErrors'] = {}
1594             er = attrs['allErrors'] = {}
1595             if 'commandName' not in attrs:
1596                 attrs['commandName'] = name
1597             newtype = type.__new__(cls, name, bases, attrs)
1598             errors = {}
1599             fatalErrors = {}
1600             accumulateClassDict(newtype, 'errors', errors)
1601             accumulateClassDict(newtype, 'fatalErrors', fatalErrors)
1602             for v, k in errors.iteritems():
1603                 reverseErrors[k] = v
1604                 er[v] = k
1605             for v, k in fatalErrors.iteritems():
1606                 reverseErrors[k] = v
1607                 er[v] = k
1608             return newtype
1609
1610     arguments = []
1611     response = []
1612     extra = []
1613     errors = {}
1614     fatalErrors = {}
1615
1616     commandType = Box
1617     responseType = Box
1618
1619     requiresAnswer = True
1620
1621
1622     def __init__(self, **kw):
1623         """
1624         Create an instance of this command with specified values for its
1625         parameters.
1626
1627         @param kw: a dict containing an appropriate value for each name
1628         specified in the L{arguments} attribute of my class.
1629
1630         @raise InvalidSignature: if you forgot any required arguments.
1631         """
1632         self.structured = kw
1633         givenArgs = kw.keys()
1634         forgotten = []
1635         for name, arg in self.arguments:
1636             pythonName = _wireNameToPythonIdentifier(name)
1637             if pythonName not in givenArgs and not arg.optional:
1638                 forgotten.append(pythonName)
1639         if forgotten:
1640             raise InvalidSignature("forgot %s for %s" % (
1641                     ', '.join(forgotten), self.commandName))
1642         forgotten = []
1643
1644
1645     def makeResponse(cls, objects, proto):
1646         """
1647         Serialize a mapping of arguments using this L{Command}'s
1648         response schema.
1649
1650         @param objects: a dict with keys matching the names specified in
1651         self.response, having values of the types that the Argument objects in
1652         self.response can format.
1653
1654         @param proto: an L{AMP}.
1655
1656         @return: an L{AmpBox}.
1657         """
1658         try:
1659             responseType = cls.responseType()
1660         except:
1661             return fail()
1662         return _objectsToStrings(objects, cls.response, responseType, proto)
1663     makeResponse = classmethod(makeResponse)
1664
1665
1666     def makeArguments(cls, objects, proto):
1667         """
1668         Serialize a mapping of arguments using this L{Command}'s
1669         argument schema.
1670
1671         @param objects: a dict with keys similar to the names specified in
1672         self.arguments, having values of the types that the Argument objects in
1673         self.arguments can parse.
1674
1675         @param proto: an L{AMP}.
1676
1677         @return: An instance of this L{Command}'s C{commandType}.
1678         """
1679         allowedNames = set()
1680         for (argName, ignored) in cls.arguments:
1681             allowedNames.add(_wireNameToPythonIdentifier(argName))
1682
1683         for intendedArg in objects:
1684             if intendedArg not in allowedNames:
1685                 raise InvalidSignature(
1686                     "%s is not a valid argument" % (intendedArg,))
1687         return _objectsToStrings(objects, cls.arguments, cls.commandType(),
1688                                  proto)
1689     makeArguments = classmethod(makeArguments)
1690
1691
1692     def parseResponse(cls, box, protocol):
1693         """
1694         Parse a mapping of serialized arguments using this
1695         L{Command}'s response schema.
1696
1697         @param box: A mapping of response-argument names to the
1698         serialized forms of those arguments.
1699         @param protocol: The L{AMP} protocol.
1700
1701         @return: A mapping of response-argument names to the parsed
1702         forms.
1703         """
1704         return _stringsToObjects(box, cls.response, protocol)
1705     parseResponse = classmethod(parseResponse)
1706
1707
1708     def parseArguments(cls, box, protocol):
1709         """
1710         Parse a mapping of serialized arguments using this
1711         L{Command}'s argument schema.
1712
1713         @param box: A mapping of argument names to the seralized forms
1714         of those arguments.
1715         @param protocol: The L{AMP} protocol.
1716
1717         @return: A mapping of argument names to the parsed forms.
1718         """
1719         return _stringsToObjects(box, cls.arguments, protocol)
1720     parseArguments = classmethod(parseArguments)
1721
1722
1723     def responder(cls, methodfunc):
1724         """
1725         Declare a method to be a responder for a particular command.
1726
1727         This is a decorator.
1728
1729         Use like so::
1730
1731             class MyCommand(Command):
1732                 arguments = [('a', ...), ('b', ...)]
1733
1734             class MyProto(AMP):
1735                 def myFunMethod(self, a, b):
1736                     ...
1737                 MyCommand.responder(myFunMethod)
1738
1739         Notes: Although decorator syntax is not used within Twisted, this
1740         function returns its argument and is therefore safe to use with
1741         decorator syntax.
1742
1743         This is not thread safe.  Don't declare AMP subclasses in other
1744         threads.  Don't declare responders outside the scope of AMP subclasses;
1745         the behavior is undefined.
1746
1747         @param methodfunc: A function which will later become a method, which
1748         has a keyword signature compatible with this command's L{argument} list
1749         and returns a dictionary with a set of keys compatible with this
1750         command's L{response} list.
1751
1752         @return: the methodfunc parameter.
1753         """
1754         CommandLocator._currentClassCommands.append((cls, methodfunc))
1755         return methodfunc
1756     responder = classmethod(responder)
1757
1758
1759     # Our only instance method
1760     def _doCommand(self, proto):
1761         """
1762         Encode and send this Command to the given protocol.
1763
1764         @param proto: an AMP, representing the connection to send to.
1765
1766         @return: a Deferred which will fire or error appropriately when the
1767         other side responds to the command (or error if the connection is lost
1768         before it is responded to).
1769         """
1770
1771         def _massageError(error):
1772             error.trap(RemoteAmpError)
1773             rje = error.value
1774             errorType = self.reverseErrors.get(rje.errorCode,
1775                                                UnknownRemoteError)
1776             return Failure(errorType(rje.description))
1777
1778         d = proto._sendBoxCommand(self.commandName,
1779                                   self.makeArguments(self.structured, proto),
1780                                   self.requiresAnswer)
1781
1782         if self.requiresAnswer:
1783             d.addCallback(self.parseResponse, proto)
1784             d.addErrback(_massageError)
1785
1786         return d
1787
1788
1789
1790 class _NoCertificate:
1791     """
1792     This is for peers which don't want to use a local certificate.  Used by
1793     AMP because AMP's internal language is all about certificates and this
1794     duck-types in the appropriate place; this API isn't really stable though,
1795     so it's not exposed anywhere public.
1796
1797     For clients, it will use ephemeral DH keys, or whatever the default is for
1798     certificate-less clients in OpenSSL.  For servers, it will generate a
1799     temporary self-signed certificate with garbage values in the DN and use
1800     that.
1801     """
1802
1803     def __init__(self, client):
1804         """
1805         Create a _NoCertificate which either is or isn't for the client side of
1806         the connection.
1807
1808         @param client: True if we are a client and should truly have no
1809         certificate and be anonymous, False if we are a server and actually
1810         have to generate a temporary certificate.
1811
1812         @type client: bool
1813         """
1814         self.client = client
1815
1816
1817     def options(self, *authorities):
1818         """
1819         Behaves like L{twisted.internet.ssl.PrivateCertificate.options}().
1820         """
1821         if not self.client:
1822             # do some crud with sslverify to generate a temporary self-signed
1823             # certificate.  This is SLOOOWWWWW so it is only in the absolute
1824             # worst, most naive case.
1825
1826             # We have to do this because OpenSSL will not let both the server
1827             # and client be anonymous.
1828             sharedDN = DN(CN='TEMPORARY CERTIFICATE')
1829             key = KeyPair.generate()
1830             cr = key.certificateRequest(sharedDN)
1831             sscrd = key.signCertificateRequest(sharedDN, cr, lambda dn: True, 1)
1832             cert = key.newCertificate(sscrd)
1833             return cert.options(*authorities)
1834         options = dict()
1835         if authorities:
1836             options.update(dict(verify=True,
1837                                 requireCertificate=True,
1838                                 caCerts=[auth.original for auth in authorities]))
1839         occo = CertificateOptions(**options)
1840         return occo
1841
1842
1843
1844 class _TLSBox(AmpBox):
1845     """
1846     I am an AmpBox that, upon being sent, initiates a TLS connection.
1847     """
1848     __slots__ = []
1849
1850     def __init__(self):
1851         if ssl is None:
1852             raise RemoteAmpError("TLS_ERROR", "TLS not available")
1853         AmpBox.__init__(self)
1854
1855
1856     def _keyprop(k, default):
1857         return property(lambda self: self.get(k, default))
1858
1859
1860     # These properties are described in startTLS
1861     certificate = _keyprop('tls_localCertificate', _NoCertificate(False))
1862     verify = _keyprop('tls_verifyAuthorities', None)
1863
1864     def _sendTo(self, proto):
1865         """
1866         Send my encoded value to the protocol, then initiate TLS.
1867         """
1868         ab = AmpBox(self)
1869         for k in ['tls_localCertificate',
1870                   'tls_verifyAuthorities']:
1871             ab.pop(k, None)
1872         ab._sendTo(proto)
1873         proto._startTLS(self.certificate, self.verify)
1874
1875
1876
1877 class _LocalArgument(String):
1878     """
1879     Local arguments are never actually relayed across the wire.  This is just a
1880     shim so that StartTLS can pretend to have some arguments: if arguments
1881     acquire documentation properties, replace this with something nicer later.
1882     """
1883
1884     def fromBox(self, name, strings, objects, proto):
1885         pass
1886
1887
1888
1889 class StartTLS(Command):
1890     """
1891     Use, or subclass, me to implement a command that starts TLS.
1892
1893     Callers of StartTLS may pass several special arguments, which affect the
1894     TLS negotiation:
1895
1896         - tls_localCertificate: This is a
1897         twisted.internet.ssl.PrivateCertificate which will be used to secure
1898         the side of the connection it is returned on.
1899
1900         - tls_verifyAuthorities: This is a list of
1901         twisted.internet.ssl.Certificate objects that will be used as the
1902         certificate authorities to verify our peer's certificate.
1903
1904     Each of those special parameters may also be present as a key in the
1905     response dictionary.
1906     """
1907
1908     arguments = [("tls_localCertificate", _LocalArgument(optional=True)),
1909                  ("tls_verifyAuthorities", _LocalArgument(optional=True))]
1910
1911     response = [("tls_localCertificate", _LocalArgument(optional=True)),
1912                 ("tls_verifyAuthorities", _LocalArgument(optional=True))]
1913
1914     responseType = _TLSBox
1915
1916     def __init__(self, **kw):
1917         """
1918         Create a StartTLS command.  (This is private.  Use AMP.callRemote.)
1919
1920         @param tls_localCertificate: the PrivateCertificate object to use to
1921         secure the connection.  If it's None, or unspecified, an ephemeral DH
1922         key is used instead.
1923
1924         @param tls_verifyAuthorities: a list of Certificate objects which
1925         represent root certificates to verify our peer with.
1926         """
1927         if ssl is None:
1928             raise RuntimeError("TLS not available.")
1929         self.certificate = kw.pop('tls_localCertificate', _NoCertificate(True))
1930         self.authorities = kw.pop('tls_verifyAuthorities', None)
1931         Command.__init__(self, **kw)
1932
1933
1934     def _doCommand(self, proto):
1935         """
1936         When a StartTLS command is sent, prepare to start TLS, but don't actually
1937         do it; wait for the acknowledgement, then initiate the TLS handshake.
1938         """
1939         d = Command._doCommand(self, proto)
1940         proto._prepareTLS(self.certificate, self.authorities)
1941         # XXX before we get back to user code we are going to start TLS...
1942         def actuallystart(response):
1943             proto._startTLS(self.certificate, self.authorities)
1944             return response
1945         d.addCallback(actuallystart)
1946         return d
1947
1948
1949
1950 class ProtocolSwitchCommand(Command):
1951     """
1952     Use this command to switch from something Amp-derived to a different
1953     protocol mid-connection.  This can be useful to use amp as the
1954     connection-startup negotiation phase.  Since TLS is a different layer
1955     entirely, you can use Amp to negotiate the security parameters of your
1956     connection, then switch to a different protocol, and the connection will
1957     remain secured.
1958     """
1959
1960     def __init__(self, _protoToSwitchToFactory, **kw):
1961         """
1962         Create a ProtocolSwitchCommand.
1963
1964         @param _protoToSwitchToFactory: a ProtocolFactory which will generate
1965         the Protocol to switch to.
1966
1967         @param kw: Keyword arguments, encoded and handled normally as
1968         L{Command} would.
1969         """
1970
1971         self.protoToSwitchToFactory = _protoToSwitchToFactory
1972         super(ProtocolSwitchCommand, self).__init__(**kw)
1973
1974
1975     def makeResponse(cls, innerProto, proto):
1976         return _SwitchBox(innerProto)
1977     makeResponse = classmethod(makeResponse)
1978
1979
1980     def _doCommand(self, proto):
1981         """
1982         When we emit a ProtocolSwitchCommand, lock the protocol, but don't actually
1983         switch to the new protocol unless an acknowledgement is received.  If
1984         an error is received, switch back.
1985         """
1986         d = super(ProtocolSwitchCommand, self)._doCommand(proto)
1987         proto._lockForSwitch()
1988         def switchNow(ign):
1989             innerProto = self.protoToSwitchToFactory.buildProtocol(
1990                 proto.transport.getPeer())
1991             proto._switchTo(innerProto, self.protoToSwitchToFactory)
1992             return ign
1993         def handle(ign):
1994             proto._unlockFromSwitch()
1995             self.protoToSwitchToFactory.clientConnectionFailed(
1996                 None, Failure(CONNECTION_LOST))
1997             return ign
1998         return d.addCallbacks(switchNow, handle)
1999
2000
2001
2002 class _DescriptorExchanger(object):
2003     """
2004     L{_DescriptorExchanger} is a mixin for L{BinaryBoxProtocol} which adds
2005     support for receiving file descriptors, a feature offered by
2006     L{IUNIXTransport<twisted.internet.interfaces.IUNIXTransport>}.
2007
2008     @ivar _descriptors: Temporary storage for all file descriptors received.
2009         Values in this dictionary are the file descriptors (as integers).  Keys
2010         in this dictionary are ordinals giving the order in which each
2011         descriptor was received.  The ordering information is used to allow
2012         L{Descriptor} to determine which is the correct descriptor for any
2013         particular usage of that argument type.
2014     @type _descriptors: C{dict}
2015
2016     @ivar _sendingDescriptorCounter: A no-argument callable which returns the
2017         ordinals, starting from 0.  This is used to construct values for
2018         C{_sendFileDescriptor}.
2019
2020     @ivar _receivingDescriptorCounter: A no-argument callable which returns the
2021         ordinals, starting from 0.  This is used to construct values for
2022         C{fileDescriptorReceived}.
2023     """
2024     implements(IFileDescriptorReceiver)
2025
2026     def __init__(self):
2027         self._descriptors = {}
2028         self._getDescriptor = self._descriptors.pop
2029         self._sendingDescriptorCounter = count().next
2030         self._receivingDescriptorCounter = count().next
2031
2032
2033     def _sendFileDescriptor(self, descriptor):
2034         """
2035         Assign and return the next ordinal to the given descriptor after sending
2036         the descriptor over this protocol's transport.
2037         """
2038         self.transport.sendFileDescriptor(descriptor)
2039         return self._sendingDescriptorCounter()
2040
2041
2042     def fileDescriptorReceived(self, descriptor):
2043         """
2044         Collect received file descriptors to be claimed later by L{Descriptor}.
2045
2046         @param descriptor: The received file descriptor.
2047         @type descriptor: C{int}
2048         """
2049         self._descriptors[self._receivingDescriptorCounter()] = descriptor
2050
2051
2052
2053 class BinaryBoxProtocol(StatefulStringProtocol, Int16StringReceiver,
2054                         _DescriptorExchanger):
2055     """
2056     A protocol for receiving L{AmpBox}es - key/value pairs - via length-prefixed
2057     strings.  A box is composed of:
2058
2059         - any number of key-value pairs, described by:
2060             - a 2-byte network-endian packed key length (of which the first
2061               byte must be null, and the second must be non-null: i.e. the
2062               value of the length must be 1-255)
2063             - a key, comprised of that many bytes
2064             - a 2-byte network-endian unsigned value length (up to the maximum
2065               of 65535)
2066             - a value, comprised of that many bytes
2067         - 2 null bytes
2068
2069     In other words, an even number of strings prefixed with packed unsigned
2070     16-bit integers, and then a 0-length string to indicate the end of the box.
2071
2072     This protocol also implements 2 extra private bits of functionality related
2073     to the byte boundaries between messages; it can start TLS between two given
2074     boxes or switch to an entirely different protocol.  However, due to some
2075     tricky elements of the implementation, the public interface to this
2076     functionality is L{ProtocolSwitchCommand} and L{StartTLS}.
2077
2078     @ivar _keyLengthLimitExceeded: A flag which is only true when the
2079         connection is being closed because a key length prefix which was longer
2080         than allowed by the protocol was received.
2081
2082     @ivar boxReceiver: an L{IBoxReceiver} provider, whose L{ampBoxReceived}
2083     method will be invoked for each L{AmpBox} that is received.
2084     """
2085
2086     implements(IBoxSender)
2087
2088     _justStartedTLS = False
2089     _startingTLSBuffer = None
2090     _locked = False
2091     _currentKey = None
2092     _currentBox = None
2093
2094     _keyLengthLimitExceeded = False
2095
2096     hostCertificate = None
2097     noPeerCertificate = False   # for tests
2098     innerProtocol = None
2099     innerProtocolClientFactory = None
2100
2101     def __init__(self, boxReceiver):
2102         _DescriptorExchanger.__init__(self)
2103         self.boxReceiver = boxReceiver
2104
2105
2106     def _switchTo(self, newProto, clientFactory=None):
2107         """
2108         Switch this BinaryBoxProtocol's transport to a new protocol.  You need
2109         to do this 'simultaneously' on both ends of a connection; the easiest
2110         way to do this is to use a subclass of ProtocolSwitchCommand.
2111
2112         @param newProto: the new protocol instance to switch to.
2113
2114         @param clientFactory: the ClientFactory to send the
2115         L{clientConnectionLost} notification to.
2116         """
2117         # All the data that Int16Receiver has not yet dealt with belongs to our
2118         # new protocol: luckily it's keeping that in a handy (although
2119         # ostensibly internal) variable for us:
2120         newProtoData = self.recvd
2121         # We're quite possibly in the middle of a 'dataReceived' loop in
2122         # Int16StringReceiver: let's make sure that the next iteration, the
2123         # loop will break and not attempt to look at something that isn't a
2124         # length prefix.
2125         self.recvd = ''
2126         # Finally, do the actual work of setting up the protocol and delivering
2127         # its first chunk of data, if one is available.
2128         self.innerProtocol = newProto
2129         self.innerProtocolClientFactory = clientFactory
2130         newProto.makeConnection(self.transport)
2131         if newProtoData:
2132             newProto.dataReceived(newProtoData)
2133
2134
2135     def sendBox(self, box):
2136         """
2137         Send a amp.Box to my peer.
2138
2139         Note: transport.write is never called outside of this method.
2140
2141         @param box: an AmpBox.
2142
2143         @raise ProtocolSwitched: if the protocol has previously been switched.
2144
2145         @raise ConnectionLost: if the connection has previously been lost.
2146         """
2147         if self._locked:
2148             raise ProtocolSwitched(
2149                 "This connection has switched: no AMP traffic allowed.")
2150         if self.transport is None:
2151             raise ConnectionLost()
2152         if self._startingTLSBuffer is not None:
2153             self._startingTLSBuffer.append(box)
2154         else:
2155             self.transport.write(box.serialize())
2156
2157
2158     def makeConnection(self, transport):
2159         """
2160         Notify L{boxReceiver} that it is about to receive boxes from this
2161         protocol by invoking L{startReceivingBoxes}.
2162         """
2163         self.transport = transport
2164         self.boxReceiver.startReceivingBoxes(self)
2165         self.connectionMade()
2166
2167
2168     def dataReceived(self, data):
2169         """
2170         Either parse incoming data as L{AmpBox}es or relay it to our nested
2171         protocol.
2172         """
2173         if self._justStartedTLS:
2174             self._justStartedTLS = False
2175         # If we already have an inner protocol, then we don't deliver data to
2176         # the protocol parser any more; we just hand it off.
2177         if self.innerProtocol is not None:
2178             self.innerProtocol.dataReceived(data)
2179             return
2180         return Int16StringReceiver.dataReceived(self, data)
2181
2182
2183     def connectionLost(self, reason):
2184         """
2185         The connection was lost; notify any nested protocol.
2186         """
2187         if self.innerProtocol is not None:
2188             self.innerProtocol.connectionLost(reason)
2189             if self.innerProtocolClientFactory is not None:
2190                 self.innerProtocolClientFactory.clientConnectionLost(None, reason)
2191         if self._keyLengthLimitExceeded:
2192             failReason = Failure(TooLong(True, False, None, None))
2193         elif reason.check(ConnectionClosed) and self._justStartedTLS:
2194             # We just started TLS and haven't received any data.  This means
2195             # the other connection didn't like our cert (although they may not
2196             # have told us why - later Twisted should make 'reason' into a TLS
2197             # error.)
2198             failReason = PeerVerifyError(
2199                 "Peer rejected our certificate for an unknown reason.")
2200         else:
2201             failReason = reason
2202         self.boxReceiver.stopReceivingBoxes(failReason)
2203
2204
2205     # The longest key allowed
2206     _MAX_KEY_LENGTH = 255
2207
2208     # The longest value allowed (this is somewhat redundant, as longer values
2209     # cannot be encoded - ah well).
2210     _MAX_VALUE_LENGTH = 65535
2211
2212     # The first thing received is a key.
2213     MAX_LENGTH = _MAX_KEY_LENGTH
2214
2215     def proto_init(self, string):
2216         """
2217         String received in the 'init' state.
2218         """
2219         self._currentBox = AmpBox()
2220         return self.proto_key(string)
2221
2222
2223     def proto_key(self, string):
2224         """
2225         String received in the 'key' state.  If the key is empty, a complete
2226         box has been received.
2227         """
2228         if string:
2229             self._currentKey = string
2230             self.MAX_LENGTH = self._MAX_VALUE_LENGTH
2231             return 'value'
2232         else:
2233             self.boxReceiver.ampBoxReceived(self._currentBox)
2234             self._currentBox = None
2235             return 'init'
2236
2237
2238     def proto_value(self, string):
2239         """
2240         String received in the 'value' state.
2241         """
2242         self._currentBox[self._currentKey] = string
2243         self._currentKey = None
2244         self.MAX_LENGTH = self._MAX_KEY_LENGTH
2245         return 'key'
2246
2247
2248     def lengthLimitExceeded(self, length):
2249         """
2250         The key length limit was exceeded.  Disconnect the transport and make
2251         sure a meaningful exception is reported.
2252         """
2253         self._keyLengthLimitExceeded = True
2254         self.transport.loseConnection()
2255
2256
2257     def _lockForSwitch(self):
2258         """
2259         Lock this binary protocol so that no further boxes may be sent.  This
2260         is used when sending a request to switch underlying protocols.  You
2261         probably want to subclass ProtocolSwitchCommand rather than calling
2262         this directly.
2263         """
2264         self._locked = True
2265
2266
2267     def _unlockFromSwitch(self):
2268         """
2269         Unlock this locked binary protocol so that further boxes may be sent
2270         again.  This is used after an attempt to switch protocols has failed
2271         for some reason.
2272         """
2273         if self.innerProtocol is not None:
2274             raise ProtocolSwitched("Protocol already switched.  Cannot unlock.")
2275         self._locked = False
2276
2277
2278     def _prepareTLS(self, certificate, verifyAuthorities):
2279         """
2280         Used by StartTLSCommand to put us into the state where we don't
2281         actually send things that get sent, instead we buffer them.  see
2282         L{_sendBox}.
2283         """
2284         self._startingTLSBuffer = []
2285         if self.hostCertificate is not None:
2286             raise OnlyOneTLS(
2287                 "Previously authenticated connection between %s and %s "
2288                 "is trying to re-establish as %s" % (
2289                     self.hostCertificate,
2290                     self.peerCertificate,
2291                     (certificate, verifyAuthorities)))
2292
2293
2294     def _startTLS(self, certificate, verifyAuthorities):
2295         """
2296         Used by TLSBox to initiate the SSL handshake.
2297
2298         @param certificate: a L{twisted.internet.ssl.PrivateCertificate} for
2299         use locally.
2300
2301         @param verifyAuthorities: L{twisted.internet.ssl.Certificate} instances
2302         representing certificate authorities which will verify our peer.
2303         """
2304         self.hostCertificate = certificate
2305         self._justStartedTLS = True
2306         if verifyAuthorities is None:
2307             verifyAuthorities = ()
2308         self.transport.startTLS(certificate.options(*verifyAuthorities))
2309         stlsb = self._startingTLSBuffer
2310         if stlsb is not None:
2311             self._startingTLSBuffer = None
2312             for box in stlsb:
2313                 self.sendBox(box)
2314
2315
2316     def _getPeerCertificate(self):
2317         if self.noPeerCertificate:
2318             return None
2319         return Certificate.peerFromTransport(self.transport)
2320     peerCertificate = property(_getPeerCertificate)
2321
2322
2323     def unhandledError(self, failure):
2324         """
2325         The buck stops here.  This error was completely unhandled, time to
2326         terminate the connection.
2327         """
2328         log.err(
2329             failure,
2330             "Amp server or network failure unhandled by client application.  "
2331             "Dropping connection!  To avoid, add errbacks to ALL remote "
2332             "commands!")
2333         if self.transport is not None:
2334             self.transport.loseConnection()
2335
2336
2337     def _defaultStartTLSResponder(self):
2338         """
2339         The default TLS responder doesn't specify any certificate or anything.
2340
2341         From a security perspective, it's little better than a plain-text
2342         connection - but it is still a *bit* better, so it's included for
2343         convenience.
2344
2345         You probably want to override this by providing your own StartTLS.responder.
2346         """
2347         return {}
2348     StartTLS.responder(_defaultStartTLSResponder)
2349
2350
2351
2352 class AMP(BinaryBoxProtocol, BoxDispatcher,
2353           CommandLocator, SimpleStringLocator):
2354     """
2355     This protocol is an AMP connection.  See the module docstring for protocol
2356     details.
2357     """
2358
2359     _ampInitialized = False
2360
2361     def __init__(self, boxReceiver=None, locator=None):
2362         # For backwards compatibility.  When AMP did not separate parsing logic
2363         # (L{BinaryBoxProtocol}), request-response logic (L{BoxDispatcher}) and
2364         # command routing (L{CommandLocator}), it did not have a constructor.
2365         # Now it does, so old subclasses might have defined their own that did
2366         # not upcall.  If this flag isn't set, we'll call the constructor in
2367         # makeConnection before anything actually happens.
2368         self._ampInitialized = True
2369         if boxReceiver is None:
2370             boxReceiver = self
2371         if locator is None:
2372             locator = self
2373         BoxDispatcher.__init__(self, locator)
2374         BinaryBoxProtocol.__init__(self, boxReceiver)
2375
2376
2377     def locateResponder(self, name):
2378         """
2379         Unify the implementations of L{CommandLocator} and
2380         L{SimpleStringLocator} to perform both kinds of dispatch, preferring
2381         L{CommandLocator}.
2382         """
2383         firstResponder = CommandLocator.locateResponder(self, name)
2384         if firstResponder is not None:
2385             return firstResponder
2386         secondResponder = SimpleStringLocator.locateResponder(self, name)
2387         return secondResponder
2388
2389
2390     def __repr__(self):
2391         """
2392         A verbose string representation which gives us information about this
2393         AMP connection.
2394         """
2395         if self.innerProtocol is not None:
2396             innerRepr = ' inner %r' % (self.innerProtocol,)
2397         else:
2398             innerRepr = ''
2399         return '<%s%s at 0x%x>' % (
2400             self.__class__.__name__, innerRepr, unsignedID(self))
2401
2402
2403     def makeConnection(self, transport):
2404         """
2405         Emit a helpful log message when the connection is made.
2406         """
2407         if not self._ampInitialized:
2408             # See comment in the constructor re: backward compatibility.  I
2409             # should probably emit a deprecation warning here.
2410             AMP.__init__(self)
2411         # Save these so we can emit a similar log message in L{connectionLost}.
2412         self._transportPeer = transport.getPeer()
2413         self._transportHost = transport.getHost()
2414         log.msg("%s connection established (HOST:%s PEER:%s)" % (
2415                 self.__class__.__name__,
2416                 self._transportHost,
2417                 self._transportPeer))
2418         BinaryBoxProtocol.makeConnection(self, transport)
2419
2420
2421     def connectionLost(self, reason):
2422         """
2423         Emit a helpful log message when the connection is lost.
2424         """
2425         log.msg("%s connection lost (HOST:%s PEER:%s)" %
2426                 (self.__class__.__name__,
2427                  self._transportHost,
2428                  self._transportPeer))
2429         BinaryBoxProtocol.connectionLost(self, reason)
2430         self.transport = None
2431
2432
2433
2434 class _ParserHelper:
2435     """
2436     A box receiver which records all boxes received.
2437     """
2438     def __init__(self):
2439         self.boxes = []
2440
2441
2442     def getPeer(self):
2443         return 'string'
2444
2445
2446     def getHost(self):
2447         return 'string'
2448
2449     disconnecting = False
2450
2451
2452     def startReceivingBoxes(self, sender):
2453         """
2454         No initialization is required.
2455         """
2456
2457
2458     def ampBoxReceived(self, box):
2459         self.boxes.append(box)
2460
2461
2462     # Synchronous helpers
2463     def parse(cls, fileObj):
2464         """
2465         Parse some amp data stored in a file.
2466
2467         @param fileObj: a file-like object.
2468
2469         @return: a list of AmpBoxes encoded in the given file.
2470         """
2471         parserHelper = cls()
2472         bbp = BinaryBoxProtocol(boxReceiver=parserHelper)
2473         bbp.makeConnection(parserHelper)
2474         bbp.dataReceived(fileObj.read())
2475         return parserHelper.boxes
2476     parse = classmethod(parse)
2477
2478
2479     def parseString(cls, data):
2480         """
2481         Parse some amp data stored in a string.
2482
2483         @param data: a str holding some amp-encoded data.
2484
2485         @return: a list of AmpBoxes encoded in the given string.
2486         """
2487         return cls.parse(StringIO(data))
2488     parseString = classmethod(parseString)
2489
2490
2491
2492 parse = _ParserHelper.parse
2493 parseString = _ParserHelper.parseString
2494
2495 def _stringsToObjects(strings, arglist, proto):
2496     """
2497     Convert an AmpBox to a dictionary of python objects, converting through a
2498     given arglist.
2499
2500     @param strings: an AmpBox (or dict of strings)
2501
2502     @param arglist: a list of 2-tuples of strings and Argument objects, as
2503     described in L{Command.arguments}.
2504
2505     @param proto: an L{AMP} instance.
2506
2507     @return: the converted dictionary mapping names to argument objects.
2508     """
2509     objects = {}
2510     myStrings = strings.copy()
2511     for argname, argparser in arglist:
2512         argparser.fromBox(argname, myStrings, objects, proto)
2513     return objects
2514
2515
2516
2517 def _objectsToStrings(objects, arglist, strings, proto):
2518     """
2519     Convert a dictionary of python objects to an AmpBox, converting through a
2520     given arglist.
2521
2522     @param objects: a dict mapping names to python objects
2523
2524     @param arglist: a list of 2-tuples of strings and Argument objects, as
2525     described in L{Command.arguments}.
2526
2527     @param strings: [OUT PARAMETER] An object providing the L{dict}
2528     interface which will be populated with serialized data.
2529
2530     @param proto: an L{AMP} instance.
2531
2532     @return: The converted dictionary mapping names to encoded argument
2533     strings (identical to C{strings}).
2534     """
2535     myObjects = objects.copy()
2536     for argname, argparser in arglist:
2537         argparser.toBox(argname, strings, myObjects, proto)
2538     return strings
2539
2540
2541
2542 class _FixedOffsetTZInfo(datetime.tzinfo):
2543     """
2544     Represents a fixed timezone offset (without daylight saving time).
2545
2546     @ivar name: A C{str} giving the name of this timezone; the name just
2547         includes how much time this offset represents.
2548
2549     @ivar offset: A C{datetime.timedelta} giving the amount of time this
2550         timezone is offset.
2551     """
2552
2553     def __init__(self, sign, hours, minutes):
2554         self.name = '%s%02i:%02i' % (sign, hours, minutes)
2555         if sign == '-':
2556             hours = -hours
2557             minutes = -minutes
2558         elif sign != '+':
2559             raise ValueError('invalid sign for timezone %r' % (sign,))
2560         self.offset = datetime.timedelta(hours=hours, minutes=minutes)
2561
2562
2563     def utcoffset(self, dt):
2564         """
2565         Return this timezone's offset from UTC.
2566         """
2567         return self.offset
2568
2569
2570     def dst(self, dt):
2571         """
2572         Return a zero C{datetime.timedelta} for the daylight saving time offset,
2573         since there is never one.
2574         """
2575         return datetime.timedelta(0)
2576
2577
2578     def tzname(self, dt):
2579         """
2580         Return a string describing this timezone.
2581         """
2582         return self.name
2583
2584
2585
2586 utc = _FixedOffsetTZInfo('+', 0, 0)
2587
2588
2589
2590 class Decimal(Argument):
2591     """
2592     Encodes C{decimal.Decimal} instances.
2593
2594     There are several ways in which a decimal value might be encoded.
2595
2596     Special values are encoded as special strings::
2597
2598       - Positive infinity is encoded as C{"Infinity"}
2599       - Negative infinity is encoded as C{"-Infinity"}
2600       - Quiet not-a-number is encoded as either C{"NaN"} or C{"-NaN"}
2601       - Signalling not-a-number is encoded as either C{"sNaN"} or C{"-sNaN"}
2602
2603     Normal values are encoded using the base ten string representation, using
2604     engineering notation to indicate magnitude without precision, and "normal"
2605     digits to indicate precision.  For example::
2606
2607       - C{"1"} represents the value I{1} with precision to one place.
2608       - C{"-1"} represents the value I{-1} with precision to one place.
2609       - C{"1.0"} represents the value I{1} with precision to two places.
2610       - C{"10"} represents the value I{10} with precision to two places.
2611       - C{"1E+2"} represents the value I{10} with precision to one place.
2612       - C{"1E-1"} represents the value I{0.1} with precision to one place.
2613       - C{"1.5E+2"} represents the value I{15} with precision to two places.
2614
2615     U{http://speleotrove.com/decimal/} should be considered the authoritative
2616     specification for the format.
2617     """
2618     fromString = decimal.Decimal
2619
2620     def toString(self, inObject):
2621         """
2622         Serialize a C{decimal.Decimal} instance to the specified wire format.
2623         """
2624         if isinstance(inObject, decimal.Decimal):
2625             # Hopefully decimal.Decimal.__str__ actually does what we want.
2626             return str(inObject)
2627         raise ValueError(
2628             "amp.Decimal can only encode instances of decimal.Decimal")
2629
2630
2631
2632 class DateTime(Argument):
2633     """
2634     Encodes C{datetime.datetime} instances.
2635
2636     Wire format: '%04i-%02i-%02iT%02i:%02i:%02i.%06i%s%02i:%02i'. Fields in
2637     order are: year, month, day, hour, minute, second, microsecond, timezone
2638     direction (+ or -), timezone hour, timezone minute. Encoded string is
2639     always exactly 32 characters long. This format is compatible with ISO 8601,
2640     but that does not mean all ISO 8601 dates can be accepted.
2641
2642     Also, note that the datetime module's notion of a "timezone" can be
2643     complex, but the wire format includes only a fixed offset, so the
2644     conversion is not lossless. A lossless transmission of a C{datetime} instance
2645     is not feasible since the receiving end would require a Python interpreter.
2646
2647     @ivar _positions: A sequence of slices giving the positions of various
2648         interesting parts of the wire format.
2649     """
2650
2651     _positions = [
2652         slice(0, 4), slice(5, 7), slice(8, 10), # year, month, day
2653         slice(11, 13), slice(14, 16), slice(17, 19), # hour, minute, second
2654         slice(20, 26), # microsecond
2655         # intentionally skip timezone direction, as it is not an integer
2656         slice(27, 29), slice(30, 32) # timezone hour, timezone minute
2657         ]
2658
2659     def fromString(self, s):
2660         """
2661         Parse a string containing a date and time in the wire format into a
2662         C{datetime.datetime} instance.
2663         """
2664         if len(s) != 32:
2665             raise ValueError('invalid date format %r' % (s,))
2666
2667         values = [int(s[p]) for p in self._positions]
2668         sign = s[26]
2669         timezone = _FixedOffsetTZInfo(sign, *values[7:])
2670         values[7:] = [timezone]
2671         return datetime.datetime(*values)
2672
2673
2674     def toString(self, i):
2675         """
2676         Serialize a C{datetime.datetime} instance to a string in the specified
2677         wire format.
2678         """
2679         offset = i.utcoffset()
2680         if offset is None:
2681             raise ValueError(
2682                 'amp.DateTime cannot serialize naive datetime instances.  '
2683                 'You may find amp.utc useful.')
2684
2685         minutesOffset = (offset.days * 86400 + offset.seconds) // 60
2686
2687         if minutesOffset > 0:
2688             sign = '+'
2689         else:
2690             sign = '-'
2691
2692         # strftime has no way to format the microseconds, or put a ':' in the
2693         # timezone. Suprise!
2694
2695         return '%04i-%02i-%02iT%02i:%02i:%02i.%06i%s%02i:%02i' % (
2696             i.year,
2697             i.month,
2698             i.day,
2699             i.hour,
2700             i.minute,
2701             i.second,
2702             i.microsecond,
2703             sign,
2704             abs(minutesOffset) // 60,
2705             abs(minutesOffset) % 60)