Fabio Vitale <fabio / sferaconsulting.it> writes:

> Now 3 major questions:
>
> Q 1: what type must I declare for Filename in the class MRKMessage?

Okay, first off I apologize but I lead you astray.  Apparently it's
not enough to override bit_length in your subclass.  When you read the
file, you're not getting the stuff lined up properly.  Therefore I've
decided to make up for it by finishing the rest of your code for you.

Note that now I override round_byte_length instead, and we get:

require 'bit-struct'
class MRKHeader < BitStruct
    unsigned :version,      32, "Version",     :endian => :native
    unsigned :uid_Validity, 32, "UIDValidity", :endian => :native
    unsigned :uid_next,     32, "UIDNext",     :endian => :native
    unsigned :last_write_counter, 32, "LastWriteCounter", :endian => :native
    rest     :unused,           "Unused"
    # Override so that it gets padded properly
    def MRKHeader.round_byte_length
      super
      36
    end
end

# Ideally, I'd construct some sort of "flags" bit-struct field
# Or define a boolean field type and make this a series of boolean
# fields.

# However, for now we can deal with a series of 0s and 1s

class MRKMessageFlags < BitStruct
  unsigned :flagUnused,   2, "Unused"
  unsigned :flagSeen,     1, "Seen"
  unsigned :flagAnswered, 1, "Answered"
  unsigned :flagFlagged,  1, "Flagged"
  unsigned :flagDeleted,  1, "Deleted"
  unsigned :flagDraft,    1, "Draft"
  unsigned :flagRecent,   1, "Recent"
end

class MRKMessage < BitStruct
  # Note "text" for nul-terminated strings
  text      :filename,  23*8, "FileName", :endian => :native
  nest      :flags,     MRKMessageFlags, "Flags"
  unsigned  :uid,       32,   "UID",      :endian => :native
  unsigned  :msg_size,  32,   "MsgSize",  :endian => :native
  unsigned  :date,      32,   "Date",     :endian => :native

  # Now we futz with the way that date is set and gotten.
  # we rename the existing date field to __date, and
  # then we supply our own meaning for "date" that does
  # translation into and out of seconds-since-1970

  # Again, the ideal solution would be to define a new bit-struct
  # field type that did this stuff itself.

  alias_method :__date=, :date=
  alias_method :__date, :date
  def date=(time)
    self.__date= time.to_i
  end
  def date
    Time.at(self.__date)
  end
  # we don't need to override the length computation here
end

File.open("imap.mrk", "rb") {|f|
  head_string = f.read(MRKHeader.round_byte_length)
  raise "No header!" unless head_string
  mrk_header = MRKHeader.new(head_string)
  puts mrk_header.inspect
  while msg_string = f.read(MRKMessage.round_byte_length) do
    puts MRKMessage.new(msg_string).inspect
  end
}

__END__

This produces (on the first bit from your file):

#<MRKHeader version=1, uid_Validity=1106138982, uid_next=5825,
last_write_counter=9872,
unused="\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\r\n">
#<MRKMessage filename="md50000004286.msg", flags=#<MRKMessageFlags
flagUnused=0, flagSeen=1, flagAnswered=1, flagFlagged=0,
flagDeleted=0, flagDraft=0, flagRecent=0>, uid=4150, msg_size=20732,
date=Mon Dec 19 12:18:35 Eastern Standard Time 2005>

This is more what you expected, right?