Class: Exchange::OfflineAddressBook::Parser

Inherits:
Object
  • Object
show all
Defined in:
lib/exchange-offline-address-book/parser.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(oab) ⇒ Parser

Returns a new instance of Parser.



631
632
633
634
# File 'lib/exchange-offline-address-book/parser.rb', line 631

def initialize(oab)
  @oab = open(oab)
  @header = _header
end

Instance Attribute Details

#headerObject (readonly)

Returns the value of attribute header.



643
644
645
# File 'lib/exchange-offline-address-book/parser.rb', line 643

def header
  @header
end

#serialNumberObject (readonly)

Returns the value of attribute serialNumber.



643
644
645
# File 'lib/exchange-offline-address-book/parser.rb', line 643

def serialNumber
  @serialNumber
end

#totalRecordsObject (readonly)

Returns the value of attribute totalRecords.



643
644
645
# File 'lib/exchange-offline-address-book/parser.rb', line 643

def totalRecords
  @totalRecords
end

Instance Method Details

#_headerObject



686
687
688
689
690
691
692
693
694
695
696
697
698
# 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

#_integerObject



652
653
654
655
656
657
658
659
660
661
662
663
664
# 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) ⇒ Object



700
701
702
703
704
705
706
707
708
709
710
711
712
# 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

#_propertyTypesObject



666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
# 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) ⇒ Object



731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
# 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) ⇒ Object



714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
# 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

#_ubyteObject



648
649
650
# File 'lib/exchange-offline-address-book/parser.rb', line 648

def _ubyte
  return @oab.read(1).unpack('C*')[0]
end

#_uint32Object



645
646
647
# File 'lib/exchange-offline-address-book/parser.rb', line 645

def _uint32
  return @oab.read(4).unpack('V*')[0]
end

#posObject



636
637
638
# File 'lib/exchange-offline-address-book/parser.rb', line 636

def pos
  return sprintf('%08o', @oab.pos)
end

#recordsObject



752
753
754
755
756
757
# 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) ⇒ Object



639
640
641
# File 'lib/exchange-offline-address-book/parser.rb', line 639

def typeof(type)
  return MapiPropertyDataType[type.to_s.to_sym]
end