def applyAttributes(self, obj, attrs, **kwargs): """ Applies C{attrs} to C{obj}. Since C{blobstore.BlobInfo} objects are read-only entities, we only care about the C{key} attribute. """ assert type(obj) is BlobInfoStub key = attrs.pop('key', None) if not key: raise pyamf.DecodeError( "Unable to build blobstore.BlobInfo instance. Missing 'key' " "attribute." ) try: key = blobstore.BlobKey(key) except: raise pyamf.DecodeError( "Unable to build a valid blobstore.BlobKey instance. Key " "supplied was %r" % (key,) ) obj.__class__ = blobstore.BlobInfo obj.__init__(key)
def decode(stream, strict=True): """ Decodes a SOL stream. L{strict} mode ensures that the sol stream is as spec compatible as possible. @return: A C{tuple} containing the C{root_name} and a C{dict} of name, value pairs. """ if not isinstance(stream, util.BufferedByteStream): stream = util.BufferedByteStream(stream) # read the version version = stream.read(2) if version != HEADER_VERSION: raise pyamf.DecodeError('Unknown SOL version in header') # read the length length = stream.read_ulong() if strict and stream.remaining() != length: raise pyamf.DecodeError('Inconsistent stream header length') # read the signature signature = stream.read(10) if signature != HEADER_SIGNATURE: raise pyamf.DecodeError('Invalid signature') length = stream.read_ushort() root_name = stream.read_utf8_string(length) # read padding if stream.read(3) != PADDING_BYTE * 3: raise pyamf.DecodeError('Invalid padding read') decoder = pyamf.get_decoder(stream.read_uchar()) decoder.stream = stream values = {} while True: if stream.at_eof(): break name = decoder.readString() value = decoder.readElement() # read the padding t = stream.read(1) while t != PADDING_BYTE: # raise pyamf.DecodeError('Missing padding byte') sys.stderr.write('Bad padding byte: 0x02%x\n' % ord(t)) t = stream.read(1) values[name] = value return (root_name, values)
def _read_header(stream, decoder, strict=False): """ Read AMF L{Message} header from the stream. @type stream: L{BufferedByteStream<pyamf.util.BufferedByteStream>} @param decoder: An AMF0 decoder. @param strict: Use strict decoding policy. Default is C{False}. Will raise a L{pyamf.DecodeError} if the data that was read from the stream does not match the header length. @type strict: C{boolean} @return: A C{tuple} containing the name of the header, a C{bool} determining if understanding this header is required and the decoded data. @note: Quite what understanding required headers actually means is unknown. @raise DecodeError: Data read from stream does not match header length. """ name_len = stream.read_ushort() name = stream.read_utf8_string(name_len) required = bool(stream.read_uchar()) data_len = stream.read_ulong() pos = stream.tell() data = decoder.readElement() if strict and pos + data_len != stream.tell(): raise pyamf.DecodeError( "Data read from stream does not match header length") return (name, required, data)
def readElement(self): """ Reads an AMF3 element from the data stream. @raise DecodeError: The ActionScript type is unsupported. @raise EOStream: No more data left to decode. """ pos = self.stream.tell() try: t = self.stream.read(1) except IOError: raise pyamf.EOStream try: func = self._func_cache[t] except KeyError: func = self.getTypeFunc(t) if not func: raise pyamf.DecodeError("Unsupported ActionScript type %s" % (hex(ord(t)), )) self._func_cache[t] = func try: return func() except IOError: self.stream.seek(pos) raise
def decode(stream, context=None, strict=False): """ Decodes the incoming stream. @type stream: L{BufferedByteStream<pyamf.util.BufferedByteStream>} @param stream: AMF data. @type context: L{amf0.Context<pyamf.amf0.Context>} or L{amf3.Context<pyamf.amf3.Context>} @param context: Context. @type strict: C{bool} @param strict: Enforce strict encoding. Default is C{False}. @raise DecodeError: Malformed stream. @raise RuntimeError: Decoder is unable to fully consume the stream buffer. @return: Message envelope. @rtype: L{Envelope} """ if not isinstance(stream, util.BufferedByteStream): stream = util.BufferedByteStream(stream) msg = Envelope() msg.amfVersion = stream.read_uchar() # see http://osflash.org/documentation/amf/envelopes/remoting#preamble # why we are doing this... if msg.amfVersion > 0x09: raise pyamf.DecodeError("Malformed stream (amfVersion=%d)" % msg.amfVersion) if context is None: context = pyamf.get_context(pyamf.AMF0) else: context = copy.copy(context) decoder = pyamf._get_decoder_class(pyamf.AMF0)(stream, context=context, strict=strict) msg.clientType = stream.read_uchar() header_count = stream.read_ushort() for i in xrange(header_count): name, required, data = _read_header(stream, decoder, strict) msg.headers[name] = data if required: msg.headers.set_required(name) body_count = stream.read_short() for i in range(body_count): context.reset() target, payload = _read_body(stream, decoder, strict) msg[target] = payload if strict and stream.remaining() > 0: raise RuntimeError("Unable to fully consume the buffer") return msg
def _read_header(stream, decoder, strict=False): """ Read AMF L{Message} header. @type stream: L{BufferedByteStream<pyamf.util.BufferedByteStream>} @param stream: AMF data. @type decoder: L{amf0.Decoder<pyamf.amf0.Decoder>} @param decoder: AMF decoder instance @type strict: C{bool} @param strict: Use strict decoding policy. Default is C{False}. @raise DecodeError: The data that was read from the stream does not match the header length. @rtype: C{tuple} @return: - Name of the header. - A C{bool} determining if understanding this header is required. - Value of the header. """ name_len = stream.read_ushort() name = stream.read_utf8_string(name_len) required = bool(stream.read_uchar()) data_len = stream.read_ulong() pos = stream.tell() data = decoder.readElement() if strict and pos + data_len != stream.tell(): raise pyamf.DecodeError( "Data read from stream does not match header length") return (name, required, data)
def _read_body(stream, decoder, strict=False): """ Read AMF message body. @type stream: L{BufferedByteStream<pyamf.util.BufferedByteStream>} @param stream: AMF data. @type decoder: L{amf0.Decoder<pyamf.amf0.Decoder>} @param decoder: AMF decoder instance. @type strict: C{bool} @param strict: Use strict decoding policy. Default is C{False}. @raise DecodeError: Data read from stream does not match body length. @rtype: C{tuple} @return: A C{tuple} containing: - ID of the request - L{Request} or L{Response} """ def _read_args(): """ @raise pyamf.DecodeError: Array type required for request body. """ if stream.read(1) != '\x0a': raise pyamf.DecodeError("Array type required for request body") x = stream.read_ulong() return [decoder.readElement() for i in xrange(x)] target = stream.read_utf8_string(stream.read_ushort()) response = stream.read_utf8_string(stream.read_ushort()) status = STATUS_OK is_request = True for (code, s) in STATUS_CODES.iteritems(): if target.endswith(s): is_request = False status = code target = target[:0 - len(s)] data_len = stream.read_ulong() pos = stream.tell() if is_request: data = _read_args() else: data = decoder.readElement() if strict and pos + data_len != stream.tell(): raise pyamf.DecodeError("Data read from stream does not match body " "length (%d != %d)" % (pos + data_len, stream.tell(),)) if is_request: return (response, Request(target, body=data)) else: if status == STATUS_ERROR and isinstance(data, pyamf.ASObject): data = get_fault(data) return (target, Response(data, status))
def __readamf__(self, input): data = input.readObject() if not hasattr(data, '__iter__'): raise pyamf.DecodeError( 'Unable to read a list when decoding ArrayCollection') self.extend(data)
def _read_args(): # we have to go through this insanity because it seems that amf0 # does not keep the array of args in the object references lookup type_byte = stream.peek(1) if type_byte == '\x11': if not decoder.use_amf3: raise pyamf.DecodeError( "Unexpected AMF3 type with incorrect message type") return decoder.readElement() if type_byte != '\x0a': raise pyamf.DecodeError("Array type required for request body") stream.read(1) x = stream.read_ulong() return [decoder.readElement() for i in xrange(x)]
def _read_args(): """ @raise pyamf.DecodeError: Array type required for request body. """ if stream.read(1) != '\x0a': raise pyamf.DecodeError("Array type required for request body") x = stream.read_ulong() return [decoder.readElement() for i in xrange(x)]
def readObject(self): """ Reads an object from the stream. @raise ReferenceError: Unknown reference found. @raise DecodeError: Unknown object encoding detected. """ ref = self.readInteger(False) if ref & REFERENCE_BIT == 0: obj = self.context.getObject(ref >> 1) if obj is None: raise pyamf.ReferenceError( 'Unknown reference %d' % (ref >> 1,) ) if self.use_proxies is True: obj = self.readProxy(obj) return obj ref >>= 1 class_def = self._getClassDefinition(ref) alias = class_def.alias obj = alias.createInstance(codec=self) obj_attrs = dict() self.context.addObject(obj) if class_def.encoding in ( ObjectEncoding.EXTERNAL, ObjectEncoding.PROXY): obj.__readamf__(DataInput(self)) if self.use_proxies is True: obj = self.readProxy(obj) return obj elif class_def.encoding == ObjectEncoding.DYNAMIC: self._readStatic(class_def, obj_attrs) self._readDynamic(class_def, obj_attrs) elif class_def.encoding == ObjectEncoding.STATIC: self._readStatic(class_def, obj_attrs) else: raise pyamf.DecodeError("Unknown object encoding") alias.applyAttributes(obj, obj_attrs, codec=self) if self.use_proxies is True: obj = self.readProxy(obj) return obj
def readType(self): """ Read and returns the next byte in the stream and determine its type. @raise DecodeError: AMF0 type not recognized. @return: AMF0 type. """ type = self.stream.read_uchar() if type not in ACTIONSCRIPT_TYPES: raise pyamf.DecodeError("Unknown AMF0 type 0x%02x at %d" % (type, self.stream.tell() - 1)) return type
def decode(stream, strict=False, logger=None, timezone_offset=None): """ Decodes the incoming stream as a remoting message. @type stream: L{BufferedByteStream<pyamf.util.BufferedByteStream>} @param strict: Enforce strict decoding. Default is C{False}. @type strict: C{boolean} @param logger: Used to log interesting events whilst decoding a remoting message. @type logger: U{logging.Logger<http:// docs.python.org/library/logging.html#loggers>} @param timezone_offset: The difference between the current timezone and UTC. Date/times should always be handled in UTC to avoid confusion but this is required for legacy systems. @type timezone_offset: U{datetime.datetime.timedelta<http:// docs.python.org/library/datetime.html#datetime.timedelta>} @return: Message L{envelope<Envelope>}. @rtype: L{Envelope} """ if not isinstance(stream, util.BufferedByteStream): stream = util.BufferedByteStream(stream) msg = Envelope() msg.amfVersion = stream.read_ushort() # see http://osflash.org/documentation/amf/envelopes/remoting#preamble # why we are doing this... if msg.amfVersion > 0x09: raise pyamf.DecodeError( "Malformed stream (amfVersion=%d)" % ( msg.amfVersion, ) ) decoder = pyamf.get_decoder( pyamf.AMF0, stream, strict=strict, timezone_offset=timezone_offset ) context = decoder.context decoder.use_amf3 = msg.amfVersion == pyamf.AMF3 header_count = stream.read_ushort() for i in xrange(header_count): name, required, data = _read_header(stream, decoder, strict) msg.headers[name] = data if required: msg.headers.set_required(name) body_count = stream.read_short() for i in xrange(body_count): context.clear() target, payload = _read_body(stream, decoder, strict, logger) msg[target] = payload if strict and stream.remaining() > 0: raise RuntimeError("Unable to fully consume the buffer") return msg
def _read_body(stream, decoder, strict=False, logger=None): """ Read an AMF message body from the stream. @type stream: L{BufferedByteStream<pyamf.util.BufferedByteStream>} @param decoder: An AMF0 decoder. @param strict: Use strict decoding policy. Default is C{False}. @type strict: C{boolean} @param logger: Used to log interesting events whilst reading a remoting body. @type logger: A C{logging.Logger} instance or C{None} @return: A C{tuple} containing the C{id} of the request and the L{Request} or L{Response} """ def _read_args(): # we have to go through this insanity because it seems that amf0 # does not keep the array of args in the object references lookup type_byte = stream.peek(1) if type_byte == '\x11': if not decoder.use_amf3: raise pyamf.DecodeError( "Unexpected AMF3 type with incorrect message type") return decoder.readElement() if type_byte != '\x0a': raise pyamf.DecodeError("Array type required for request body") stream.read(1) x = stream.read_ulong() return [decoder.readElement() for i in xrange(x)] target = stream.read_utf8_string(stream.read_ushort()) response = stream.read_utf8_string(stream.read_ushort()) status = STATUS_OK is_request = True for code, s in STATUS_CODES.iteritems(): if not target.endswith(s): continue is_request = False status = code target = target[:0 - len(s)] data_len = stream.read_ulong() pos = stream.tell() if is_request: data = _read_args() else: data = decoder.readElement() if strict and pos + data_len != stream.tell(): raise pyamf.DecodeError( "Data read from stream does not match body length (%d != %d)" % ( pos + data_len, stream.tell(), ) ) if is_request: return response, Request(target, body=data) if status == STATUS_ERROR and isinstance(data, pyamf.ASObject): data = get_fault(data) return target, Response(data, status)
def decode(stream, context=None, strict=False, logger=None, timezone_offset=None): """ Decodes the incoming stream as a remoting message. @param stream: AMF data. @type stream: L{BufferedByteStream<pyamf.util.BufferedByteStream>} @param context: Context. @type context: L{amf0.Context<pyamf.amf0.Context>} or L{amf3.Context<pyamf.amf3.Context>} @param strict: Enforce strict decoding. Default is C{False}. @type strict: C{bool} @param logger: Used to log interesting events whilst decoding a remoting message. @type logger: A L{logging.Logger} instance or C{None}. @param timezone_offset: The difference between the current timezone and UTC. Date/times should always be handled in UTC to avoid confusion but this is required for legacy systems. @type timezone_offset: L{datetime.timedelta} @raise DecodeError: Malformed stream. @raise RuntimeError: Decoder is unable to fully consume the stream buffer. @return: Message envelope. @rtype: L{Envelope} """ if not isinstance(stream, util.BufferedByteStream): stream = util.BufferedByteStream(stream) if logger is not None: logger.debug('remoting.decode start') msg = Envelope() msg.amfVersion = stream.read_uchar() # see http://osflash.org/documentation/amf/envelopes/remoting#preamble # why we are doing this... if msg.amfVersion > 0x09: raise pyamf.DecodeError("Malformed stream (amfVersion=%d)" % msg.amfVersion) if context is None: context = pyamf.get_context(pyamf.AMF0, exceptions=False) decoder = pyamf.get_decoder(pyamf.AMF0, stream, context=context, strict=strict, timezone_offset=timezone_offset) msg.clientType = stream.read_uchar() header_count = stream.read_ushort() for i in xrange(header_count): name, required, data = _read_header(stream, decoder, strict) msg.headers[name] = data if required: msg.headers.set_required(name) body_count = stream.read_short() for i in range(body_count): context.clear() target, payload = _read_body(stream, decoder, strict, logger) msg[target] = payload if strict and stream.remaining() > 0: raise RuntimeError("Unable to fully consume the buffer") if logger is not None: logger.debug('remoting.decode end') return msg