Class: Mapi::PropertySet
- Inherits:
-
Object
- Object
- Mapi::PropertySet
- 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
- #raw ⇒ Hash readonly
Instance Method Summary collapse
- #[](arg, guid = nil) ⇒ Object
- #[]=(arg, *args) ⇒ Object
-
#body ⇒ String?
for providing rtf to plain text conversion.
-
#body_html ⇒ String?
for providing rtf to html extraction or conversion.
-
#body_rtf ⇒ String?
for providing rtf decompression.
- #decode_ansi_str(str) ⇒ Object
-
#initialize(raw) ⇒ PropertySet
constructor
raw
should be an hash-like object that mapsKey
s to values. - #inspect ⇒ Object
- #keys ⇒ Object
- #method_missing(name, *args) ⇒ Object
-
#resolve(arg, guid = nil) ⇒ Key
resolve
arg
(could be key, code, string, or symbol), and possibleguid
to a key. -
#sym_to_key ⇒ Hash{Symbol => Key}
this is the function that creates a symbol to key mapping.
- #to_h ⇒ Object
- #values ⇒ Object
Constructor Details
#initialize(raw) ⇒ PropertySet
raw
should be an hash-like object that maps Key
s to values. Should respond_to? [], keys, values, each, and optionally []=, and delete.
164 165 166 |
# File 'lib/mapi/property_set.rb', line 164 def initialize raw @raw = raw end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(name, *args) ⇒ Object
232 233 234 235 236 237 238 239 240 |
# File 'lib/mapi/property_set.rb', line 232 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
#raw ⇒ Hash (readonly)
158 159 160 |
# File 'lib/mapi/property_set.rb', line 158 def raw @raw end |
Instance Method Details
#[](arg, guid = nil) ⇒ Object
220 221 222 |
# File 'lib/mapi/property_set.rb', line 220 def [] arg, guid=nil raw[resolve(arg, guid)] end |
#[]=(arg, *args) ⇒ Object
224 225 226 227 228 229 230 |
# File 'lib/mapi/property_set.rb', line 224 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 |
#body ⇒ String?
for providing rtf to plain text conversion. later, html to text too.
268 269 270 271 272 273 274 275 276 277 |
# File 'lib/mapi/property_set.rb', line 268 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 = decode_ansi_str(RTF::Converter.rtf2text body_rtf) rescue nil end @body end |
#body_html ⇒ String?
for providing rtf to html extraction or conversion
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 |
# File 'lib/mapi/property_set.rb', line 298 def body_html return @body_html if defined?(@body_html) @body_html = self[:body_html] # sometimes body_html is a stream, and sometimes a string @body_html = @body_html.read if @body_html.respond_to?(:read) @body_html = nil if @body_html.to_s.strip.empty? if body_rtf and !@body_html begin #https://github.com/l3akage/ruby-msg/commit/90865e091b21c4b738dcba45015ddbbf7b3d3fb3 #@body_html = RTF.rtf2html body_rtf @body_html = decode_ansi_str(RTF.rtf2html body_rtf) rescue => e Log.warn "unable to extract html from rtf: #{e.class} #{e.}" end if !@body_html # Log.warn 'creating html body from rtf' begin #https://github.com/l3akage/ruby-msg/commit/90865e091b21c4b738dcba45015ddbbf7b3d3fb3 #@body_html = RTF::Converter.rtf2text body_rtf, :html @body_html = decode_ansi_str(RTF::Converter.rtf2text body_rtf, :html) rescue Log.warn "unable to convert rtf to html #{e.class} #{e.}" end end end #https://github.com/l3akage/ruby-msg/commit/90865e091b21c4b738dcba45015ddbbf7b3d3fb3 #@body_html.force_encoding("iso-8859-1").encode('utf-8') if @body_html && @body_html.respond_to?(:encoding) @body_html end |
#body_rtf ⇒ String?
for providing rtf decompression
282 283 284 285 286 287 288 289 290 291 292 293 |
# File 'lib/mapi/property_set.rb', line 282 def body_rtf return @body_rtf if defined?(@body_rtf) @body_rtf = nil if self[:rtf_compressed] begin @body_rtf = decode_ansi_str(RTF.rtfdecompr self[:rtf_compressed].read) rescue => e Log.warn 'unable to decompress rtf' end end @body_rtf end |
#decode_ansi_str(str) ⇒ Object
253 254 255 256 257 258 259 |
# File 'lib/mapi/property_set.rb', line 253 def decode_ansi_str str if defined? raw.helper raw.helper.convert_ansi_str(str) else str end end |
#inspect ⇒ Object
246 247 248 249 250 251 |
# File 'lib/mapi/property_set.rb', line 246 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 |
#keys ⇒ Object
212 213 214 |
# File 'lib/mapi/property_set.rb', line 212 def keys sym_to_key.keys end |
#resolve(arg, guid = nil) ⇒ Key
resolve arg
(could be key, code, string, or symbol), and possible guid
to a key. returns nil on failure
174 175 176 177 178 179 180 181 182 183 |
# File 'lib/mapi/property_set.rb', line 174 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_key ⇒ Hash{Symbol => Key}
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.
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
# File 'lib/mapi/property_set.rb', line 191 def sym_to_key return @sym_to_key if defined? @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_h ⇒ Object
242 243 244 |
# File 'lib/mapi/property_set.rb', line 242 def to_h sym_to_key.inject({}) { |hash, (sym, key)| hash.update sym => raw[key] } end |
#values ⇒ Object
216 217 218 |
# File 'lib/mapi/property_set.rb', line 216 def values sym_to_key.values.map { |key| raw[key] } end |