Ejemplo n.º 1
0
    def _writeDict(self, o):
        """
        Write C{dict} to the data stream.

        @param o: The C{dict} data to be encoded to the AMF0 data stream.
        """

        # For consistency with AMF3, all string indices are output first,
        # then all integer indices.  We don't do any of the other special
        # handling for integer indices required in AMF3, though.
        int_items = []
        str_items = []

        for k, v in six.iteritems(o):
            if isinstance(k, six.integer_types):
                int_items.append((six.text_type(k).encode("utf-8"), v))
            elif isinstance(k, six.text_type):
                str_items.append((k.encode("utf-8"), v))
            elif isinstance(k, six.binary_type):
                str_items.append((k, v))
            else:
                raise miniamf.EncodeError(
                    "Unable to encode key %r in dict %r" % (k, o))

        int_items.sort()
        str_items.sort()

        for k, v in int_items:
            self.serialiseString(k)
            self.writeElement(v)

        for k, v in str_items:
            self.serialiseString(k)
            self.writeElement(v)
Ejemplo n.º 2
0
def convert_Decimal(x, encoder):
    """
    Called when an instance of U{decimal.Decimal<http://
    docs.python.org/library/decimal.html#decimal-objects>} is about to be
    encoded to an AMF stream.

    @return: If the encoder is in 'strict' mode then C{x} will be converted to
        a float. Otherwise an L{miniamf.EncodeError} with a friendly message is
        raised.
    """
    if encoder.strict is False:
        return float(x)

    raise miniamf.EncodeError(
        'Unable to encode decimal.Decimal instances as there is no way to '
        'guarantee exact conversion. Use strict=False to convert to a float.')
Ejemplo n.º 3
0
    def writeElement(self, data):
        """
        Encodes C{data} to AMF. If the data is not able to be matched to an AMF
        type, then L{miniamf.EncodeError} will be raised.
        """
        key = type(data)
        func = None

        try:
            func = self._func_cache[key]
        except KeyError:
            func = self.getTypeFunc(data)

            if func is None:
                raise miniamf.EncodeError('Unable to encode %r (type %r)' % (
                    data, key))

            self._func_cache[key] = func

        func(data)
Ejemplo n.º 4
0
    def writeDate(self, n):
        """
        Writes a C{datetime} instance to the stream.

        Does not support C{datetime.time} instances because AMF3 has
        no way to encode time objects, so please use C{datetime.datetime}
        instead.

        @type n: L{datetime}
        @param n: The C{Date} data to be encoded to the AMF3 data stream.
        @raise EncodeError: A datetime.time instance was found
        """
        if isinstance(n, datetime.time):
            raise miniamf.EncodeError(
                'A datetime.time instance was found but AMF3 has no way to '
                'encode time objects. Please use datetime.datetime instead '
                '(got:%r)' % (n,)
            )

        self.stream.write(TYPE_DATE)

        ref = self.context.getObjectReference(n)

        if ref != -1:
            self._writeInteger(ref << 1)

            return

        self.context.addObject(n)

        self.stream.write_uchar(REFERENCE_BIT)

        if self.timezone_offset is not None:
            n -= self.timezone_offset

        ms = util.get_timestamp(n)
        self.stream.write_double(ms * 1000.0)
Ejemplo n.º 5
0
    def writeDate(self, d):
        """
        Writes a date to the data stream.

        @type d: Instance of C{datetime.datetime}
        @param d: The date to be encoded to the AMF0 data stream.
        """
        if isinstance(d, datetime.time):
            raise miniamf.EncodeError(
                'A datetime.time instance was found but AMF0 has no way to '
                'encode time objects. Please use datetime.datetime instead '
                '(got:%r)' % (d, ))

        # According to the Red5 implementation of AMF0, dates references are
        # created, but not used.
        if self.timezone_offset is not None:
            d -= self.timezone_offset

        secs = util.get_timestamp(d)
        tz = 0

        self.writeType(TYPE_DATE)
        self.stream.write_double(secs * 1000.0)
        self.stream.write_short(tz)
Ejemplo n.º 6
0
    def writeObject(self, obj):
        """
        Writes an object to the stream.
        """

        self.stream.write(TYPE_OBJECT)

        ref = self.context.getObjectReference(obj)

        if ref != -1:
            self._writeInteger(ref << 1)

            return

        self.context.addObject(obj)

        # object is not referenced, serialise it
        kls = obj.__class__
        definition = self.context.getClass(kls)
        alias = None
        class_ref = False  # if the class definition is a reference

        if definition:
            class_ref = True
            alias = definition.alias
        else:
            alias = self.context.getClassAlias(kls)
            definition = ClassDefinition(alias)

            self.context.addClass(definition, alias.klass)

        if class_ref:
            self.stream.write(definition.reference)
        else:
            ref = 0

            if definition.encoding != ObjectEncoding.EXTERNAL:
                ref += definition.attr_len << 4

            final_reference = encode_int(
                ref |
                definition.encoding << 2 |
                REFERENCE_BIT << 1 |
                REFERENCE_BIT
            )

            self.stream.write(final_reference)

            definition.reference = encode_int(
                definition.reference << 2 | REFERENCE_BIT)

            if alias.anonymous:
                self.stream.write(b'\x01')
            else:
                self.serialiseString(alias.alias)

            # work out what the final reference for the class will be.
            # this is okay because the next time an object of the same
            # class is encoded, class_ref will be True and never get here
            # again.

        if alias.external:
            obj.__writeamf__(DataOutput(self))

            return

        attrs = alias.getEncodableAttributes(obj, codec=self)

        if alias.static_attrs:
            sattrs = sorted(alias.static_attrs)
            if not class_ref:
                for attr in sattrs:
                    self.serialiseString(attr)

            for attr in sattrs:
                value = attrs.pop(attr)

                self.writeElement(value)

            if definition.encoding == ObjectEncoding.STATIC:
                return

        if definition.encoding == ObjectEncoding.DYNAMIC:
            if attrs:
                try:
                    keys = sorted(attrs)
                except TypeError:
                    raise miniamf.EncodeError(
                        'Unable to encode %r (unsortable attrs: %r)'
                        % (obj, attrs))

                for key in keys:
                    value = attrs[key]
                    if isinstance(key, six.integer_types):
                        key = six.text_type(key)
                    elif not isinstance(key, six.string_types):
                        raise miniamf.EncodeError(
                            'Unable to encode %r (key %r not supported)'
                            % (obj, key))

                    self.serialiseString(key)
                    self.writeElement(value)

            self.stream.write(b'\x01')
Ejemplo n.º 7
0
    def writeDict(self, n):
        """
        Writes a C{dict} to the stream.

        @type n: C{__builtin__.dict}
        @param n: The C{dict} data to be encoded to the AMF3 data stream.
        @raise ValueError: Non int/string key value found in the C{dict}
        @raise EncodeError: C{dict} contains empty string keys.
        """
        # Design bug in AMF3 that cannot read/write empty key strings
        # for more info
        if '' in n:
            raise miniamf.EncodeError("dicts cannot contain empty string keys")

        self.stream.write(TYPE_ARRAY)

        ref = self.context.getObjectReference(n)

        if ref != -1:
            self._writeInteger(ref << 1)

            return

        self.context.addObject(n)

        # The AMF3 spec demands that all string indices be listed first
        int_keys = []
        str_keys = []

        for x in n.keys():
            if isinstance(x, six.integer_types):
                int_keys.append(x)
            elif isinstance(x, six.string_types):
                str_keys.append(x)
            else:
                raise ValueError("Non integer/string key value found in dict")

        # Make sure the integer keys are within range
        l = len(int_keys)

        for x in int_keys:
            if l < x <= 0:
                # treat as a string key
                str_keys.append(x)
                del int_keys[int_keys.index(x)]

        int_keys.sort()

        # If integer keys don't start at 0, they will be treated as strings
        if len(int_keys) > 0 and int_keys[0] != 0:
            for x in int_keys:
                str_keys.append(six.text_type(x))
                del int_keys[int_keys.index(x)]

        self._writeInteger(len(int_keys) << 1 | REFERENCE_BIT)

        str_keys.sort()

        for x in str_keys:
            self.serialiseString(x)
            self.writeElement(n[x])

        self.stream.write_uchar(0x01)

        for k in int_keys:
            self.writeElement(n[k])