Class: 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.



618
619
620
621
# File 'lib/exchange-offline-address-book/parser.rb', line 618

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

Instance Attribute Details

#headerObject (readonly)

Returns the value of attribute header.



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

def header
  @header
end

#serialNumberObject (readonly)

Returns the value of attribute serialNumber.



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

def serialNumber
  @serialNumber
end

#totalRecordsObject (readonly)

Returns the value of attribute totalRecords.



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

def totalRecords
  @totalRecords
end

Instance Method Details

#_headerObject



673
674
675
676
677
678
679
680
681
682
683
684
685
# File 'lib/exchange-offline-address-book/parser.rb', line 673

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



639
640
641
642
643
644
645
646
647
648
649
650
651
# File 'lib/exchange-offline-address-book/parser.rb', line 639

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



687
688
689
690
691
692
693
694
695
696
697
698
699
# File 'lib/exchange-offline-address-book/parser.rb', line 687

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



653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
# File 'lib/exchange-offline-address-book/parser.rb', line 653

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



718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
# File 'lib/exchange-offline-address-book/parser.rb', line 718

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



701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
# File 'lib/exchange-offline-address-book/parser.rb', line 701

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



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

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

#_uint32Object



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

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

#posObject



623
624
625
# File 'lib/exchange-offline-address-book/parser.rb', line 623

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

#recordsObject



739
740
741
742
743
744
# File 'lib/exchange-offline-address-book/parser.rb', line 739

def records
  @records = Enumerator.new(@totalRecords) do |y|
    @totalRecords.times { y.yield _record }
  end
  return @records
end

#typeof(type) ⇒ Object



626
627
628
# File 'lib/exchange-offline-address-book/parser.rb', line 626

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