lib/ole/property_set.rb in ruby-ole-1.2.3 vs lib/ole/property_set.rb in ruby-ole-1.2.4
- old
+ new
@@ -1,6 +1,7 @@
require 'ole/types'
+require 'yaml'
module Ole
module Types
#
# The PropertySet class currently supports readonly access to the properties
@@ -11,34 +12,45 @@
#
# See http://poi.apache.org/hpsf/internals.html for details
#
class PropertySet
HEADER_SIZE = 28
- HEADER_UNPACK = "vvVa#{Clsid::SIZE}V"
+ HEADER_PACK = "vvVa#{Clsid::SIZE}V"
OS_MAP = {
0 => :win16,
1 => :mac,
- 2 => :win32
+ 2 => :win32,
+ 0x20001 => :ooffice, # open office on linux...
}
# define a smattering of the property set guids.
- FMTID_SummaryInformation = Clsid.parse '{f29f85e0-4ff9-1068-ab91-08002b27b3d9}'
- FMTID_DocSummaryInformation = Clsid.parse '{d5cdd502-2e9c-101b-9397-08002b2cf9ae}'
- FMTID_UserDefinedProperties = Clsid.parse '{d5cdd505-2e9c-101b-9397-08002b2cf9ae}'
+ #FMTID_SummaryInformation = Clsid.parse '{f29f85e0-4ff9-1068-ab91-08002b27b3d9}'
+ #FMTID_DocSummaryInformation = Clsid.parse '{d5cdd502-2e9c-101b-9397-08002b2cf9ae}'
+ #FMTID_UserDefinedProperties = Clsid.parse '{d5cdd505-2e9c-101b-9397-08002b2cf9ae}'
+ DATA = YAML.load_file(File.dirname(__FILE__) + '/../../data/propids.yaml').
+ inject({}) { |hash, (key, value)| hash.update Clsid.parse(key) => value }
+
+ module Constants
+ DATA.each { |guid, (name, map)| const_set name, guid }
+ end
+
+ include Constants
+
class Section < Struct.new(:guid, :offset)
include Variant::Constants
include Enumerable
SIZE = Clsid::SIZE + 4
- UNPACK_STR = "a#{Clsid::SIZE}v"
+ PACK = "a#{Clsid::SIZE}v"
attr_reader :length
def initialize str, property_set
@property_set = property_set
- super(*str.unpack(UNPACK_STR))
+ super(*str.unpack(PACK))
self.guid = Clsid.load guid
+ @map = DATA[guid] ? DATA[guid][1] : nil
load_header
end
def io
@property_set.io
@@ -64,13 +76,34 @@
yield id, type, value
end
self
end
+ def [] key
+ unless Integer === key
+ return unless @map and key = @map.invert[key]
+ end
+ return unless result = properties.assoc(key)
+ result.last
+ end
+
+ def method_missing name, *args
+ if args.empty? and @map and @map.values.include? name.to_s
+ self[name.to_s]
+ else
+ super
+ end
+ end
+
def properties
- to_enum.to_a
+ @properties ||= to_enum.to_a
end
+
+ #def to_h
+ # properties.inject({}) do |hash, (key, type, value)|
+ # hash.update
+ #end
end
attr_reader :io, :signature, :unknown, :os, :guid, :sections
def initialize io
@io = io
@@ -79,18 +112,61 @@
# expect no gap between last section and start of data.
#Log.warn "gap between section list and property data" unless io.pos == @sections.map(&:offset).min
end
def load_header str
- @signature, @unknown, @os_id, @guid, @num_sections = str.unpack HEADER_UNPACK
+ @signature, @unknown, @os_id, @guid, @num_sections = str.unpack HEADER_PACK
# should i check that unknown == 0? it usually is. so is the guid actually
@guid = Clsid.load @guid
@os = OS_MAP[@os_id] || Log.warn("unknown operating system id #{@os_id}")
end
def load_section_list str
@sections = str.scan(/.{#{Section::SIZE}}/m).map { |str| Section.new str, self }
end
end
+ end
+
+ class Storage
+ # i'm thinking - search for a property set in +filenames+ containing a
+ # section with guid +guid+. then yield it. can read/write to it in the
+ # block.
+ # propsets themselves can have guids, but they are often all null.
+ def with_property_set guid, filenames=nil
+ end
+
+ class PropertySetSectionProxy
+ attr_reader :obj, :section_num
+ def initialize obj, section_num
+ @obj, @section_num = obj, section_num
+ end
+
+ def method_missing name, *args, &block
+ obj.open do |io|
+ section = Types::PropertySet.new(io).sections[section_num]
+ section.send name, *args, &block
+ end
+ end
+ end
+
+ # this will be changed to use with_property_set
+ def summary_information
+ dirent = root["\005SummaryInformation"]
+ dirent.open do |io|
+ propset = Types::PropertySet.new(io)
+ sections = propset.sections
+ # this will maybe get wrapped up as
+ # section = propset[guid]
+ # maybe taking it one step further, i'd hide the section thing,
+ # and let you use composite keys, like
+ # propset[4, guid] eg in MAPI, and just propset.doc_author.
+ section = sections.find do |section|
+ section.guid == Types::PropertySet::FMTID_SummaryInformation
+ end
+ return PropertySetSectionProxy.new(dirent, sections.index(section))
+ end
+ end
+
+ alias summary_info :summary_information
end
end