class Exchange::OfflineAddressBook::Parser

Attributes

header[R]
serialNumber[R]
totalRecords[R]

Public Class Methods

new(oab) click to toggle source
# File lib/exchange-offline-address-book/parser.rb, line 631
def initialize(oab)
  @oab = open(oab)
  @header = _header
end

Public Instance Methods

_header() click to toggle source
# File lib/exchange-offline-address-book/parser.rb, line 686
def _header
  # Read OAB_HDR
  version = _uint32
  raise "Version not found, got #{version.inspect}" unless version == 0x20 # version
  @serialNumber = _uint32
  @totalRecords = _uint32
  # Read OAB_META_DATA
  metadataSize = _uint32

  @headerProperties = _propertyTypes
  @oabProperties = _propertyTypes
  return _record(true)
end
_integer() click to toggle source
# File lib/exchange-offline-address-book/parser.rb, line 652
def _integer
  firstByte = _ubyte

  return firstByte if firstByte < 0x80

  case firstByte
    when 0x81 then return _ubyte
    when 0x82 then return _ubyte + (_ubyte << 8)
    when 0x83 then return _ubyte + (_ubyte << 8) + (_ubyte << 16)
    when 0x84 then return _ubyte + (_ubyte << 8) + (_ubyte << 16) + (_ubyte << 24)
  end
  raise "Unexpected first byte #{sprintf('%x', firstByte)} of integer"
end
_property(prop) click to toggle source
# File lib/exchange-offline-address-book/parser.rb, line 700
def _property(prop)
  if prop.array
    valueCount = _integer
    value = []
    valueCount.times{
      value << _scalar(prop.type)
    }
  else
    value = _scalar(prop.type)
  end
  p = OpenStruct.new(type: prop.type, id: prop.id, name: prop.name, value: value)
  return p
end
_propertyTypes() click to toggle source
# File lib/exchange-offline-address-book/parser.rb, line 666
def _propertyTypes
  n = _uint32
  return 1.upto(n).collect{|i|
    prop = OpenStruct.new
    prop.pos = pos

    id = _uint32
    prop._id = id.to_s(16)
    prop.id = sprintf('%04x', id >> 16).upcase
    prop.name = MapiPropertyName[prop.id]
    throw prop.id unless prop.name && prop.name != ''
    type = id & 0xffff
    prop.type = typeof(type & ~MapiPropertyDataType.Mv)
    prop._type = sprintf('%04x', (type & ~MapiPropertyDataType.Mv))
    prop.array = ((type & MapiPropertyDataType.Mv) != 0)
    prop.flags = _uint32
    prop
  }
end
_record(headerRecord = false) click to toggle source
# File lib/exchange-offline-address-book/parser.rb, line 731
def _record(headerRecord = false)
  initialPosition = @oab.pos
  recordSize = 0
  record = Record.new
  begin
    properties = headerRecord ? @headerProperties : @oabProperties
    recordSize = _uint32
    recordPresence = @oab.read((properties.length + 7) / 8).unpack("b*").join.split('').collect{|bit| bit.to_i != 0 }
    properties.each_with_index{|prop, i|
      next unless recordPresence[i + 7 - 2 * (i % 8)]
      p = _property(prop)
      pn = p.name.to_s
      record[pn] ||= []
      record[pn] << p.value
    }
  ensure
    @oab.pos = initialPosition + recordSize
  end
  return record
end
_scalar(type) click to toggle source
# File lib/exchange-offline-address-book/parser.rb, line 714
def _scalar(type)
  case type.to_sym
    when :Long    then return _integer
    when :Boolean then return (_ubyte > 0)
    when :Binary  then return @oab.read(_integer)
    when :AnsiString, :UnicodeString
      string = ''
      while (byte = @oab.read(1)) != "\x00"
        string << byte
      end
      # TODO: string.force_encoding(MapiPropertyDataType.UnicodeString ? 'UTF-8' : 'ASCII')
      string.force_encoding(type == :UnicodeString ? 'UTF-8' : 'ASCII')
      return string
  end
  raise "Unknown scalar type #{type}"
end
_ubyte() click to toggle source
# File lib/exchange-offline-address-book/parser.rb, line 648
def _ubyte
  return @oab.read(1).unpack('C*')[0]
end
_uint32() click to toggle source
# File lib/exchange-offline-address-book/parser.rb, line 645
def _uint32
  return @oab.read(4).unpack('V*')[0]
end
pos() click to toggle source
# File lib/exchange-offline-address-book/parser.rb, line 636
def pos
  return sprintf('%08o', @oab.pos)
end
records() click to toggle source
# File lib/exchange-offline-address-book/parser.rb, line 752
def records
  @records = Enumerator.new(@totalRecords) do |y|
    @totalRecords.times { y.yield _record }
  end
  return @records
end
typeof(type) click to toggle source
# File lib/exchange-offline-address-book/parser.rb, line 639
def typeof(type)
  return MapiPropertyDataType[type.to_s.to_sym]
end