예제 #1
0
 def length_wrapper(buf, pos):
     """Decode repeat values prefixed with the length"""
     length, pos = varint.decode_varint(buf, pos)
     end = pos + length
     output = []
     while pos < end:
         value, pos = wrapped_decoder(buf, pos)
         output.append(value)
     if pos > end:
         raise decoder._DecodeError("Invalid Packed Field Length")
     return output, pos
예제 #2
0
def decode_message(buf, typedef=None, pos=0, end=None, group=False):
    """Decode a protobuf message with no length delimiter"""
    if end is None:
        end = len(buf)

    if typedef is None:
        typedef = {}
    else:
        # Don't want to accidentally modify the original
        typedef = copy.deepcopy(typedef)

    output = {}

    while pos < end:
        # Read in a field
        tag, pos = decoder._DecodeVarint(buf, pos)
        try:
            field_number, wire_type = wire_format.UnpackTag(tag)
        except Exception as exc:
            raise (
                ValueError,
                'Could not read valid tag at pos %d. Ensure it is a valid protobuf message: %s'
                % (pos - len(tag), exc), sys.exc_info()[2])

        # Convert to str
        field_number = str(field_number)
        orig_field_number = field_number

        field_typedef = None
        if field_number in typedef:
            field_typedef = typedef[field_number]
        else:
            field_typedef = {}
            field_typedef['type'] = types.wire_type_defaults[wire_type]

        field_type = field_typedef['type']

        # If field_type is None, its either an unsupported wire type, length delim or group
        # length delim we have to try and decode first
        field_out = None
        if field_type is None:
            if wire_type == wire_format.WIRETYPE_LENGTH_DELIMITED:
                out, field_type = decode_guess(buf, pos)
                if field_type == 'message':
                    field_out, message_typedef, pos = out
                    field_typedef['message_typedef'] = message_typedef
                else:
                    field_out, pos = out
            elif wire_type == wire_format.WIRETYPE_END_GROUP:
                # TODO Should probably match the field_number to START_GROUP
                if not group:
                    raise ValueError("Found END_GROUP before START_GROUP")
                # exit out
                return output, typedef, pos
            else:
                raise ValueError(
                    "Could not find default type for wiretype: %d" % wire_type)
        else:
            if field_type == 'message':
                #TODO probably big enough to factor out
                message_typedef = None
                # Check for a anonymous type
                if 'message_typedef' in field_typedef:
                    message_typedef = field_typedef['message_typedef']
                # Check for type defined by message type name
                elif 'message_type_name' in field_typedef:
                    message_typedef = types.messages[
                        field_typedef['message_type_name']]

                try:
                    field_out, message_typedef, pos = decode_lendelim_message(
                        buf, message_typedef, pos)
                    # Save type definition
                    field_typedef['message_typedef'] = message_typedef
                except Exception as exc:
                    # If this is the root message just fail
                    if pos == 0:
                        raise exc

                if field_out is None and 'alt_typedefs' in field_typedef:
                    # check for an alternative type definition
                    for alt_field_number, alt_typedef in field_typedef[
                            'alt_typedefs'].items():
                        try:
                            field_out, message_typedef, pos = decode_lendelim_message(
                                buf, alt_typedef, pos)
                        except Exception as exc:
                            pass

                        if field_out is not None:
                            # Found working typedef
                            field_typedef['alt_typedefs'][
                                alt_field_number] = message_typedef
                            field_number = field_number + "-" + alt_field_number
                            break

                if field_out is None:
                    # Still no typedef, try anonymous, and let the error propogate if it fails
                    field_out, message_typedef, pos = decode_lendelim_message(
                        buf, {}, pos)
                    if 'alt_typedefs' in field_typedef:
                        # get the next higher alt field number
                        alt_field_number = str(
                            max(map(int, field_typedef['alt_typedefs'].keys()))
                            + 1)
                    else:
                        field_typedef['alt_typedefs'] = {}
                        alt_field_number = '1'

                    field_typedef['alt_typedefs'][
                        alt_field_number] = message_typedef
                    field_number = field_number + "-" + alt_field_number
            elif field_type == 'group':
                group_typedef = None
                # Check for a anonymous type
                if 'group_typedef' in field_typedef:
                    group_typedef = field_typedef['group_typedef']
                field_out, group_typedef, pos = decode_group(
                    buf, group_typedef, pos)
                # Save type definition
                field_typedef['group_typedef'] = group_typedef
            else:
                # Verify wiretype matches
                if types.wiretypes[field_type] != wire_type:
                    raise ValueError(
                        "Invalid wiretype for field number %s. %s is not wiretype %s"
                        % (field_number, field_type, wire_type))

                # Simple type, just look up the decoder
                field_out, pos = types.decoders[field_type](buf, pos)
        field_typedef['type'] = field_type
        if 'name' not in field_typedef:
            field_typedef['name'] = ''

        field_key = field_number
        if '-' not in field_number and 'name' in field_typedef and field_typedef[
                'name'] != '':
            field_key = field_typedef['name']
        # Deal with repeats
        if field_key in output:
            if isinstance(field_out, list):
                if isinstance(output[field_number], list):
                    output[field_key] += field_out
                else:
                    output[field_key] = field_out.append(output[field_key])
            else:
                if isinstance(output[field_number], list):
                    output[field_key].append(field_out)
                else:
                    output[field_key] = [output[field_key], field_out]
        else:
            output[field_key] = field_out
            typedef[orig_field_number] = field_typedef
    if pos > end:
        raise decoder._DecodeError("Invalid Message Length")
    # Should never hit here as a group
    if group:
        raise ValueError("Got START_GROUP with no END_GROUP.")
    return output, typedef, pos