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
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