Class: Mapi::PropertySet

Inherits:
Object
  • Object
show all
Includes:
Constants
Defined in:
lib/mapi/property_set.rb

Overview

The Mapi::PropertySet class is used to wrap the lower level Msg or Pst property stores, and provide a consistent and more friendly interface. It allows you to just say:

properties.subject

instead of:

properites.raw[0x0037, PS_MAPI]

The underlying store can be just a hash, or lazily loading directly from the file. A good compromise is to cache all the available keys, and just return the values on demand, rather than load up many possibly unwanted values.

Defined Under Namespace

Modules: Constants Classes: Key

Constant Summary collapse

NAMES =
{
	oleguid['00020328'] => 'PS_MAPI',
	oleguid['00020329'] => 'PS_PUBLIC_STRINGS',
	oleguid['00020380'] => 'PS_ROUTING_EMAIL_ADDRESSES',
	oleguid['00020381'] => 'PS_ROUTING_ADDRTYPE',
	oleguid['00020382'] => 'PS_ROUTING_DISPLAY_NAME',
	oleguid['00020383'] => 'PS_ROUTING_ENTRYID',
	oleguid['00020384'] => 'PS_ROUTING_SEARCH_KEY',
	# string properties in this namespace automatically get added to the internet headers
	oleguid['00020386'] => 'PS_INTERNET_HEADERS',
	# theres are bunch of outlook ones i think
	# http://blogs.msdn.com/stephen_griffin/archive/2006/05/10/outlook-2007-beta-documentation-notification-based-indexing-support.aspx
	# IPM.Appointment
	oleguid['00062002'] => 'PSETID_Appointment',
	# IPM.Task
	oleguid['00062003'] => 'PSETID_Task',
	# used for IPM.Contact
	oleguid['00062004'] => 'PSETID_Address',
	oleguid['00062008'] => 'PSETID_Common',
	# didn't find a source for this name. it is for IPM.StickyNote
	oleguid['0006200e'] => 'PSETID_Note',
	# for IPM.Activity. also called the journal?
	oleguid['0006200a'] => 'PSETID_Log',
}
SUPPORT_DIR =

duplicated here for now

File.dirname(__FILE__) + '/../..'
TAGS =

data files that provide for the code to symbolic name mapping guids in named_map are really constant references to the above

YAML.load_file "#{SUPPORT_DIR}/data/mapitags.yaml"
NAMED_MAP =
YAML.load_file("#{SUPPORT_DIR}/data/named_map.yaml").inject({}) do |hash, (key, value)|
	hash.update Key.new(key[0], const_get(key[1])) => value
end

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(raw) ⇒ PropertySet

raw should be an hash-like object that maps Keys to values. Should respond_to? [], keys, values, each, and optionally []=, and delete.



150
151
152
# File 'lib/mapi/property_set.rb', line 150

def initialize raw
	@raw = raw
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args) ⇒ Object



212
213
214
215
216
217
218
219
220
# File 'lib/mapi/property_set.rb', line 212

def method_missing name, *args
	if name.to_s !~ /\=$/ and args.empty?
		self[name]
	elsif name.to_s =~ /(.*)\=$/ and args.length == 1
		self[$1] = args[0]
	else
		super
	end
end

Instance Attribute Details

#rawObject (readonly)

Returns the value of attribute raw.



146
147
148
# File 'lib/mapi/property_set.rb', line 146

def raw
  @raw
end

Instance Method Details

#[](arg, guid = nil) ⇒ Object



200
201
202
# File 'lib/mapi/property_set.rb', line 200

def [] arg, guid=nil
	raw[resolve(arg, guid)]
end

#[]=(arg, *args) ⇒ Object



204
205
206
207
208
209
210
# File 'lib/mapi/property_set.rb', line 204

def []= arg, *args
	args.unshift nil if args.length == 1
	guid, value = args
	# FIXME this won't really work properly. it would need to go
	# to TAGS to resolve, as it often won't be there already...
	raw[resolve(arg, guid)] = value
end

#bodyObject

for providing rtf to plain text conversion. later, html to text too.



238
239
240
241
242
243
244
245
246
247
# File 'lib/mapi/property_set.rb', line 238

def body
	return @body if defined?(@body)
	@body = (self[:body] rescue nil)
	# last resort
	if !@body or @body.strip.empty?
		Log.warn 'creating text body from rtf'
		@body = (::RTF::Converter.rtf2text body_rtf rescue nil)
	end
	@body
end

#body_htmlObject

for providing rtf to html conversion



256
257
258
259
260
261
262
263
264
265
266
# File 'lib/mapi/property_set.rb', line 256

def body_html
	return @body_html if defined?(@body_html)
	@body_html = (self[:body_html].read rescue nil)
	@body_html = (RTF.rtf2html body_rtf rescue nil) if !@body_html or @body_html.strip.empty?
	# last resort
	if !@body_html or @body_html.strip.empty?
		Log.warn 'creating html body from rtf'
		@body_html = (::RTF::Converter.rtf2text body_rtf, :html rescue nil)
	end
	@body_html
end

#body_rtfObject

for providing rtf decompression



250
251
252
253
# File 'lib/mapi/property_set.rb', line 250

def body_rtf
	return @body_rtf if defined?(@body_rtf)
	@body_rtf = (RTF.rtfdecompr rtf_compressed.read rescue nil)
end

#inspectObject



226
227
228
229
230
231
# File 'lib/mapi/property_set.rb', line 226

def inspect
	"#<#{self.class} " + to_h.sort_by { |k, v| k.to_s }.map do |k, v|
		v = v.inspect
		"#{k}=#{v.length > 32 ? v[0..29] + '..."' : v}"
	end.join(' ') + '>'
end

#keysObject



192
193
194
# File 'lib/mapi/property_set.rb', line 192

def keys
	sym_to_key.keys
end

#resolve(arg, guid = nil) ⇒ Object

resolve arg (could be key, code, string, or symbol), and possible guid to a key. returns nil on failure



156
157
158
159
160
161
162
163
164
165
# File 'lib/mapi/property_set.rb', line 156

def resolve arg, guid=nil
	if guid;        Key.new arg, guid
	else
		case arg
		when Key;     arg
		when Integer; Key.new arg
		else          sym_to_key[arg.to_sym]
		end
	end
end

#sym_to_keyObject

this is the function that creates a symbol to key mapping. currently this works by making a pass through the raw properties, but conceivably you could map symbols to keys using the mapitags directly. problem with that would be that named properties wouldn’t map automatically, but maybe thats not too important.



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/mapi/property_set.rb', line 171

def sym_to_key
	return @sym_to_key if @sym_to_key
	@sym_to_key = {}
	raw.keys.each do |key|
		sym = key.to_sym
		unless Symbol === sym
			Log.debug "couldn't find symbolic name for key #{key.inspect}" 
			next
		end
		if @sym_to_key[sym]
			Log.warn "duplicate key #{key.inspect}"
			# we give preference to PS_MAPI keys
			@sym_to_key[sym] = key if key.guid == PS_MAPI
		else
			# just assign
			@sym_to_key[sym] = key
		end
	end
	@sym_to_key
end

#to_hObject



222
223
224
# File 'lib/mapi/property_set.rb', line 222

def to_h
	sym_to_key.inject({}) { |hash, (sym, key)| hash.update sym => raw[key] }
end

#valuesObject



196
197
198
# File 'lib/mapi/property_set.rb', line 196

def values
	sym_to_key.values.map { |key| raw[key] }
end