aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/ruby-msg
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/ruby-msg')
-rw-r--r--vendor/ruby-msg/ChangeLog82
-rw-r--r--vendor/ruby-msg/FIXES56
-rw-r--r--vendor/ruby-msg/README128
-rw-r--r--vendor/ruby-msg/Rakefile77
-rw-r--r--vendor/ruby-msg/TODO184
-rwxr-xr-xvendor/ruby-msg/bin/mapitool195
-rw-r--r--vendor/ruby-msg/contrib/rtf2html.c155
-rw-r--r--vendor/ruby-msg/contrib/rtfdecompr.c105
-rw-r--r--vendor/ruby-msg/contrib/wmf.rb107
-rw-r--r--vendor/ruby-msg/data/mapitags.yaml4168
-rw-r--r--vendor/ruby-msg/data/named_map.yaml114
-rw-r--r--vendor/ruby-msg/data/types.yaml15
-rw-r--r--vendor/ruby-msg/lib/mapi.rb109
-rw-r--r--vendor/ruby-msg/lib/mapi/convert.rb61
-rw-r--r--vendor/ruby-msg/lib/mapi/convert/contact.rb142
-rw-r--r--vendor/ruby-msg/lib/mapi/convert/note-mime.rb274
-rw-r--r--vendor/ruby-msg/lib/mapi/convert/note-tmail.rb287
-rw-r--r--vendor/ruby-msg/lib/mapi/msg.rb440
-rw-r--r--vendor/ruby-msg/lib/mapi/property_set.rb269
-rw-r--r--vendor/ruby-msg/lib/mapi/pst.rb1806
-rw-r--r--vendor/ruby-msg/lib/mapi/rtf.rb169
-rw-r--r--vendor/ruby-msg/lib/mapi/types.rb51
-rw-r--r--vendor/ruby-msg/lib/mime.rb165
-rw-r--r--vendor/ruby-msg/lib/orderedhash.rb218
-rwxr-xr-xvendor/ruby-msg/lib/rtf.rb109
25 files changed, 0 insertions, 9486 deletions
diff --git a/vendor/ruby-msg/ChangeLog b/vendor/ruby-msg/ChangeLog
deleted file mode 100644
index fb502127a..000000000
--- a/vendor/ruby-msg/ChangeLog
+++ /dev/null
@@ -1,82 +0,0 @@
-== 1.4.0 / 2008-10-12
-
-- Initial simple msg test case.
-- Update TODO, stripping out all the redundant ole stuff.
-- Fix property set guids to use the new Ole::Types::Clsid type.
-- Add block form of Msg.open
-- Fix file requires for running tests individually.
-- Update pst RangesIO subclasses for changes in ruby-ole.
-- Merge initial pst reading code (converted from libpst).
-- Pretty big pst refactoring, adding initial outlook 2003 pst support.
-- Flesh out move to mapi to clean up the way pst hijacks the msg
- classes currently.
-- Add a ChangeLog :).
-- Update README, by converting Home.wiki with wiki2rdoc converter.
-- Separate out generic mapi object code from msg code, and separate out
- conversion code.
-- Add decent set of Mapi and Msg unit tests, approaching ~55% code coverage,
- not including pst.
-- Add TMail note conversion alternative, to eventually allow removal of
- custom Mime class.
-- Expose experimental pst support through renamed mapitool program.
-
-== 1.3.1 / 2007-08-21
-
-- Add fix for issue #2, and #4.
-- Move ole code to ruby-ole project, and depend on it.
-
-== 1.2.17 / 2007-05-13
-
-(This was last release before splitting out ruby-ole. subsequent bug fix
-point releases 1-3 were made directly on the gem, not reflected in the
-repository, though the fixes were also forward-ported.)
-
-- Update Ole::Storage backend, finalising api for split to separate
- library.
-
-== 1.2.16 / 2007-04-28
-
-- Some minor fixes to msg parser.
-- Extending RTF and body conversion support.
-- Initial look at possible wmf conversion for embedded images.
-- Add initial cli converter tool
-- Add rdoc to ole/storage, and msg/properties
-- Add streaming IO support to Ole::Storage, and use it in Msg::Properties
-- Updates to test cases
-- Add README, and update TODO
-- Convert rtf support tools in c to small ruby class.
-- Merge preliminary write support for Ole::Storage, as well as preliminary
- filesystem api.
-
-== 1.2.13 / 2007-01-22
-
-- Nested msg support
-
-== 1.2.10 / 2007-01-21
-
-- Add initial vcard support.
-- Implement a named properties map, for vcard conversion.
-- Add orderedhash to Mime for keeping header order
-- Fix line endings in lib/mime
-- First released version
-
-== <= 1.2.9 / 2007-01-11..2007-01-19
-
-(Haven't bothered to note exact versions and dates - nothing here was released.
-can look at history of lib/msg.rb to see exact VERSION at each commit.)
-
-- Merged most of the named property work.
-- Added some test files.
-- Update svn:ignore, to exclude test messages and ole files which I can't
- release. Need to get some clean files for use in test cases.
- Also excluding source to the mapitags files for the moment.
- A lot of it is not redistributable
-- Added a converter to extract embedded html in rtf. Downloaded somewhere,
- source unknown.
-- Minor fix to ole/storage.rb, after new OleDir#type behaviour
-- Imported support.rb, replacing previously required std.rb
-- Added initial support for parsing times in Msg::Properties.
-- Imported some rtf decompression code and minor updates.
-- Cleaned up the ole class a bit
-- Fixed OleDir#data method using sb_blocks map (see POLE).
-
diff --git a/vendor/ruby-msg/FIXES b/vendor/ruby-msg/FIXES
deleted file mode 100644
index 7a094b437..000000000
--- a/vendor/ruby-msg/FIXES
+++ /dev/null
@@ -1,56 +0,0 @@
-FIXES
-
-recent fixes based on importing results into evolution
-
-1. was running into some issue with base64 encoded message/rfc822 attachments displaying
- as empty. encoding them as plain solved the issue (odd).
-
-2. problem with a large percentage of emails, not displaying as mime. turned out to be
- all received from blackberry. further, turned out there was 2 content-type headers,
- "Content-Type", which I add, and "Content-type". normally my override works, but I
- need to handle it case insensitvely it would appear. more tricky, whats the story
- with these. fixing that will probably fix that whole class of issues there.
- evolution was renaming my second content type as X-Invalid-Content-Type or something.
-
-3. another interesting one. had content-transfer-encoding set in the transport message
- headers. it was set to base64. i didn't override that, so evolution "decoded" my
- plaintext message into complete garbage.
- fix - delete content-transfer-encoding.
-
-4. added content-location and content-id output in the mime handling of attachments
- to get some inline html/image mails to work properly.
- further, the containing mime content-type must be multipart/related, not multipart/mixed,
- at least for evolution, in order for the images to appear inline.
- could still improve in this area. if someone drags and drops in an image, it may
- be inline in the rtf version, but exchanges generates crappy html such that the image
- doesn't display inline. maybe i should correct the html output in these cases as i'm
- throwing away the rtf version.
-
-5. note you may need wingdings installed. i had a lot of L and J appear in messages from
- outlook users. turns out its smilies in wingdings. i think its only if word is used
- as email editor and has autotext messing things up.
-
-6. still unsure about how to do my "\r" handling.
-
-7. need to join addresses with , instead of ; i think. evolution only shows the
- first one otherwise it appears, but all when they are , separated.
-
-8. need to solve ole storage issues with the very large file using extra bat
- stuff.
-
-9. retest a bit on evolution and thunderbird, and release. tested on a corups
- of >1000 msg files, so should be starting to get pretty good quality.
-
-10. longer term, things fall into a few basic categories:
-
-- non mail conversions (look further into vcard, ical et al support for other
- types of msg)
-- further tests and robustness for what i handle now. ie, look into corner
- cases covered so far, and work on the mime code. fix random charset encoding
- issues, in the various weird mime ways, do header wrapping etc etc.
- check fidelity of conversions, and capture some more properties as headers,
- such as importance which i don't do yet.
-- fix that named property bug. tidy up warnings, exceptions.
-- extend conversion to make better html.
- this is longer term. as i don't use the rtf, i need to make my html better.
- emulating some rtf things. harder, not important atm.
diff --git a/vendor/ruby-msg/README b/vendor/ruby-msg/README
deleted file mode 100644
index bd16dfcc4..000000000
--- a/vendor/ruby-msg/README
+++ /dev/null
@@ -1,128 +0,0 @@
-= Introduction
-
-Generally, the goal of the project is to enable the conversion of
-msg and pst files into standards based formats, without reliance on
-outlook, or any platform dependencies. In fact its currently <em>pure
-ruby</em>, so it should be easy to get running.
-
-It is targeted at people who want to migrate their PIM data from outlook,
-converting msg and pst files into rfc2822 emails, vCard contacts,
-iCalendar appointments etc. However, it also aims to be a fairly complete
-mapi message store manipulation library, providing a sane model for
-(currently read-only) access to msg and pst files (message stores).
-
-I am happy to accept patches, give commit bits etc.
-
-Please let me know how it works for you, any feedback would be welcomed.
-
-= Features
-
-Broad features of the project:
-
-* Can be used as a general mapi library, where conversion to and working
- on a standard format doesn't make sense.
-
-* Supports conversion of messages to standard formats, like rfc2822
- emails, vCard, etc.
-
-* Well commented, and easily extended.
-
-* Basic RTF converter, for providing a readable body when only RTF
- exists (needs work)
-
-* RTF decompression support included, as well as HTML extraction from
- RTF where appropriate (both in pure ruby, see <tt>lib/mapi/rtf.rb</tt>)
-
-* Support for mapping property codes to symbolic names, with many
- included.
-
-Features of the msg format message store:
-
-* Most key .msg structures are understood, and the only the parsing
- code should require minor tweaks. Most of remaining work is in achieving
- high-fidelity conversion to standards formats (see [TODO]).
-
-* Supports both types of property storage (large ones in +substg+
- files, and small ones in the +properties+ file.
-
-* Complete support for named properties in different GUID namespaces.
-
-* Initial support for handling embedded ole files, converting nested
- .msg files to message/rfc822 attachments, and serializing others
- as ole file attachments (allows you to view embedded excel for example).
-
-Features of the pst format message store:
-
-* Handles both Outlook 1997 & 2003 format pst files, both with no-
- and "compressible-" encryption.
-
-* Understanding of the file format is still very superficial.
-
-= Usage
-
-At the command line, it is simple to convert individual msg or pst
-files to .eml, or to convert a batch to an mbox format file. See mapitool
-help for details:
-
- mapitool -si some_email.msg > some_email.eml
- mapitool -s *.msg > mbox
-
-There is also a fairly complete and easy to use high level library
-access:
-
- require 'mapi/msg'
-
- msg = Mapi::Msg.open filename
-
- # access to the 3 main data stores, if you want to poke with the msg
- # internals
- msg.recipients
- # => [#<Recipient:'\'Marley, Bob\' <bob.marley@gmail.com>'>]
- msg.attachments
- # => [#<Attachment filename='blah1.tif'>, #<Attachment filename='blah2.tif'>]
- msg.properties
- # => #<Properties ... normalized_subject='Testing' ...
- # creation_time=#<DateTime: 2454042.45074714,0,2299161> ...>
-
-To completely abstract away all msg peculiarities, convert the msg
-to a mime object. The message as a whole, and some of its main parts
-support conversion to mime objects.
-
- msg.attachments.first.to_mime
- # => #<Mime content_type='application/octet-stream'>
- mime = msg.to_mime
- puts mime.to_tree
- # =>
- - #<Mime content_type='multipart/mixed'>
- |- #<Mime content_type='multipart/alternative'>
- | |- #<Mime content_type='text/plain'>
- | \- #<Mime content_type='text/html'>
- |- #<Mime content_type='application/octet-stream'>
- \- #<Mime content_type='application/octet-stream'>
-
- # convert mime object to serialised form,
- # inclusive of attachments etc. (not ideal in memory, but its wip).
- puts mime.to_s
-
-= Thanks
-
-* The initial implementation of parsing msg files was based primarily
- on msgconvert.pl[http://www.matijs.net/software/msgconv/].
-
-* The basis for the outlook 97 pst file was the source to +libpst+.
-
-* The code for rtf decompression was implemented by inspecting the
- algorithm used in the +JTNEF+ project.
-
-= Other
-
-For more information, see
-
-* TODO
-
-* MsgDetails[http://code.google.com/p/ruby-msg/wiki/MsgDetails]
-
-* PstDetails[http://code.google.com/p/ruby-msg/wiki/PstDetails]
-
-* OleDetails[http://code.google.com/p/ruby-ole/wiki/OleDetails]
-
diff --git a/vendor/ruby-msg/Rakefile b/vendor/ruby-msg/Rakefile
deleted file mode 100644
index 066ca3741..000000000
--- a/vendor/ruby-msg/Rakefile
+++ /dev/null
@@ -1,77 +0,0 @@
-require 'rake/rdoctask'
-require 'rake/testtask'
-require 'rake/packagetask'
-require 'rake/gempackagetask'
-
-require 'rbconfig'
-require 'fileutils'
-
-$:.unshift 'lib'
-
-require 'mapi/msg'
-
-PKG_NAME = 'ruby-msg'
-PKG_VERSION = Mapi::VERSION
-
-task :default => [:test]
-
-Rake::TestTask.new(:test) do |t|
- t.test_files = FileList["test/test_*.rb"] - ['test/test_pst.rb']
- t.warning = false
- t.verbose = true
-end
-
-begin
- require 'rcov/rcovtask'
- # NOTE: this will not do anything until you add some tests
- desc "Create a cross-referenced code coverage report"
- Rcov::RcovTask.new do |t|
- t.test_files = FileList['test/test*.rb']
- t.ruby_opts << "-Ilib" # in order to use this rcov
- t.rcov_opts << "--xrefs" # comment to disable cross-references
- t.rcov_opts << "--exclude /usr/local/lib/site_ruby"
- t.verbose = true
- end
-rescue LoadError
- # Rcov not available
-end
-
-Rake::RDocTask.new do |t|
- t.rdoc_dir = 'doc'
- t.title = "#{PKG_NAME} documentation"
- t.options += %w[--main README --line-numbers --inline-source --tab-width 2]
- t.rdoc_files.include 'lib/**/*.rb'
- t.rdoc_files.include 'README'
-end
-
-spec = Gem::Specification.new do |s|
- s.name = PKG_NAME
- s.version = PKG_VERSION
- s.summary = %q{Ruby Msg library.}
- s.description = %q{A library for reading and converting Outlook msg and pst files (mapi message stores).}
- s.authors = ["Charles Lowe"]
- s.email = %q{aquasync@gmail.com}
- s.homepage = %q{http://code.google.com/p/ruby-msg}
- s.rubyforge_project = %q{ruby-msg}
-
- s.executables = ['mapitool']
- s.files = FileList['data/*.yaml', 'Rakefile', 'README', 'FIXES']
- s.files += FileList['lib/**/*.rb', 'test/test_*.rb', 'bin/*']
-
- s.has_rdoc = true
- s.extra_rdoc_files = ['README']
- s.rdoc_options += ['--main', 'README',
- '--title', "#{PKG_NAME} documentation",
- '--tab-width', '2']
-
- s.add_dependency 'ruby-ole', '>=1.2.8'
- s.add_dependency 'vpim', '>=0.360'
-end
-
-Rake::GemPackageTask.new(spec) do |p|
- p.gem_spec = spec
- p.need_tar = false #true
- p.need_zip = false
- p.package_dir = 'build'
-end
-
diff --git a/vendor/ruby-msg/TODO b/vendor/ruby-msg/TODO
deleted file mode 100644
index 2e4309c21..000000000
--- a/vendor/ruby-msg/TODO
+++ /dev/null
@@ -1,184 +0,0 @@
-= Newer Msg
-
-* a lot of the doc is out of sync given the Mapi:: changes lately. need to
- fix.
-
-* extend msgtool with support for the other kinds of msg files. things like,
- - dump properties to yaml / xml (with option on how the keys should be dumped).
- should be fairly easy to implement. hash, with array of attach & recips.
- - just write out the mime type
- - convert to the automatically guessed output type, also allowing an override
- of some sort. have a batch mode that converts all arguments, using automatic
- extensions, eg .vcf, .eml etc.
- - options regarding preferring rtf / html / txt output for things. like, eg
- the output for note conversion, will be one of them i guess.
-
-* fix nameid handling for sub Properties objects. needs to inherit top-level @nameid.
-
-* better handling for "Untitled Attachments", or attachments with no filename at all.
- maybe better handling in general for attached messages. maybe re-write filename with
- subject.
-
-* do the above 2, then go for a new release.
- for this release, i want it to be pretty robust, and have better packaging.
- have the gem install 2 tools - oletool (--tree, --read, --write, --repack)
- and msgtool (--convert, --dump-attachments etc)
- fix docs, add option parsing etc etc.
- submit to the usual gem repositories etc, announce project as usable.
-
-* better handling for other types of embedded ole attachments. try to recognise .wmf to
- start with. etc. 2 goals, form .eml, and for .rtf output.
-
-* rtf to text support, initially. just simple strip all the crap out approach. maybe html
- later on. sometimes the only body is rtf, which is problematic. is rtf an allowed body
- type?
-
-* better encoding support (i'm thinking, check some things like m-dash in the non-wide
- string type. is it windows codepage?. how to know code page in general? change
- ENCODER[] to convert these to utf8 i think). I'm trying to just move everything to utf8.
- then i need to make sure the mime side of that holds up.
-
-* certain parsing still not correct, especially small properties directory,
- things like bools, multiavlue shorts etc.
-
-* test cases for msg/properties.rb msg/rtf.rb and mime.rb
-* regarding the mime fix ups. check out:
-
-- http://rubyforge.org/projects/rubymail/, and
-- http://rubyforge.org/projects/mime-alt-lite/
-
-both haven't been touched in 2 years though.
-
-* get some sort of working From: header
- there doesn't appear to be a way to get an smtp address for the sender in an
- internal mail. you need to query the exchanger server, using the sender_entryid
- as far as i can tell.
- you may also get these so called "X.400" addresses for external recipients
- that are on the GAL (Global Address List), as custom recipients.
- (mostly complete. better way?)
-* check lots of little details: encoding issues with code pages etc other
- than ascii stuff. consider Msg, Mime, Vcard etc, to check its clean. check
- timezones, inaccuracies in date handling.
-* http://msdn2.microsoft.com/en-us/library/ms526761.aspx
- import guids for these?
-PS_ROUTING_EMAIL_ADDRESSES
-PS_ROUTING_ADDRTYPE
-PS_ROUTING_SEARCH_KEY
-PS_ROUTING_DISPLAY_NAME
-PS_ROUTING_ENTRYID
-* check this thing i wrote:
- creating ones in ps_mapi works strangely, as ps_mapi is the top levels ones.
- don't get entries in nameid. as should match the definitions.
- however still get allocated an 8 number. that number becomes permanent...
-* do something about the message class-specific range, 0x6800 through 0x7FFF.
-* multivalue encoding may explain some of the unknown data in properties objs
-* get some clean test msgs from somewhere
-* tackle other types of msgs, such as appointments, contacts etc, converting
- to their appropriate standard types too.
- would like to look at this next. see about the content specific range
- http://en.wikipedia.org/wiki/ICalendar
- is it better/easier to go through micro formats? there are libraries for ruby
- already i think. check this out. although using IMC standards seem to make
- more sense. encoding issues?
- try contact => vcf,
- note => ?? icalendar. VTODO? / VJOURNAL?
- appointment => VEVENT?
- (initial try done. really basic vcard output supported. not difficult)
-* ole code is suppopsed to be able to return guids for something too. supporting
- all this probably means creating a new file for ole/types.rb, containing all
- the various classes, and binary conversion code.
-* some dubiously useful info about some unknown codes i wrote:
-unknown property 5ff6
- * appears to be equal to display_name, and transmitable_display_name
-unknown property 5ff7
- * recipient.properties[0x5ff7].upcase == recipient.properties.entryid
- is equivalent, but not all uppercase.
- everything else is upper though. maybe a displayname kind of thing.
-* common relationship
- CGI.escape(msg.properties.subject.strip).tr('+', ' ') + '.EML' == msg.properties.url_comp_name
-
-(28 bytes binary data and msg.properties.sender_email_address and "\000")
-entryids are a strange format. for internal or from exchange whatever, they have that
-EX:/O=XXXXXX/...
-otherwise, they may have SMTP in them.
- such as msg.properties.sent_representing_search_key
- == "SMTP:SOMEGUY@XXX.COM\000"
- but Ole::UTF16_TO_UTF8[msg2.properties.sender_entryid[/.*\000\000(.+)\000/, 1][0..-2]]
- == "SomeGuy@XXX.COM"
- for external people, entry ids have displayname and address.
-
-longer term, i want to investigate the overlap with PST stuff, like libpst,
-which seems to be another kind of mapi tag property storage, and try to
-understand the relationship with existing TNEF work also.
-
-hmmmm for future work:
-http://blogs.msdn.com/stephen_griffin/archive/2005/10/25/484656.aspx
-
-
-= Older
-
-- set 'From' in Msg#populate_headers.
- Notes:
- # ways to get the sender of a mail.
- # if external, you can do this (different for internal).
- name, protocol, email = Ole::UTF16_TO_UTF8[msg.props.sender_entryid[24..-1]].split(/\x00/)
- # here is an excerpt from a yaml dump.
- # need to consider how to get it. also when its a draft, and other stuff.
- creator_name:
- sent_representing_name:
- last_modifier_name:
- sender_email_address:
- sent_representing_email_address:
- sender_name:
-- fill out some of the tag holes. mostly done
-- properly integrate rtf decompression, and the html conversion from rtf
-- figure out some things, like entryids, and the properties directories,
- and the ntsecurity data 0e27.
- http://peach.ease.lsoft.com/scripts/wa.exe?A2=ind0207&L=mapi-l&P=24515
- "In case anybody is interested, Exchange stores PR_NT_SECURITY_DESCRIPTOR as a header plus the regular self=-relative SECURITY_DESCRIPTOR structure. The first two bytes of the header (WORD) is the length of the header; Read these two bytes to find out how many bytes you must skip to get to the real data. Many thanks to Stephen Griffin for this info."
- using outlook spy gives an actual dump. for example:
- <<
- Control: SE_DACL_AUTO_INHERITED | SE_DACL_DEFAULTED | SE_DACL_PRESENT | SE_GROUP_DEFAULTED | SE_OWNER_DEFAULTED | SE_SACL_AUTO_INHERITED | SE_SACL_DEFAULTED | SE_SELF_RELATIVE
- Owner:
- SID: S-1-5-21-1004336348-602609370-725345543-44726
- Name: lowecha
- DomainName: XXX
- Group:
- SID: S-1-5-21-1004336348-602609370-725345543-513
- Name: Domain Users
- DomainName: XXX
- Dacl:
- Header:
- AceType: ACCESS_DENIED_ACE_TYPE
- AceFlags: INHERITED_ACE
- Mask: fsdrightReadBody (fsdrightListContents) | fsdrightWriteBody (fsdrightCreateItem) | fsdrightAppendMsg (fsdrightCreateContainer) | fsdrightReadProperty | fsdrightWriteProperty | fsdrightExecute | fsdrightReadAttributes | fsdrightWriteAttributes | fsdrightWriteOwnProperty | fsdrightDeleteOwnItem | fsdrightViewItem | fsdrightWriteSD | fsdrightDelete | fsdrightWriteOwner | fsdrightReadControl | fsdrightSynchronize
- Sid:
- SID: S-1-5-7
- Name: ANONYMOUS LOGON
- DomainName: NT AUTHORITY
- >>
- Not something i care about at the moment.
-
-- conversion of inline images.
- Content-Location, cid: urls etc etc.
- what would be cool, is conversion of outlooks text/rtf's only real "feature" over
- text/html - convert inline attachments to be <a href> links, using cid: urls to the
- actual content data, and using an <img with cid: url to a converted image from the
- attach_rendering property (image data), along with the text itself. (although i think
- the rendering may actually include the text ??. that would explain why its always clipped.
- can these be used for contact pictures too?
-- entryid format cf. entry_id.h. another serialized structure.
- entryids are for the addressbook connection. EMS (exchange message something), AB
- address book. MUIDEMSAB. makes sense.
-
-mapidefs.h:
-
- 174 /* Types of message receivers */
- 175 #ifndef MAPI_ORIG
- 176 #define MAPI_ORIG 0 /* The original author */
- 177 #define MAPI_TO 1 /* The primary message receiver */
- 178 #define MAPI_CC 2 /* A carbon copy receiver */
- 179 #define MAPI_BCC 3 /* A blind carbon copy receiver */
- 180 #define MAPI_P1 0x10000000 /* A message resend */
- 181 #define MAPI_SUBMITTED 0x80000000 /* This message has already been sent */
- 182 #endif
diff --git a/vendor/ruby-msg/bin/mapitool b/vendor/ruby-msg/bin/mapitool
deleted file mode 100755
index 79824daa4..000000000
--- a/vendor/ruby-msg/bin/mapitool
+++ /dev/null
@@ -1,195 +0,0 @@
-#! /usr/bin/ruby
-
-$:.unshift File.dirname(__FILE__) + '/../lib'
-
-require 'optparse'
-require 'rubygems'
-require 'mapi/msg'
-require 'mapi/pst'
-require 'mapi/convert'
-require 'time'
-
-class Mapitool
- attr_reader :files, :opts
- def initialize files, opts
- @files, @opts = files, opts
- seen_pst = false
- raise ArgumentError, 'Must specify 1 or more input files.' if files.empty?
- files.map! do |f|
- ext = File.extname(f.downcase)[1..-1]
- raise ArgumentError, 'Unsupported file type - %s' % f unless ext =~ /^(msg|pst)$/
- raise ArgumentError, 'Expermiental pst support not enabled' if ext == 'pst' and !opts[:enable_pst]
- [ext.to_sym, f]
- end
- if dir = opts[:output_dir]
- Dir.mkdir(dir) unless File.directory?(dir)
- end
- end
-
- def each_message(&block)
- files.each do |format, filename|
- if format == :pst
- if filter_path = opts[:filter_path]
- filter_path = filter_path.tr("\\", '/').gsub(/\/+/, '/').sub(/^\//, '').sub(/\/$/, '')
- end
- open filename do |io|
- pst = Mapi::Pst.new io
- pst.each do |message|
- next unless message.type == :message
- if filter_path
- next unless message.path =~ /^#{Regexp.quote filter_path}(\/|$)/i
- end
- yield message
- end
- end
- else
- Mapi::Msg.open filename, &block
- end
- end
- end
-
- def run
- each_message(&method(:process_message))
- end
-
- def make_unique filename
- @map ||= {}
- return @map[filename] if !opts[:individual] and @map[filename]
- try = filename
- i = 1
- try = filename.gsub(/(\.[^.]+)$/, ".#{i += 1}\\1") while File.exist?(try)
- @map[filename] = try
- try
- end
-
- def process_message message
- # TODO make this more informative
- mime_type = message.mime_type
- return unless pair = Mapi::Message::CONVERSION_MAP[mime_type]
-
- combined_map = {
- 'eml' => 'Mail.mbox',
- 'vcf' => 'Contacts.vcf',
- 'txt' => 'Posts.txt'
- }
-
- # TODO handle merged mode, pst, etc etc...
- case message
- when Mapi::Msg
- if opts[:individual]
- filename = message.root.ole.io.path.gsub(/msg$/i, pair.last)
- else
- filename = combined_map[pair.last] or raise NotImplementedError
- end
- when Mapi::Pst::Item
- if opts[:individual]
- filename = "#{message.subject.tr ' ', '_'}.#{pair.last}".gsub(/[^A-Za-z0-9.()\[\]{}-]/, '_')
- else
- filename = combined_map[pair.last] or raise NotImplementedError
- filename = (message.path.tr(' /', '_.').gsub(/[^A-Za-z0-9.()\[\]{}-]/, '_') + '.' + File.extname(filename)).squeeze('.')
- end
- dir = File.dirname(message.instance_variable_get(:@desc).pst.io.path)
- filename = File.join dir, filename
- else
- raise
- end
-
- if dir = opts[:output_dir]
- filename = File.join dir, File.basename(filename)
- end
-
- filename = make_unique filename
-
- write_message = proc do |f|
- data = message.send(pair.first).to_s
- if !opts[:individual] and pair.last == 'eml'
- # we do the append > style mbox quoting (mboxrd i think its called), as it
- # is the only one that can be robuslty un-quoted. evolution doesn't use this!
- f.puts "From mapitool@localhost #{Time.now.rfc2822}"
- #munge_headers mime, opts
- data.each do |line|
- if line =~ /^>*From /o
- f.print '>' + line
- else
- f.print line
- end
- end
- else
- f.write data
- end
- end
-
- if opts[:stdout]
- write_message[STDOUT]
- else
- open filename, 'a', &write_message
- end
- end
-
- def munge_headers mime, opts
- opts[:header_defaults].each do |s|
- key, val = s.match(/(.*?):\s+(.*)/)[1..-1]
- mime.headers[key] = [val] if mime.headers[key].empty?
- end
- end
-end
-
-def mapitool
- opts = {:verbose => false, :action => :convert, :header_defaults => []}
- op = OptionParser.new do |op|
- op.banner = "Usage: mapitool [options] [files]"
- #op.separator ''
- #op.on('-c', '--convert', 'Convert input files (default)') { opts[:action] = :convert }
- op.separator ''
- op.on('-o', '--output-dir DIR', 'Put all output files in DIR') { |d| opts[:output_dir] = d }
- op.on('-i', '--[no-]individual', 'Do not combine converted files') { |i| opts[:individual] = i }
- op.on('-s', '--stdout', 'Write all data to stdout') { opts[:stdout] = true }
- op.on('-f', '--filter-path PATH', 'Only process pst items in PATH') { |path| opts[:filter_path] = path }
- op.on( '--enable-pst', 'Turn on experimental PST support') { opts[:enable_pst] = true }
- #op.on('-d', '--header-default STR', 'Provide a default value for top level mail header') { |hd| opts[:header_defaults] << hd }
- # --enable-pst
- op.separator ''
- op.on('-v', '--[no-]verbose', 'Run verbosely') { |v| opts[:verbose] = v }
- op.on_tail('-h', '--help', 'Show this message') { puts op; exit }
- end
-
- files = op.parse ARGV
-
- # for windows. see issue #2
- STDOUT.binmode
-
- Mapi::Log.level = Ole::Log.level = opts[:verbose] ? Logger::WARN : Logger::FATAL
-
- tool = begin
- Mapitool.new(files, opts)
- rescue ArgumentError
- puts $!
- puts op
- exit 1
- end
-
- tool.run
-end
-
-mapitool
-
-__END__
-
-mapitool [options] [files]
-
-files is a list of *.msg & *.pst files.
-
-one of the options should be some sort of path filter to apply to pst items.
-
---filter-path=
---filter-type=eml,vcf
-
-with that out of the way, the entire list of files can be converted into a
-list of items (with meta data about the source).
-
---convert
---[no-]separate one output file per item or combined output
---stdout
---output-dir=.
-
-
diff --git a/vendor/ruby-msg/contrib/rtf2html.c b/vendor/ruby-msg/contrib/rtf2html.c
deleted file mode 100644
index 937e22ff1..000000000
--- a/vendor/ruby-msg/contrib/rtf2html.c
+++ /dev/null
@@ -1,155 +0,0 @@
-#include <stdio.h>
-#define bool int
-#define false 0
-#define true 1
-
-// RTF/HTML functions
-// --------------------
-//
-// Sometimes in MAPI, the PR_BODY_HTML property contains the HTML of a message.
-// But more usually, the HTML is encoded inside the RTF body (which you get in the
-// PR_RTF_COMPRESSED property). These routines concern the decoding of the HTML
-// from this RTF body.
-//
-// An encoded htmlrtf file is a valid RTF document, but which contains additional
-// html markup information in its comments, and sometimes contains the equivalent
-// rtf markup outside the comments. Therefore, when it is displayed by a plain
-// simple RTF reader, the html comments are ignored and only the rtf markup has
-// effect. Typically, this rtf markup is not as rich as the html markup would have been.
-// But for an html-aware reader (such as the code below), we can ignore all the
-// rtf markup, and extract the html markup out of the comments, and get a valid
-// html document.
-//
-// There are actually two kinds of html markup in comments. Most of them are
-// prefixed by "\*\htmltagNNN", for some number NNN. But sometimes there's one
-// prefixed by "\*\mhtmltagNNN" followed by "\*\htmltagNNN". In this case,
-// the two are equivalent, but the m-tag is for a MIME Multipart/Mixed Message
-// and contains tags that refer to content-ids (e.g. img src="cid:072344a7")
-// while the normal tag just refers to a name (e.g. img src="fred.jpg")
-// The code below keeps the m-tag and discards the normal tag.
-// If there are any m-tags like this, then the message also contains an
-// attachment with a PR_CONTENT_ID property e.g. "072344a7". Actually,
-// sometimes the m-tag is e.g. img src="http://outlook/welcome.html" and the
-// attachment has a PR_CONTENT_LOCATION "http://outlook/welcome.html" instead
-// of a PR_CONTENT_ID.
-//
-// This code is experimental. It works on my own message archive, of about
-// a thousand html-encoded messages, received in Outlook97 and Outlook2000
-// and OutlookXP. But I can't guarantee that it will work on all rtf-encoded
-// messages. Indeed, it used to be the case that people would simply stick
-// {\fromhtml at the start of an html document, and } at the end, and send
-// this as RTF. If someone did this, then it will almost work in my function
-// but not quite. (Because I ignore \r and \n, and respect only \par. Thus,
-// any linefeeds in the erroneous encoded-html will be ignored.)
-
-
-
-
-
-// ISRTFHTML -- Given an uncompressed RTF body of the message, this
-// function tells you whether it encodes some html.
-// [in] (buf,*len) indicate the start and length of the uncompressed RTF body.
-// [return-value] true or false, for whether it really does encode some html
-bool isrtfhtml(const char *buf,unsigned int len)
-{ // We look for the words "\fromhtml" somewhere in the file.
- // If the rtf encodes text rather than html, then instead
- // it will only find "\fromtext".
- const char *c;
- for (c=buf; c<buf+len; c++)
- { if (strncmp(c,"\\from",5)==0) return strncmp(c,"\\fromhtml",9)==0;
- }
- return false;
-}
-
-
-
-
-// DECODERTFHTML -- Given an uncompressed RTF body of the message,
-// and assuming that it contains encoded-html, this function
-// turns it onto regular html.
-// [in] (buf,*len) indicate the start and length of the uncompressed RTF body.
-// [out] the buffer is overwritten with the HTML version, null-terminated,
-// and *len indicates the length of this HTML.
-//
-// Notes: (1) because of how the encoding works, the HTML version is necessarily
-// shorter than the encoded version. That's why it's safe for the function to
-// place the decoded html in the same buffer that formerly held the encoded stuff.
-// (2) Some messages include characters \'XX, where XX is a hexedecimal number.
-// This function simply converts this into ASCII. The conversion will only make
-// sense if the right code-page is being used. I don't know how rtf specifies which
-// code page it wants.
-// (3) By experiment, I discovered that \pntext{..} and \liN and \fi-N are RTF
-// markup that should be removed. There might be other RTF markup that should
-// also be removed. But I don't know what else.
-//
-void decodertfhtml(char *buf,unsigned int *len)
-{ // c -- pointer to where we're reading from
- // d -- pointer to where we're writing to. Invariant: d<c
- // max -- how far we can read from (i.e. to the end of the original rtf)
- // ignore_tag -- stores 'N': after \mhtmlN, we will ignore the subsequent \htmlN.
- char *c=buf, *max=buf+*len, *d=buf; int ignore_tag=-1;
- // First, we skip forwards to the first \htmltag.
- while (c<max && strncmp(c,"{\\*\\htmltag",11)!=0) c++;
- //
- // Now work through the document. Our plan is as follows:
- // * Ignore { and }. These are part of RTF markup.
- // * Ignore \htmlrtf...\htmlrtf0. This is how RTF keeps its equivalent markup separate from the html.
- // * Ignore \r and \n. The real carriage returns are stored in \par tags.
- // * Ignore \pntext{..} and \liN and \fi-N. These are RTF junk.
- // * Convert \par and \tab into \r\n and \t
- // * Convert \'XX into the ascii character indicated by the hex number XX
- // * Convert \{ and \} into { and }. This is how RTF escapes its curly braces.
- // * When we get \*\mhtmltagN, keep the tag, but ignore the subsequent \*\htmltagN
- // * When we get \*\htmltagN, keep the tag as long as it isn't subsequent to a \*\mhtmltagN
- // * All other text should be kept as it is.
- while (c<max)
- { if (*c=='{') c++;
- else if (*c=='}') c++;
- else if (strncmp(c,"\\*\\htmltag",10)==0)
- { c+=10; int tag=0; while (*c>='0' && *c<='9') {tag=tag*10+*c-'0'; c++;}
- if (*c==' ') c++;
- if (tag==ignore_tag) {while (c<max && *c!='}') c++; if (*c=='}') c++;}
- ignore_tag=-1;
- }
- else if (strncmp(c,"\\*\\mhtmltag",11)==0)
- { c+=11; int tag=0; while (*c>='0' && *c<='9') {tag=tag*10+*c-'0'; c++;}
- if (*c==' ') c++;
- ignore_tag=tag;
- }
- else if (strncmp(c,"\\par",4)==0) {strcpy(d,"\r\n"); d+=2; c+=4; if (*c==' ') c++;}
- else if (strncmp(c,"\\tab",4)==0) {strcpy(d," "); d+=3; c+=4; if (*c==' ') c++;}
- else if (strncmp(c,"\\li",3)==0)
- { c+=3; while (*c>='0' && *c<='9') c++; if (*c==' ') c++;
- }
- else if (strncmp(c,"\\fi-",4)==0)
- { c+=4; while (*c>='0' && *c<='9') c++; if (*c==' ') c++;
- }
- else if (strncmp(c,"\\'",2)==0)
- { unsigned int hi=c[2], lo=c[3];
- if (hi>='0' && hi<='9') hi-='0'; else if (hi>='A' && hi<='Z') hi-='A'; else if (hi>='a' && hi<='z') hi-='a';
- if (lo>='0' && lo<='9') lo-='0'; else if (lo>='A' && lo<='Z') lo-='A'; else if (lo>='a' && lo<='z') lo-='a';
- *((unsigned char*)d) = (unsigned char)(hi*16+lo);
- c+=4; d++;
- }
- else if (strncmp(c,"\\pntext",7)==0) {c+=7; while (c<max && *c!='}') c++;}
- else if (strncmp(c,"\\htmlrtf",8)==0)
- { c++; while (c<max && strncmp(c,"\\htmlrtf0",9)!=0) c++;
- if (c<max) c+=9; if (*c==' ') c++;
- }
- else if (*c=='\r' || *c=='\n') c++;
- else if (strncmp(c,"\\{",2)==0) {*d='{'; d++; c+=2;}
- else if (strncmp(c,"\\}",2)==0) {*d='}'; d++; c+=2;}
- else {*d=*c; c++; d++;}
- }
- *d=0; d++;
- *len = d-buf;
-}
-
-
-void main()
-{
- unsigned char buf[1024*1024];
- int len = fread(buf, 1, 1024*1024, stdin);
- decodertfhtml(buf, &len);
- fwrite(buf, 1, len, stdout);
-}
diff --git a/vendor/ruby-msg/contrib/rtfdecompr.c b/vendor/ruby-msg/contrib/rtfdecompr.c
deleted file mode 100644
index 633d50286..000000000
--- a/vendor/ruby-msg/contrib/rtfdecompr.c
+++ /dev/null
@@ -1,105 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-void decompress_rtf(FILE *srcf)
-{
-// #define prebuf_len (sizeof(prebuf))
-// static unsigned char prebuf[] =
-
- // the window of decompressed bytes that can be referenced for copies.
- // moved to this rather than indexing directly into output for streaming.
- // circular buffer.
- // because we use single-function call approach, no need for copy.
- // if using libstream-3, i would have a few options. i would be part of
- // the filter interface, which doesn't care if it is reading or writing,
- // all it knows about is its input and output buffers. we can't just
- // flush some data to the output buffer in that scenario, so we would need
- // to keep the window around. we also can't guarantee availability of that
- // buffer. so, we would probably have a instance member which would be
- // this ->
- unsigned char buf[4096] =
- "{\\rtf1\\ansi\\mac\\deff0\\deftab720{\\fonttbl;}"
- "{\\f0\\fnil \\froman \\fswiss \\fmodern \\fscript "
- "\\fdecor MS Sans SerifSymbolArialTimes New RomanCourier"
- "{\\colortbl\\red0\\green0\\blue0\n\r\\par "
- "\\pard\\plain\\f0\\fs20\\b\\i\\u\\tab\\tx";
-
- #define BUF_MASK 4095
-
- int wp = strlen((char *)buf);
-
- unsigned char *dst; // destination for uncompressed bytes
- int in = 0; // current position in src array
- int out = 0; // current position in dst array
-
- unsigned char hdr[16];
- int got;
- // get header fields (as defined in RTFLIB.H)
- got = fread(hdr, 1, 16, srcf);
- if (got != 16) {
- printf("Invalid compressed-RTF header\n");
- exit(1);
- }
-
- int compr_size = *(unsigned int *)(hdr);
- int uncompr_size = *(unsigned int *)(hdr + 4);
- int magic = *(unsigned int *)(hdr + 8);
- long crc32 = *(unsigned int *)(hdr + 12);
-
- unsigned char *x, *y;;
- unsigned char *src = malloc(compr_size - 12); // includes the 3 header fields
- y = src;
- x = src + compr_size - 12;
- got = fread(src, 1, compr_size - 12, srcf);
- if (got != compr_size - 12) {
- printf("compressed-RTF data size mismatch (%d != %d)\n", got, compr_size - 12);
- exit(1);
- }
- // shouldn't be any more than that
- got = fread(dst, 1, 16, srcf);
- if (got > 0) {
- printf("warning: data after the size\n");
- }
-
- // process the data
- if (magic == 0x414c454d) { // magic number that identifies the stream as a uncompressed stream
- dst = malloc(uncompr_size);
- memcpy(dst, src, uncompr_size);
- }
- else if (magic == 0x75465a4c) { // magic number that identifies the stream as a compressed stream
- out = 0; //strlen(prebuf);
- int dst_len;
- dst = malloc(dst_len = uncompr_size);
-
- int flagCount = 0;
- int flags = 0;
- while (out < dst_len && src < x) {
- // each flag byte flags 8 literals/references, 1 per bit
- flags = (flagCount++ % 8 == 0) ? *src++ : flags >> 1;
- if (flags & 1) { // each flag bit is 1 for reference, 0 for literal
- int rp = *src++;
- int l = *src++;
- //offset is a 12 byte number. 2^12 is 4096, so thats fine
- rp = (rp << 4) | (l >> 4); // the offset relative to block start
- l = (l & 0xf) + 2; // the number of bytes to copy
- int e = rp + l;
- while (rp < e)
- putchar(buf[wp++ & BUF_MASK] = buf[rp++ & BUF_MASK]);
- }
- else putchar(buf[wp++ & BUF_MASK] = *src++);
- }
- }
- else { // unknown magic number
- printf("Unknown compression type (magic number %04x)", magic);
- }
-
- free(y);
-}
-
-int main(int argc, char *argv[])
-{
- FILE *file = fopen(argv[1], "rb");
- decompress_rtf(file);
- fclose(file);
-}
diff --git a/vendor/ruby-msg/contrib/wmf.rb b/vendor/ruby-msg/contrib/wmf.rb
deleted file mode 100644
index 531e5fc99..000000000
--- a/vendor/ruby-msg/contrib/wmf.rb
+++ /dev/null
@@ -1,107 +0,0 @@
-
-# this file will be used later to enhance the msg conversion.
-
-# doesn't really work very well....
-
-def wmf_getdimensions wmf_data
- # check if we have a placeable metafile
- if wmf_data.unpack('L')[0] == 0x9ac6cdd7
- # do check sum test
- shorts = wmf_data.unpack 'S11'
- warn 'bad wmf header checksum' unless shorts.pop == shorts.inject(0) { |a, b| a ^ b }
- # determine dimensions
- left, top, right, bottom, twips_per_inch = wmf_data[6, 10].unpack 'S5'
- p [left, top, right, bottom, twips_per_inch]
- [right - left, bottom - top].map { |i| (i * 96.0 / twips_per_inch).round }
- else
- [nil, nil]
- end
-end
-
-=begin
-
-some attachment stuff
-rendering_position
-object_type
-attach_num
-attach_method
-
-rendering_position is around (1 << 32) - 1 if its inline
-
-attach_method 1 for plain data?
-attach_method 6 for embedded ole
-
-display_name instead of reading the embedded ole type.
-
-
-PR_RTF_IN_SYNC property is missing or set to FALSE.
-
-
-Before reading from the uncompressed RTF stream, sort the message's attachment
-table on the value of the PR_RENDERING_POSITION property. The attachments will
-now be in order by how they appear in the message.
-
-As your client scans through the RTF stream, check for the token "\objattph".
-The character following the token is the place to put the next attachment from
-the sorted table. Handle attachments that have set their PR_RENDERING_POSITION
-property to -1 separately.
-
-eg from rtf.
-
-\b\f2\fs20{\object\objemb{\*\objclass PBrush}\objw1320\objh1274{\*\objdata
-01050000 <- looks like standard header
-02000000 <- not sure
-07000000 <- this means length of following is 7.
-50427275736800 <- Pbrush\000 in hex
-00000000 <- ?
-00000000 <- ?
-e0570000 <- this is 22496. length of the following in hex
-this is the bitmap data, starting with BM....
-424dde57000000000000360000002800000058000000550000000100180000000000a857000000
-000000000000000000000000000000c8d0d4c8d0d4c8d0d4c8d0d4c8d0d4c8d0d4c8d0d4c8d0d4
-
----------------
-
-tested 3 different embedded files:
-
-1. excel embedded
- - "\002OlePres000"[40..-1] can be saved to '.wmf' and opened.
- - "\002OlePres001" similarly.
- much better looking image. strange
- - For the rtf serialization, it has the file contents as an
- ole, "d0cf11e" serialization, which i can't do yet. this can
- be extracted as a working .xls
- followed by a METAFILEPICT chunk, correspoding to one of the
- ole pres chunks.
- then the very same metafile chunk in the result bit.
-
-2. pbrush embedded image
- - "\002OlePres000" wmf as above.
- - "\001Ole10Native" is a long followed by a plain old .bmp
- - Serialization:
- Basic header as before, then bitmap data follows, then the
- metafile chunk follows, though labeled PBrush again this time.
- the result chunk was corrupted
-
-3. metafile embedded image
- - no presentation section, just a
- - "CONTENTS" section, which can be saved directly as a wmf.
- different header to the other 2 metafiles. it starts with
- 9AC6CDD7, which is the Aldus placeable metafile header.
- (http://wvware.sourceforge.net/caolan/ora-wmf.html)
- you can decode the left, top, right, bottom, and then
- multiply by 96, and divide by the metafile unit converter thing
- to get pixel values.
-
-the above ones were always the plain metafiles
-word filetype (0 = memory, 1 = disk)
-word headersize (always 9)
-word version
-thus leading to the
-0100
-0900
-0003
-pattern i usually see.
-
-=end
-
diff --git a/vendor/ruby-msg/data/mapitags.yaml b/vendor/ruby-msg/data/mapitags.yaml
deleted file mode 100644
index d6c5d5756..000000000
--- a/vendor/ruby-msg/data/mapitags.yaml
+++ /dev/null
@@ -1,4168 +0,0 @@
----
-66b0:
-- PR_RECIPIENT_ON_ASSOC_MSG_COUNT
-- PT_LONG
-3a01:
-- PR_ALTERNATE_RECIPIENT
-- PT_BINARY
-"6628":
-- PR_GW_MTSIN_ENTRYID
-- PT_BINARY
-"0061":
-- PR_END_DATE
-- PT_SYSTIME
-66b1:
-- PR_ATTACH_ON_NORMAL_MSG_COUNT
-- PT_LONG
-"1006":
-- PR_RTF_SYNC_BODY_CRC
-- PT_LONG
-3a02:
-- PR_CALLBACK_TELEPHONE_NUMBER
-- PT_TSTRING
-67aa:
-- PR_ASSOCIATED
-- PT_BOOLEAN
-"6629":
-- PR_GW_MTSOUT_ENTRYID
-- PT_BINARY
-"0062":
-- PR_OWNER_APPT_ID
-- PT_LONG
-fffa:
-- PR_EMS_AB_OBJECT_OID
-- PT_BINARY
-66b2:
-- PR_ATTACH_ON_ASSOC_MSG_COUNT
-- PT_LONG
-36e0:
-- PR_FOLDER_XVIEWINFO_E
-- PT_BINARY
-"1007":
-- PR_RTF_SYNC_BODY_COUNT
-- PT_LONG
-3a03:
-- PR_CONVERSION_PROHIBITED
-- PT_BOOLEAN
-"0063":
-- PR_RESPONSE_REQUESTED
-- PT_BOOLEAN
-fffb:
-- PR_EMS_AB_IS_MASTER
-- PT_BOOLEAN
-002a:
-- PR_RECEIPT_TIME
-- PT_SYSTIME
-66b3:
-- PR_NORMAL_MESSAGE_SIZE
-- PT_LONG|PT_I8
-36e1:
-- PR_FOLDER_VIEWS_ONLY
-- PT_LONG
-"1008":
-- PR_RTF_SYNC_BODY_TAG
-- PT_TSTRING
-3a04:
-- PR_DISCLOSE_RECIPIENTS
-- PT_BOOLEAN
-"0064":
-- PR_SENT_REPRESENTING_ADDRTYPE
-- PT_TSTRING
-fffc:
-- PR_EMS_AB_PARENT_ENTRYID
-- PT_BINARY
-002b:
-- PR_RECIPIENT_REASSIGNMENT_PROHIBITED
-- PT_BOOLEAN
-66b4:
-- PR_ASSOC_MESSAGE_SIZE
-- PT_LONG|PT_I8
-"1009":
-- PR_RTF_COMPRESSED
-- PT_BINARY
-3a05:
-- PR_GENERATION
-- PT_TSTRING
-"0065":
-- PR_SENT_REPRESENTING_EMAIL_ADDRESS
-- PT_TSTRING
-002c:
-- PR_REDIRECTION_HISTORY
-- PT_BINARY
-66b5:
-- PR_FOLDER_PATHNAME
-- PT_TSTRING
-3a06:
-- PR_GIVEN_NAME
-- PT_TSTRING
-fffe:
-- PR_EMS_AB_SERVER
-- PT_TSTRING
-002d:
-- PR_RELATED_IPMS
-- PT_BINARY
-66b6:
-- PR_OWNER_COUNT
-- PT_LONG
-36e4:
-- PR_FREEBUSY_ENTRYIDS
-- PT_MV_BINARY
-3a07:
-- PR_GOVERNMENT_ID_NUMBER
-- PT_TSTRING
-"0066":
-- PR_ORIGINAL_SENDER_ADDRTYPE
-- PT_TSTRING
-002e:
-- PR_ORIGINAL_SENSITIVITY
-- PT_LONG
-"1010":
-- PR_RTF_SYNC_PREFIX_COUNT
-- PT_LONG
-66b7:
-- PR_CONTACT_COUNT
-- PT_LONG
-36e5:
-- PR_DEF_MSG_CLASS
-- PT_UNICODE
-3a08:
-- PR_BUSINESS_TELEPHONE_NUMBER
-- PT_TSTRING
-"0067":
-- PR_ORIGINAL_SENDER_EMAIL_ADDRESS
-- PT_TSTRING
-002f:
-- PR_LANGUAGES
-- PT_TSTRING
-"1011":
-- PR_RTF_SYNC_TRAILING_COUNT
-- PT_LONG
-"6634":
-- PR_CHANGE_ADVISOR
-- PT_OBJECT
-36e6:
-- PR_DEF_FORM_NAME
-- PT_UNICODE
-3a09:
-- PR_HOME_TELEPHONE_NUMBER
-- PT_TSTRING
-0068:
-- PR_ORIGINAL_SENT_REPRESENTING_ADDRTYPE
-- PT_TSTRING
-"6635":
-- PR_FAVORITES_DEFAULT_NAME
-- PT_TSTRING
-0069:
-- PR_ORIGINAL_SENT_REPRESENTING_EMAIL_ADDRESS
-- PT_TSTRING
-"1012":
-- PR_ORIGINALLY_INTENDED_RECIP_ENTRYID
-- PT_BINARY
-0e96:
-- PR_ATTACH_VIRUS_SCAN_INFO
-- PT_BINARY
-"6636":
-- PR_SYS_CONFIG_FOLDER_ENTRYID
-- PT_BINARY
-67f0:
-- PR_PROFILE_SECURE_MAILBOX
-- PT_BINARY
-"1013":
-- PR_BODY_HTML
-- PT_TSTRING
-"3400":
-- PR_DEFAULT_STORE
-- PT_BOOLEAN
-"6637":
-- PR_CHANGE_NOTIFICATION_GUID
-- PT_CLSID
-36e9:
-- PR_GENERATE_EXCHANGE_VIEWS
-- PT_BOOLEAN
-"0070":
-- PR_CONVERSATION_TOPIC
-- PT_TSTRING
-0e5e:
-- PR_MIME_HANDLER_CLASSIDS
-- PT_CLSID
-3a10:
-- PR_ORGANIZATIONAL_ID_NUMBER
-- PT_TSTRING
-"6638":
-- PR_FOLDER_CHILD_COUNT
-- PT_LONG
-"0071":
-- PR_CONVERSATION_INDEX
-- PT_BINARY
-3a11:
-- PR_SURNAME
-- PT_TSTRING
-"0072":
-- PR_ORIGINAL_DISPLAY_BCC
-- PT_TSTRING
-"7001":
-- PR_VD_BINARY
-- PT_BINARY
-3a12:
-- PR_ORIGINAL_ENTRYID
-- PT_BINARY
-"0073":
-- PR_ORIGINAL_DISPLAY_CC
-- PT_TSTRING
-003a:
-- PR_REPORT_NAME
-- PT_TSTRING
-"7002":
-- PR_VD_STRINGS
-- PT_UNICODE
-3a13:
-- PR_ORIGINAL_DISPLAY_NAME
-- PT_TSTRING
-"0074":
-- PR_ORIGINAL_DISPLAY_TO
-- PT_TSTRING
-686b:
-- PR_DELEGATES_SEE_PRIVATE
-- PT_MV_LONG
-003b:
-- PR_SENT_REPRESENTING_SEARCH_KEY
-- PT_BINARY
-66c3:
-- PR_CODE_PAGE_ID
-- PT_LONG
-"7003":
-- PR_VD_FLAGS
-- PT_LONG
-3a14:
-- PR_ORIGINAL_SEARCH_KEY
-- PT_BINARY
-"0075":
-- PR_RECEIVED_BY_ADDRTYPE
-- PT_TSTRING
-686c:
-- PR_PERSONAL_FREEBUSY
-- PT_BINARY
-003c:
-- PR_X400_CONTENT_TYPE
-- PT_BINARY
-66c4:
-- PR_RETENTION_AGE_LIMIT
-- PT_LONG
-"7004":
-- PR_VD_LINK_TO
-- PT_BINARY
-3a15:
-- PR_POSTAL_ADDRESS
-- PT_TSTRING
-"0076":
-- PR_RECEIVED_BY_EMAIL_ADDRESS
-- PT_TSTRING
-686d:
-- PR_PROCESS_MEETING_REQUESTS
-- PT_BOOLEAN
-003d:
-- PR_SUBJECT_PREFIX
-- PT_TSTRING
-66c5:
-- PR_DISABLE_PERUSER_READ
-- PT_BOOLEAN
-"7005":
-- PR_VD_VIEW_FOLDER
-- PT_BINARY
-3a16:
-- PR_COMPANY_NAME
-- PT_TSTRING
-686e:
-- PR_DECLINE_RECURRING_MEETING_REQUESTS
-- PT_BOOLEAN
-003e:
-- PR_NON_RECEIPT_REASON
-- PT_LONG
-66c6:
-- PR_INTERNET_PARSE_STATE
-- PT_BINARY
-"7006":
-- PR_VD_NAME
-- PT_UNICODE
-3a17:
-- PR_TITLE
-- PT_TSTRING
-660a:
-- PR_PROFILE_TYPE
-- PT_LONG
-"0077":
-- PR_RCVD_REPRESENTING_ADDRTYPE
-- PT_TSTRING
-686f:
-- PR_DECLINE_CONFLICTING_MEETING_REQUESTS
-- PT_BOOLEAN
-003f:
-- PR_RECEIVED_BY_ENTRYID
-- PT_BINARY
-66c7:
-- PR_INTERNET_MESSAGE_INFO
-- PT_BINARY
-3a18:
-- PR_DEPARTMENT_NAME
-- PT_TSTRING
-660b:
-- PR_PROFILE_MAILBOX
-- PT_TSTRING
-0078:
-- PR_RCVD_REPRESENTING_EMAIL_ADDRESS
-- PT_TSTRING
-7d01:
-- PR_FAV_AUTOSUBFOLDERS
-- PT_LONG
-"7007":
-- PR_VD_VERSION
-- PT_LONG
-3a19:
-- PR_OFFICE_LOCATION
-- PT_TSTRING
-660c:
-- PR_PROFILE_SERVER
-- PT_TSTRING
-0079:
-- PR_ORIGINAL_AUTHOR_ADDRTYPE
-- PT_TSTRING
-7d02:
-- PR_FAV_PARENT_SOURCE_KEY
-- PT_BINARY
-660d:
-- PR_PROFILE_MAX_RESTRICT
-- PT_LONG
-7d03:
-- PR_FAV_LEVEL_MASK
-- PT_LONG
-"3410":
-- PR_IPM_SUBTREE_SEARCH_KEY
-- PT_BINARY
-660e:
-- PR_PROFILE_AB_FILES_PATH
-- PT_TSTRING
-3a20:
-- PR_TRANSMITTABLE_DISPLAY_NAME
-- PT_TSTRING
-"3411":
-- PR_IPM_OUTBOX_SEARCH_KEY
-- PT_BINARY
-"6779":
-- PR_PF_QUOTA_STYLE
-- PT_LONG
-0c0a:
-- PR_PHYSICAL_DELIVERY_BUREAU_FAX_DELIVERY
-- PT_BOOLEAN
-660f:
-- PR_PROFILE_FAVFLD_DISPLAY_NAME
-- PT_TSTRING
-3a21:
-- PR_PAGER_TELEPHONE_NUMBER
-- PT_TSTRING
-"3412":
-- PR_IPM_WASTEBASKET_SEARCH_KEY
-- PT_BINARY
-0c0b:
-- PR_PHYSICAL_DELIVERY_MODE
-- PT_LONG
-3a22:
-- PR_USER_CERTIFICATE
-- PT_BINARY
-"3413":
-- PR_IPM_SENTMAIL_SEARCH_KEY
-- PT_BINARY
-0c0c:
-- PR_PHYSICAL_DELIVERY_REPORT_REQUEST
-- PT_LONG
-7d07:
-- PR_FAV_INHERIT_AUTO
-- PT_LONG
-65a0:
-- PR_RULE_SERVER_RULE_ID
-- PT_I8
-3a23:
-- PR_PRIMARY_FAX_NUMBER
-- PT_TSTRING
-"3414":
-- PR_MDB_PROVIDER
-- PT_BINARY
-0c0d:
-- PR_PHYSICAL_FORWARDING_ADDRESS
-- PT_BINARY
-004a:
-- PR_DISC_VAL
-- PT_BOOLEAN
-7d08:
-- PR_FAV_DEL_SUBS
-- PT_BINARY
-"6650":
-- PR_RULE_ACTION_NUMBER
-- PT_LONG
-3a24:
-- PR_BUSINESS_FAX_NUMBER
-- PT_TSTRING
-"3415":
-- PR_RECEIVE_FOLDER_SETTINGS
-- PT_OBJECT
-0c0e:
-- PR_PHYSICAL_FORWARDING_ADDRESS_REQUESTED
-- PT_BOOLEAN
-004b:
-- PR_ORIG_MESSAGE_CLASS
-- PT_TSTRING
-"6651":
-- PR_RULE_FOLDER_ENTRYID
-- PT_BINARY
-"6783":
-- PR_SEARCH_FLAGS
-- PT_LONG
-3a25:
-- PR_HOME_FAX_NUMBER
-- PT_TSTRING
-674a:
-- PR_MID
-- PT_I8
-0c0f:
-- PR_PHYSICAL_FORWARDING_PROHIBITED
-- PT_BOOLEAN
-004c:
-- PR_ORIGINAL_AUTHOR_ENTRYID
-- PT_BINARY
-"6652":
-- PR_ACTIVE_USER_ENTRYID
-- PT_BINARY
-3a26:
-- PR_COUNTRY
-- PT_TSTRING
-674b:
-- PR_CATEG_ID
-- PT_I8
-004d:
-- PR_ORIGINAL_AUTHOR_NAME
-- PT_TSTRING
-"1030":
-- PR_INTERNET_APPROVED
-- PT_TSTRING
-"6653":
-- PR_X400_ENVELOPE_TYPE
-- PT_LONG
-3a27:
-- PR_LOCALITY
-- PT_TSTRING
-661a:
-- PR_USER_NAME
-- PT_TSTRING
-674c:
-- PR_PARENT_CATEG_ID
-- PT_I8
-004e:
-- PR_ORIGINAL_SUBMIT_TIME
-- PT_SYSTIME
-"1031":
-- PR_INTERNET_CONTROL
-- PT_TSTRING
-"6654":
-- PR_MSG_FOLD_TIME
-- PT_SYSTIME
-3a28:
-- PR_STATE_OR_PROVINCE
-- PT_TSTRING
-661b:
-- PR_MAILBOX_OWNER_ENTRYID
-- PT_BINARY
-674d:
-- PR_INST_ID
-- PT_I8
-004f:
-- PR_REPLY_RECIPIENT_ENTRIES
-- PT_BINARY
-"1032":
-- PR_INTERNET_DISTRIBUTION
-- PT_TSTRING
-3a29:
-- PR_STREET_ADDRESS
-- PT_TSTRING
-661c:
-- PR_MAILBOX_OWNER_NAME
-- PT_TSTRING
-674e:
-- PR_INSTANCE_NUM
-- PT_LONG
-"1033":
-- PR_INTERNET_FOLLOWUP_TO
-- PT_TSTRING
-"6655":
-- PR_ICS_CHANGE_KEY
-- PT_BINARY
-661d:
-- PR_OOF_STATE
-- PT_BOOLEAN
-674f:
-- PR_ADDRBOOK_MID
-- PT_I8
-661e:
-- PR_SCHEDULE_FOLDER_ENTRYID
-- PT_BINARY
-"1034":
-- PR_INTERNET_LINES
-- PT_LONG
-3a30:
-- PR_ASSISTANT
-- PT_TSTRING
-0c1a:
-- PR_SENDER_NAME
-- PT_TSTRING
-661f:
-- PR_IPM_DAF_ENTRYID
-- PT_BINARY
-"1035":
-- PR_INTERNET_MESSAGE_ID
-- PT_TSTRING
-"6658":
-- PR_GW_ADMIN_OPERATIONS
-- PT_LONG
-0c1b:
-- PR_SUPPLEMENTARY_INFO
-- PT_TSTRING
-"1036":
-- PR_INTERNET_NEWSGROUPS
-- PT_TSTRING
-"6659":
-- PR_INTERNET_CONTENT
-- PT_BINARY
-0c1c:
-- PR_TYPE_OF_MTS_USER
-- PT_LONG
-0c1d:
-- PR_SENDER_SEARCH_KEY
-- PT_BINARY
-005a:
-- PR_ORIGINAL_SENDER_NAME
-- PT_TSTRING
-"1037":
-- PR_INTERNET_ORGANIZATION
-- PT_TSTRING
-66aa:
-- PR_RESTRICTION_COUNT
-- PT_LONG
-0c1e:
-- PR_SENDER_ADDRTYPE
-- PT_TSTRING
-005b:
-- PR_ORIGINAL_SENDER_ENTRYID
-- PT_BINARY
-10c0:
-- PR_SMTP_TEMP_TBL_DATA
-- PT_BINARY
-"6660":
-- PR_TRACE_INFO
-- PT_BINARY
-"1038":
-- PR_INTERNET_NNTP_PATH
-- PT_TSTRING
-66ab:
-- PR_CATEG_COUNT
-- PT_LONG
-675a:
-- PR_PCL_EXPORT
-- PT_BINARY
-0c1f:
-- PR_SENDER_EMAIL_ADDRESS
-- PT_TSTRING
-005c:
-- PR_ORIGINAL_SENDER_SEARCH_KEY
-- PT_BINARY
-10c1:
-- PR_SMTP_TEMP_TBL_DATA_2
-- PT_LONG
-35e0:
-- PR_IPM_SUBTREE_ENTRYID
-- PT_BINARY
-"6661":
-- PR_SUBJECT_TRACE_INFO
-- PT_BINARY
-"1039":
-- PR_INTERNET_REFERENCES
-- PT_TSTRING
-66ac:
-- PR_CACHED_COLUMN_COUNT
-- PT_LONG
-675b:
-- PR_CN_MV_EXPORT
-- PT_MV_BINARY
-"8100":
-- PR_EMS_AB_OPEN_RETRY_INTERVAL
-- PT_LONG
-005d:
-- PR_ORIGINAL_SENT_REPRESENTING_NAME
-- PT_TSTRING
-10c2:
-- PR_SMTP_TEMP_TBL_DATA_3
-- PT_BINARY
-"6662":
-- PR_RECIPIENT_NUMBER
-- PT_LONG
-66ad:
-- PR_NORMAL_MSG_W_ATTACH_COUNT
-- PT_LONG
-662a:
-- PR_TRANSFER_ENABLED
-- PT_BOOLEAN
-"8101":
-- PR_EMS_AB_ORGANIZATION_NAME
-- PT_MV_TSTRING
-005e:
-- PR_ORIGINAL_SENT_REPRESENTING_ENTRYID
-- PT_BINARY
-10c3:
-- PR_CAL_START_TIME
-- PT_SYSTIME
-"1040":
-- PR_NNTP_XREF
-- PT_TSTRING
-35e2:
-- PR_IPM_OUTBOX_ENTRYID
-- PT_BINARY
-"6663":
-- PR_MTS_SUBJECT_ID
-- PT_BINARY
-66ae:
-- PR_ASSOC_MSG_W_ATTACH_COUNT
-- PT_LONG
-662b:
-- PR_TEST_LINE_SPEED
-- PT_BINARY
-"8102":
-- PR_EMS_AB_ORGANIZATIONAL_UNIT_NAME
-- PT_MV_TSTRING
-005f:
-- PR_ORIGINAL_SENT_REPRESENTING_SEARCH_KEY
-- PT_BINARY
-10c4:
-- PR_CAL_END_TIME
-- PT_SYSTIME
-"1041":
-- PR_INTERNET_PRECEDENCE
-- PT_TSTRING
-35e3:
-- PR_IPM_WASTEBASKET_ENTRYID
-- PT_BINARY
-"6664":
-- PR_REPORT_DESTINATION_NAME
-- PT_TSTRING
-66af:
-- PR_RECIPIENT_ON_NORMAL_MSG_COUNT
-- PT_LONG
-662c:
-- PR_HIERARCHY_SYNCHRONIZER
-- PT_OBJECT
-"8103":
-- PR_EMS_AB_ORIGINAL_DISPLAY_TABLE
-- PT_BINARY
-10c5:
-- PR_CAL_RECURRING_ID
-- PT_SYSTIME
-"1042":
-- PR_IN_REPLY_TO_ID
-- PT_UNICODE
-35e4:
-- PR_IPM_SENTMAIL_ENTRYID
-- PT_BINARY
-"6665":
-- PR_REPORT_DESTINATION_ENTRYID
-- PT_BINARY
-662d:
-- PR_CONTENTS_SYNCHRONIZER
-- PT_OBJECT
-"8104":
-- PR_EMS_AB_ORIGINAL_DISPLAY_TABLE_MSDOS
-- PT_BINARY
-10c6:
-- PR_DAV_SUBMIT_DATA
-- PT_UNICODE
-"1043":
-- PR_LIST_HELP
-- PT_UNICODE
-35e5:
-- PR_VIEWS_ENTRYID
-- PT_BINARY
-662e:
-- PR_COLLECTOR
-- PT_OBJECT
-36df:
-- PR_FOLDER_WEBVIEWINFO
-- PT_BINARY
-"8105":
-- PR_EMS_AB_OUTBOUND_SITES
-- PT_OBJECT|PT_MV_TSTRING
-10c7:
-- PR_CDO_EXPANSION_INDEX
-- PT_LONG
-"1044":
-- PR_LIST_SUBSCRIBE
-- PT_UNICODE
-35e6:
-- PR_COMMON_VIEWS_ENTRYID
-- PT_BINARY
-"6666":
-- PR_CONTENT_SEARCH_KEY
-- PT_BINARY
-662f:
-- PR_FAST_TRANSFER
-- PT_OBJECT
-"8106":
-- PR_EMS_AB_P_SELECTOR
-- PT_BINARY
-10c8:
-- PR_IFS_INTERNAL_DATA
-- PT_BINARY
-3a40:
-- PR_SEND_RICH_INFO
-- PT_BOOLEAN
-35e7:
-- PR_FINDER_ENTRYID
-- PT_BINARY
-"6667":
-- PR_FOREIGN_ID
-- PT_BINARY
-"1045":
-- PR_LIST_UNSUBSCRIBE
-- PT_UNICODE
-3a41:
-- PR_WEDDING_ANNIVERSARY
-- PT_SYSTIME
-"6668":
-- PR_FOREIGN_REPORT_ID
-- PT_BINARY
-"8107":
-- PR_EMS_AB_P_SELECTOR_INBOUND
-- PT_BINARY
-"3301":
-- PR_FORM_VERSION
-- PT_TSTRING
-3a42:
-- PR_BIRTHDAY
-- PT_SYSTIME
-"6669":
-- PR_FOREIGN_SUBJECT_ID
-- PT_BINARY
-3a0a:
-- PR_INITIALS
-- PT_TSTRING
-"8108":
-- PR_EMS_AB_PER_MSG_DIALOG_DISPLAY_TABLE
-- PT_BINARY
-"3302":
-- PR_FORM_CLSID
-- PT_CLSID
-3a43:
-- PR_HOBBIES
-- PT_TSTRING
-"8109":
-- PR_EMS_AB_PER_RECIP_DIALOG_DISPLAY_TABLE
-- PT_BINARY
-"6670":
-- PR_LONGTERM_ENTRYID_FROM_TABLE
-- PT_BINARY
-"3303":
-- PR_FORM_CONTACT_NAME
-- PT_TSTRING
-3a44:
-- PR_MIDDLE_NAME
-- PT_TSTRING
-3a0b:
-- PR_KEYWORD
-- PT_TSTRING
-65c2:
-- PR_REPLY_TEMPLATE_ID
-- PT_BINARY
-"6671":
-- PR_MEMBER_ID
-- PT_I8
-"3304":
-- PR_FORM_CATEGORY
-- PT_TSTRING
-3a45:
-- PR_DISPLAY_NAME_PREFIX
-- PT_TSTRING
-3a0c:
-- PR_LANGUAGE
-- PT_TSTRING
-"6672":
-- PR_MEMBER_NAME
-- PT_TSTRING
-"3305":
-- PR_FORM_CATEGORY_SUB
-- PT_TSTRING
-3a46:
-- PR_PROFESSION
-- PT_TSTRING
-"8110":
-- PR_EMS_AB_PUBLIC_DELEGATES_BL
-- PT_OBJECT|PT_MV_TSTRING
-3a0d:
-- PR_LOCATION
-- PT_TSTRING
-"6673":
-- PR_MEMBER_RIGHTS
-- PT_LONG
-"3306":
-- PR_FORM_HOST_MAP
-- PT_MV_LONG
-3a47:
-- PR_PREFERRED_BY_NAME
-- PT_TSTRING
-663a:
-- PR_HAS_RULES
-- PT_BOOLEAN
-"8111":
-- PR_EMS_AB_QUOTA_NOTIFICATION_SCHEDULE
-- PT_BINARY
-3a0e:
-- PR_MAIL_PERMISSION
-- PT_BOOLEAN
-"6674":
-- PR_RULE_ID
-- PT_I8
-"3307":
-- PR_FORM_HIDDEN
-- PT_BOOLEAN
-3a48:
-- PR_SPOUSE_NAME
-- PT_TSTRING
-663b:
-- PR_ADDRESS_BOOK_ENTRYID
-- PT_BINARY
-36ec:
-- PR_AGING_PERIOD
-- PT_LONG
-"8112":
-- PR_EMS_AB_QUOTA_NOTIFICATION_STYLE
-- PT_LONG
-3a0f:
-- PR_MHS_COMMON_NAME
-- PT_TSTRING
-"6675":
-- PR_RULE_IDS
-- PT_BINARY
-"3308":
-- PR_FORM_DESIGNER_NAME
-- PT_TSTRING
-3a49:
-- PR_COMPUTER_NETWORK_NAME
-- PT_TSTRING
-663c:
-- PR_PUBLIC_FOLDER_ENTRYID
-- PT_BINARY
-"8113":
-- PR_EMS_AB_RANGE_LOWER
-- PT_LONG
-"6676":
-- PR_RULE_SEQUENCE
-- PT_LONG
-"3309":
-- PR_FORM_DESIGNER_GUID
-- PT_CLSID
-663d:
-- PR_OFFLINE_FLAGS
-- PT_LONG
-36ee:
-- PR_AGING_GRANULARITY
-- PT_LONG
-7c00:
-- PR_FAV_DISPLAY_NAME
-- PT_TSTRING
-"8114":
-- PR_EMS_AB_RANGE_UPPER
-- PT_LONG
-663e:
-- PR_HIERARCHY_CHANGE_NUM
-- PT_LONG
-"8115":
-- PR_EMS_AB_RAS_CALLBACK_NUMBER
-- PT_TSTRING
-3a50:
-- PR_PERSONAL_HOME_PAGE
-- PT_TSTRING
-"6677":
-- PR_RULE_STATE
-- PT_LONG
-663f:
-- PR_HAS_MODERATOR_RULES
-- PT_BOOLEAN
-7c02:
-- PR_FAV_PUBLIC_SOURCE_KEY
-- PT_BINARY
-"8116":
-- PR_EMS_AB_RAS_PHONE_NUMBER
-- PT_TSTRING
-3a51:
-- PR_BUSINESS_HOME_PAGE
-- PT_TSTRING
-"6678":
-- PR_RULE_USER_FLAGS
-- PT_LONG
-"8117":
-- PR_EMS_AB_RAS_PHONEBOOK_ENTRY_NAME
-- PT_TSTRING
-3a52:
-- PR_CONTACT_VERSION
-- PT_CLSID
-"6679":
-- PR_RULE_CONDITION
-- PT_SRESTRICTION
-3a1a:
-- PR_PRIMARY_TELEPHONE_NUMBER
-- PT_TSTRING
-7c04:
-- PR_OST_OSTID
-- PT_BINARY
-"4000":
-- PR_NEW_ATTACH
-- PT_LONG
-3a53:
-- PR_CONTACT_ENTRYIDS
-- PT_MV_BINARY
-3a1b:
-- PR_BUSINESS2_TELEPHONE_NUMBER
-- PT_TSTRING
-007a:
-- PR_ORIGINAL_AUTHOR_EMAIL_ADDRESS
-- PT_TSTRING
-"8118":
-- PR_EMS_AB_RAS_REMOTE_SRVR_NAME
-- PT_TSTRING
-"4001":
-- PR_START_EMBED
-- PT_LONG
-3a54:
-- PR_CONTACT_ADDRTYPES
-- PT_MV_TSTRING
-81a1:
-- PR_EMS_AB_X500_RDN
-- PT_TSTRING
-340d:
-- PR_STORE_SUPPORT_MASK
-- PT_LONG
-007b:
-- PR_ORIGINALLY_INTENDED_RECIP_ADDRTYPE
-- PT_TSTRING
-"8119":
-- PR_EMS_AB_REGISTERED_ADDRESS
-- PT_MV_BINARY
-"4002":
-- PR_END_EMBED
-- PT_LONG
-"6681":
-- PR_RULE_PROVIDER
-- PT_TSTRING
-3a55:
-- PR_CONTACT_DEFAULT_ADDRESS_INDEX
-- PT_LONG
-81a2:
-- PR_EMS_AB_X500_NC
-- PT_TSTRING
-3a1c:
-- PR_MOBILE_TELEPHONE_NUMBER
-- PT_TSTRING
-340e:
-- PR_STORE_STATE
-- PT_LONG
-007c:
-- PR_ORIGINALLY_INTENDED_RECIP_EMAIL_ADDRESS
-- PT_TSTRING
-"4003":
-- PR_START_RECIP
-- PT_LONG
-"6682":
-- PR_RULE_NAME
-- PT_TSTRING
-3a56:
-- PR_CONTACT_EMAIL_ADDRESSES
-- PT_MV_TSTRING
-677b:
-- PR_PF_STORAGE_QUOTA
-- PT_LONG
-81a3:
-- PR_EMS_AB_REFERRAL_LIST
-- PT_MV_TSTRING
-"8120":
-- PR_EMS_AB_REPORT_TO_ORIGINATOR
-- PT_BOOLEAN
-3a1d:
-- PR_RADIO_TELEPHONE_NUMBER
-- PT_TSTRING
-007d:
-- PR_TRANSPORT_MESSAGE_HEADERS
-- PT_TSTRING
-"6683":
-- PR_RULE_LEVEL
-- PT_LONG
-3a57:
-- PR_COMPANY_MAIN_PHONE_NUMBER
-- PT_TSTRING
-664a:
-- PR_HAS_NAMED_PROPERTIES
-- PT_BOOLEAN
-81a4:
-- PR_EMS_AB_NNTP_DISTRIBUTIONS_FLAG
-- PT_BOOLEAN
-"8121":
-- PR_EMS_AB_REPORT_TO_OWNER
-- PT_BOOLEAN
-3a1e:
-- PR_CAR_TELEPHONE_NUMBER
-- PT_TSTRING
-007e:
-- PR_DELEGATION
-- PT_BINARY
-"4004":
-- PR_END_RECIP
-- PT_LONG
-"6684":
-- PR_RULE_PROVIDER_DATA
-- PT_BINARY
-3a58:
-- PR_CHILDRENS_NAMES
-- PT_MV_TSTRING
-664b:
-- PR_REPLICA_VERSION
-- PT_I8
-81a5:
-- PR_EMS_AB_ASSOC_PROTOCOL_CFG_NNTP
-- PT_OBJECT|PT_MV_TSTRING
-"8122":
-- PR_EMS_AB_REQ_SEQ
-- PT_LONG
-3a1f:
-- PR_OTHER_TELEPHONE_NUMBER
-- PT_TSTRING
-007f:
-- PR_TNEF_CORRELATION_KEY
-- PT_BINARY
-"4005":
-- PR_END_CC_RECIP
-- PT_LONG
-"6685":
-- PR_LAST_FULL_BACKUP
-- PT_SYSTIME
-3a59:
-- PR_HOME_ADDRESS_CITY
-- PT_TSTRING
-81a6:
-- PR_EMS_AB_NNTP_NEWSFEEDS
-- PT_OBJECT|PT_MV_TSTRING
-"8123":
-- PR_EMS_AB_RESPONSIBLE_LOCAL_DXA
-- PT_OBJECT|PT_MV_TSTRING
-"4006":
-- PR_END_BCC_RECIP
-- PT_LONG
-"8124":
-- PR_EMS_AB_RID_SERVER
-- PT_OBJECT|PT_MV_TSTRING
-"4007":
-- PR_END_P1_RECIP
-- PT_LONG
-"6687":
-- PR_PROFILE_ADDR_INFO
-- PT_BINARY
-81a8:
-- PR_EMS_AB_ENABLED_PROTOCOL_CFG
-- PT_BOOLEAN
-"8125":
-- PR_EMS_AB_ROLE_OCCUPANT
-- PT_OBJECT|PT_MV_TSTRING
-81a9:
-- PR_EMS_AB_HTTP_PUB_AB_ATTRIBUTES
-- PT_MV_TSTRING
-"8126":
-- PR_EMS_AB_ROUTING_LIST
-- PT_MV_TSTRING
-"4009":
-- PR_START_TOP_FLD
-- PT_LONG
-3a60:
-- PR_OTHER_ADDRESS_COUNTRY
-- PT_TSTRING
-"8127":
-- PR_EMS_AB_RTS_CHECKPOINT_SIZE
-- PT_LONG
-"5902":
-- PR_INET_MAIL_OVERRIDE_FORMAT
-- PT_LONG
-3a61:
-- PR_OTHER_ADDRESS_POSTAL_CODE
-- PT_TSTRING
-"6689":
-- PR_PROFILE_OPTIONS_DATA
-- PT_BINARY
-3a2a:
-- PR_POSTAL_CODE
-- PT_TSTRING
-"8128":
-- PR_EMS_AB_RTS_RECOVERY_TIMEOUT
-- PT_LONG
-"4010":
-- PR_START_FAI_MSG
-- PT_LONG
-3a62:
-- PR_OTHER_ADDRESS_STATE_OR_PROVINCE
-- PT_TSTRING
-81b0:
-- PR_EMS_AB_OUTBOUND_HOST_TYPE
-- PT_BOOLEAN
-3a2b:
-- PR_POST_BOX
-- PT_TSTRING
-"4011":
-- PR_NEW_FX_FOLDER
-- PT_BINARY
-7ffa:
-- PR_ATTACHMENT_LINKID
-- PT_LONG
-65e0:
-- PR_SOURCE_KEY
-- PT_BINARY
-3a63:
-- PR_OTHER_ADDRESS_STREET
-- PT_TSTRING
-81b1:
-- PR_EMS_AB_PROXY_GENERATION_ENABLED
-- PT_BOOLEAN
-3a2c:
-- PR_TELEX_NUMBER
-- PT_TSTRING
-"8129":
-- PR_EMS_AB_RTS_WINDOW_SIZE
-- PT_LONG
-"4012":
-- PR_INCR_SYNC_CHG
-- PT_LONG
-7ffb:
-- PR_EXCEPTION_STARTTIME
-- PT_SYSTIME
-65e1:
-- PR_PARENT_SOURCE_KEY
-- PT_BINARY
-"6690":
-- PR_REPLICATION_STYLE
-- PT_LONG
-3a64:
-- PR_OTHER_ADDRESS_POST_OFFICE_BOX
-- PT_TSTRING
-81b2:
-- PR_EMS_AB_ROOT_NEWSGROUPS_FOLDER_ID
-- PT_BINARY
-10f1:
-- PR_OWA_URL
-- PT_TSTRING
-"4013":
-- PR_INCR_SYNC_DEL
-- PT_LONG
-7ffc:
-- PR_EXCEPTION_ENDTIME
-- PT_SYSTIME
-65e2:
-- PR_CHANGE_KEY
-- PT_BINARY
-"6691":
-- PR_REPLICATION_SCHEDULE
-- PT_BINARY
-81b3:
-- PR_EMS_AB_CONNECTION_TYPE
-- PT_BOOLEAN
-3a2d:
-- PR_ISDN_NUMBER
-- PT_TSTRING
-"8130":
-- PR_EMS_AB_SERIAL_NUMBER
-- PT_MV_TSTRING
-10f2:
-- PR_DISABLE_FULL_FIDELITY
-- PT_BOOLEAN
-"4014":
-- PR_INCR_SYNC_END
-- PT_LONG
-7ffd:
-- PR_ATTACHMENT_FLAGS
-- PT_LONG
-65e3:
-- PR_PREDECESSOR_CHANGE_LIST
-- PT_BINARY
-"6692":
-- PR_REPLICATION_MESSAGE_PRIORITY
-- PT_LONG
-81b4:
-- PR_EMS_AB_CONNECTION_LIST_FILTER_TYPE
-- PT_LONG
-"8131":
-- PR_EMS_AB_SERVICE_ACTION_FIRST
-- PT_LONG
-3a2e:
-- PR_ASSISTANT_TELEPHONE_NUMBER
-- PT_TSTRING
-10f3:
-- PR_URL_COMP_NAME
-- PT_UNICODE
-7ffe:
-- PR_ATTACHMENT_HIDDEN
-- PT_BOOLEAN
-65e4:
-- PR_SYNCHRONIZE_FLAGS
-- PT_LONG
-"6693":
-- PR_OVERALL_MSG_AGE_LIMIT
-- PT_LONG
-665a:
-- PR_HAS_ATTACH_FROM_IMAIL
-- PT_BOOLEAN
-81b5:
-- PR_EMS_AB_PORT_NUMBER
-- PT_LONG
-"8132":
-- PR_EMS_AB_SERVICE_ACTION_OTHER
-- PT_LONG
-3a2f:
-- PR_HOME2_TELEPHONE_NUMBER
-- PT_TSTRING
-10f4:
-- PR_ATTR_HIDDEN
-- PT_BOOLEAN
-"4015":
-- PR_INCR_SYNC_MSG
-- PT_LONG
-65e5:
-- PR_AUTO_ADD_NEW_SUBS
-- PT_BOOLEAN
-"6694":
-- PR_REPLICATION_ALWAYS_INTERVAL
-- PT_LONG
-"5909":
-- PR_MSG_EDITOR_FORMAT
-- PT_LONG
-665b:
-- PR_ORIGINATOR_NAME
-- PT_TSTRING
-"8001":
-- PR_EMS_AB_DISPLAY_NAME_OVERRIDE
-- PT_BOOLEAN
-81b6:
-- PR_EMS_AB_PROTOCOL_SETTINGS
-- PT_MV_TSTRING
-"8133":
-- PR_EMS_AB_SERVICE_ACTION_SECOND
-- PT_LONG
-10f5:
-- PR_ATTR_SYSTEM
-- PT_BOOLEAN
-"4016":
-- PR_FX_DEL_PROP
-- PT_LONG
-65e6:
-- PR_NEW_SUBS_GET_AUTO_ADD
-- PT_BOOLEAN
-"6695":
-- PR_REPLICATION_MSG_SIZE
-- PT_LONG
-665c:
-- PR_ORIGINATOR_ADDR
-- PT_TSTRING
-81b7:
-- PR_EMS_AB_GROUP_BY_ATTR_1
-- PT_TSTRING
-"8134":
-- PR_EMS_AB_SERVICE_RESTART_DELAY
-- PT_LONG
-10f6:
-- PR_ATTR_READONLY
-- PT_BOOLEAN
-"4017":
-- PR_IDSET_GIVEN
-- PT_LONG
-65e7:
-- PR_MESSAGE_SITE_NAME
-- PT_TSTRING
-"6696":
-- PR_IS_NEWSGROUP_ANCHOR
-- PT_BOOLEAN
-103a:
-- PR_SUPERSEDES
-- PT_TSTRING
-665d:
-- PR_ORIGINATOR_ADDRTYPE
-- PT_TSTRING
-"8003":
-- PR_EMS_AB_CA_CERTIFICATE
-- PT_MV_BINARY
-81b8:
-- PR_EMS_AB_GROUP_BY_ATTR_2
-- PT_TSTRING
-"8135":
-- PR_EMS_AB_SERVICE_RESTART_MESSAGE
-- PT_TSTRING
-65e8:
-- PR_MESSAGE_PROCESSED
-- PT_BOOLEAN
-"6697":
-- PR_IS_NEWSGROUP
-- PT_BOOLEAN
-103b:
-- PR_POST_FOLDER_ENTRIES
-- PT_BINARY
-665e:
-- PR_ORIGINATOR_ENTRYID
-- PT_BINARY
-"8004":
-- PR_EMS_AB_FOLDER_PATHNAME
-- PT_TSTRING
-81b9:
-- PR_EMS_AB_GROUP_BY_ATTR_3
-- PT_TSTRING
-"8136":
-- PR_EMS_AB_SESSION_DISCONNECT_TIMER
-- PT_LONG
-"4019":
-- PR_SENDER_FLAGS
-- PT_LONG
-65e9:
-- PR_RULE_MSG_STATE
-- PT_LONG
-3a70:
-- PR_USER_X509_CERTIFICATE
-- PT_MV_BINARY
-"6698":
-- PR_REPLICA_LIST
-- PT_BINARY
-103c:
-- PR_POST_FOLDER_NAMES
-- PT_TSTRING
-665f:
-- PR_ARRIVAL_TIME
-- PT_SYSTIME
-"8005":
-- PR_EMS_AB_MANAGER
-- PT_OBJECT|PT_MV_TSTRING
-"8137":
-- PR_EMS_AB_SITE_AFFINITY
-- PT_MV_TSTRING
-3a71:
-- PR_SEND_INTERNET_ENCODING
-- PT_LONG
-103d:
-- PR_POST_REPLY_FOLDER_ENTRIES
-- PT_BINARY
-35df:
-- PR_VALID_FOLDER_MASK
-- PT_LONG
-"8006":
-- PR_EMS_AB_HOME_MDB
-- PT_OBJECT|PT_MV_TSTRING
-"8138":
-- PR_EMS_AB_SITE_PROXY_SPACE
-- PT_MV_TSTRING
-"4020":
-- PR_READ_RECEIPT_FLAGS
-- PT_LONG
-"6699":
-- PR_OVERALL_AGE_LIMIT
-- PT_LONG
-103e:
-- PR_POST_REPLY_FOLDER_NAMES
-- PT_TSTRING
-"8007":
-- PR_EMS_AB_HOME_MTA
-- PT_OBJECT|PT_MV_TSTRING
-"8139":
-- PR_EMS_AB_SPACE_LAST_COMPUTED
-- PT_SYSTIME
-"4021":
-- PR_SOFT_DELETES
-- PT_BOOLEAN
-65f0:
-- PR_RULE_MSG_CONDITION
-- PT_BINARY
-103f:
-- PR_POST_REPLY_DENIED
-- PT_BINARY
-81c0:
-- PR_EMS_AB_VIEW_CONTAINER_2
-- PT_TSTRING
-65f1:
-- PR_RULE_MSG_CONDITION_LCID
-- PT_LONG
-81c1:
-- PR_EMS_AB_VIEW_CONTAINER_3
-- PT_TSTRING
-65f2:
-- PR_RULE_MSG_VERSION
-- PT_SHORT
-81c2:
-- PR_EMS_AB_PROMO_EXPIRATION
-- PT_SYSTIME
-"8008":
-- PR_EMS_AB_IS_MEMBER_OF_DL
-- PT_OBJECT|PT_MV_TSTRING
-65f3:
-- PR_RULE_MSG_SEQUENCE
-- PT_LONG
-10ca:
-- PR_CAL_REMINDER_NEXT_TIME
-- PT_SYSTIME
-81c3:
-- PR_EMS_AB_DISABLED_GATEWAY_PROXY
-- PT_MV_TSTRING
-"8140":
-- PR_EMS_AB_T_SELECTOR
-- PT_BINARY
-"8009":
-- PR_EMS_AB_MEMBER
-- PT_OBJECT|PT_MV_TSTRING
-"1080":
-- PR_ACTION
-- PT_LONG
-65f4:
-- PR_PREVENT_MSG_CREATE
-- PT_BOOLEAN
-81c4:
-- PR_EMS_AB_COMPROMISED_KEY_LIST
-- PT_BINARY
-"8141":
-- PR_EMS_AB_T_SELECTOR_INBOUND
-- PT_BINARY
-"1081":
-- PR_ACTION_FLAG
-- PT_LONG
-65f5:
-- PR_IMAP_INTERNAL_DATE
-- PT_SYSTIME
-"8010":
-- PR_EMS_AB_HELP_DATA32
-- PT_BINARY
-81c5:
-- PR_EMS_AB_INSADMIN
-- PT_OBJECT|PT_MV_TSTRING
-"8142":
-- PR_EMS_AB_TARGET_MTAS
-- PT_MV_TSTRING
-"1082":
-- PR_ACTION_DATE
-- PT_SYSTIME
-666c:
-- PR_IN_CONFLICT
-- PT_BOOLEAN
-"8011":
-- PR_EMS_AB_TARGET_ADDRESS
-- PT_TSTRING
-81c6:
-- PR_EMS_AB_OVERRIDE_NNTP_CONTENT_FORMAT
-- PT_BOOLEAN
-"8143":
-- PR_EMS_AB_TELETEX_TERMINAL_IDENTIFIER
-- PT_MV_BINARY
-3f00:
-- PR_CONTROL_FLAGS
-- PT_LONG
-810a:
-- PR_EMS_AB_PERIOD_REP_SYNC_TIMES
-- PT_BINARY
-"8012":
-- PR_EMS_AB_TELEPHONE_NUMBER
-- PT_MV_TSTRING
-81c7:
-- PR_EMS_AB_OBJ_VIEW_CONTAINERS
-- PT_OBJECT|PT_MV_TSTRING
-"8144":
-- PR_EMS_AB_TEMP_ASSOC_THRESHOLD
-- PT_LONG
-3f01:
-- PR_CONTROL_STRUCTURE
-- PT_BINARY
-810b:
-- PR_EMS_AB_PERIOD_REPL_STAGGER
-- PT_LONG
-"8013":
-- PR_EMS_AB_NT_SECURITY_DESCRIPTOR
-- PT_BINARY
-"8145":
-- PR_EMS_AB_TOMBSTONE_LIFETIME
-- PT_LONG
-3f02:
-- PR_CONTROL_TYPE
-- PT_LONG
-810c:
-- PR_EMS_AB_POSTAL_ADDRESS
-- PT_MV_BINARY
-"8014":
-- PR_EMS_AB_HOME_MDB_BL
-- PT_OBJECT|PT_MV_TSTRING
-"8146":
-- PR_EMS_AB_TRACKING_LOG_PATH_NAME
-- PT_TSTRING
-3f03:
-- PR_DELTAX
-- PT_LONG
-810d:
-- PR_EMS_AB_PREFERRED_DELIVERY_METHOD
-- PT_MV_LONG
-"8015":
-- PR_EMS_AB_PUBLIC_DELEGATES
-- PT_OBJECT|PT_MV_TSTRING
-"8147":
-- PR_EMS_AB_TRANS_RETRY_MINS
-- PT_LONG
-3f04:
-- PR_DELTAY
-- PT_LONG
-810e:
-- PR_EMS_AB_PRMD
-- PT_TSTRING
-3a4a:
-- PR_CUSTOMER_ID
-- PT_TSTRING
-"8016":
-- PR_EMS_AB_CERTIFICATE_REVOCATION_LIST
-- PT_BINARY
-"8148":
-- PR_EMS_AB_TRANS_TIMEOUT_MINS
-- PT_LONG
-"4030":
-- PR_SENDER_SIMPLE_DISP_NAME
-- PT_UNICODE
-3f05:
-- PR_XPOS
-- PT_LONG
-810f:
-- PR_EMS_AB_PROXY_GENERATOR_DLL
-- PT_TSTRING
-330a:
-- PR_FORM_MESSAGE_BEHAVIOR
-- PT_LONG
-3a4b:
-- PR_TTYTDD_PHONE_NUMBER
-- PT_TSTRING
-"8017":
-- PR_EMS_AB_ADDRESS_ENTRY_DISPLAY_TABLE
-- PT_BINARY
-"8149":
-- PR_EMS_AB_TRANSFER_RETRY_INTERVAL
-- PT_LONG
-"4031":
-- PR_SENT_REPRESENTING_SIMPLE_DISP_NAME
-- PT_UNICODE
-3f06:
-- PR_YPOS
-- PT_LONG
-3a4c:
-- PR_FTP_SITE
-- PT_TSTRING
-"8018":
-- PR_EMS_AB_ADDRESS_SYNTAX
-- PT_BINARY
-3f07:
-- PR_CONTROL_ID
-- PT_BINARY
-80a0:
-- PR_EMS_AB_DXA_TYPES
-- PT_LONG
-3a4d:
-- PR_GENDER
-- PT_SHORT
-3f08:
-- PR_INITIAL_DETAILS_PANE
-- PT_LONG
-80a1:
-- PR_EMS_AB_DXA_UNCONF_CONTAINER_LIST
-- PT_OBJECT|PT_MV_TSTRING
-"8150":
-- PR_EMS_AB_TURN_REQUEST_THRESHOLD
-- PT_LONG
-3a4e:
-- PR_MANAGER_NAME
-- PT_TSTRING
-66fe:
-- PR_OWNER_NAME
-- PT_STRING8
-80a2:
-- PR_EMS_AB_ENCAPSULATION_METHOD
-- PT_LONG
-"8151":
-- PR_EMS_AB_TWO_WAY_ALTERNATE_FACILITY
-- PT_BOOLEAN
-"1090":
-- PR_FLAG_STATUS
-- PT_LONG
-667b:
-- PR_PROFILE_MOAB
-- PT_TSTRING
-80a3:
-- PR_EMS_AB_ENCRYPT
-- PT_BOOLEAN
-"8152":
-- PR_EMS_AB_UNAUTH_ORIG_BL
-- PT_OBJECT|PT_MV_TSTRING
-3a4f:
-- PR_NICKNAME
-- PT_TSTRING
-"3900":
-- PR_DISPLAY_TYPE
-- PT_LONG
-"1091":
-- PR_FLAG_COMPLETE
-- PT_SYSTIME
-66ff:
-- PR_ASSIGNED_ACCESS
-- PT_LONG
-667c:
-- PR_PROFILE_MOAB_GUID
-- PT_TSTRING
-80a4:
-- PR_EMS_AB_EXPAND_DLS_LOCALLY
-- PT_BOOLEAN
-"8153":
-- PR_EMS_AB_USER_PASSWORD
-- PT_MV_BINARY
-811a:
-- PR_EMS_AB_REMOTE_BRIDGE_HEAD
-- PT_TSTRING
-667d:
-- PR_PROFILE_MOAB_SEQ
-- PT_LONG
-80a5:
-- PR_EMS_AB_EXPORT_CONTAINERS
-- PT_OBJECT|PT_MV_TSTRING
-"8154":
-- PR_EMS_AB_USN_CREATED
-- PT_LONG
-"3902":
-- PR_TEMPLATEID
-- PT_BINARY
-811b:
-- PR_EMS_AB_REMOTE_BRIDGE_HEAD_ADDRESS
-- PT_TSTRING
-80a6:
-- PR_EMS_AB_EXPORT_CUSTOM_RECIPIENTS
-- PT_BOOLEAN
-"8023":
-- PR_EMS_AB_BUSINESS_ROLES
-- PT_BINARY
-"8155":
-- PR_EMS_AB_USN_DSA_LAST_OBJ_REMOVED
-- PT_LONG
-811c:
-- PR_EMS_AB_REMOTE_OUT_BH_SERVER
-- PT_OBJECT|PT_MV_TSTRING
-"4038":
-- PR_CREATOR_SIMPLE_DISP_NAME
-- PT_UNICODE
-667f:
-- PR_IMPLIED_RESTRICTIONS
-- PT_MV_BINARY
-80a7:
-- PR_EMS_AB_EXTENDED_CHARS_ALLOWED
-- PT_BOOLEAN
-"8024":
-- PR_EMS_AB_OWNER_BL
-- PT_OBJECT|PT_MV_TSTRING
-"8156":
-- PR_EMS_AB_USN_LAST_OBJ_REM
-- PT_LONG
-"3904":
-- PR_PRIMARY_CAPABILITY
-- PT_BINARY
-7c0a:
-- PR_STORE_SLOWLINK
-- PT_BOOLEAN
-811d:
-- PR_EMS_AB_REMOTE_SITE
-- PT_OBJECT|PT_MV_TSTRING
-80a8:
-- PR_EMS_AB_EXTENSION_DATA
-- PT_MV_BINARY
-"8025":
-- PR_EMS_AB_CROSS_CERTIFICATE_PAIR
-- PT_MV_BINARY
-"8157":
-- PR_EMS_AB_USN_SOURCE
-- PT_LONG
-811e:
-- PR_EMS_AB_REPLICATION_SENSITIVITY
-- PT_LONG
-80a9:
-- PR_EMS_AB_EXTENSION_NAME
-- PT_MV_TSTRING
-"8026":
-- PR_EMS_AB_AUTHORITY_REVOCATION_LIST
-- PT_MV_BINARY
-"8158":
-- PR_EMS_AB_X121_ADDRESS
-- PT_MV_TSTRING
-811f:
-- PR_EMS_AB_REPLICATION_STAGGER
-- PT_LONG
-3a5a:
-- PR_HOME_ADDRESS_COUNTRY
-- PT_TSTRING
-"8027":
-- PR_EMS_AB_ASSOC_NT_ACCOUNT
-- PT_BINARY
-"8159":
-- PR_EMS_AB_X25_CALL_USER_DATA_INCOMING
-- PT_BINARY
-3a5b:
-- PR_HOME_ADDRESS_POSTAL_CODE
-- PT_TSTRING
-"8028":
-- PR_EMS_AB_EXPIRATION_TIME
-- PT_SYSTIME
-3a5c:
-- PR_HOME_ADDRESS_STATE_OR_PROVINCE
-- PT_TSTRING
-"8029":
-- PR_EMS_AB_USN_CHANGED
-- PT_LONG
-400a:
-- PR_START_SUB_FLD
-- PT_LONG
-80b0:
-- PR_EMS_AB_GATEWAY_LOCAL_CRED
-- PT_TSTRING
-3a5d:
-- PR_HOME_ADDRESS_STREET
-- PT_TSTRING
-81ab:
-- PR_EMS_AB_HTTP_SERVERS
-- PT_MV_TSTRING
-400b:
-- PR_END_FOLDER
-- PT_LONG
-80b1:
-- PR_EMS_AB_GATEWAY_LOCAL_DESIG
-- PT_TSTRING
-"8160":
-- PR_EMS_AB_X400_ATTACHMENT_TYPE
-- PT_BINARY
-3a5e:
-- PR_HOME_ADDRESS_POST_OFFICE_BOX
-- PT_TSTRING
-81ac:
-- PR_EMS_AB_MODERATED
-- PT_BOOLEAN
-400c:
-- PR_START_MESSAGE
-- PT_LONG
-80b2:
-- PR_EMS_AB_GATEWAY_PROXY
-- PT_MV_TSTRING
-"8161":
-- PR_EMS_AB_X400_SELECTOR_SYNTAX
-- PT_LONG
-3a5f:
-- PR_OTHER_ADDRESS_CITY
-- PT_TSTRING
-81ad:
-- PR_EMS_AB_RAS_ACCOUNT
-- PT_TSTRING
-400d:
-- PR_END_MESSAGE
-- PT_LONG
-668b:
-- PR_NNTP_CONTROL_FOLDER_ENTRYID
-- PT_BINARY
-80b3:
-- PR_EMS_AB_GATEWAY_ROUTING_TREE
-- PT_BINARY
-"8030":
-- PR_EMS_AB_EXTENSION_ATTRIBUTE_4
-- PT_TSTRING
-"8162":
-- PR_EMS_AB_X500_ACCESS_CONTROL_LIST
-- PT_BINARY
-81ae:
-- PR_EMS_AB_RAS_PASSWORD
-- PT_BINARY
-812a:
-- PR_EMS_AB_RUNS_ON
-- PT_OBJECT|PT_MV_TSTRING
-400e:
-- PR_END_ATTACH
-- PT_LONG
-668c:
-- PR_NEWSGROUP_ROOT_FOLDER_ENTRYID
-- PT_BINARY
-80b4:
-- PR_EMS_AB_GWART_LAST_MODIFIED
-- PT_SYSTIME
-"8031":
-- PR_EMS_AB_EXTENSION_ATTRIBUTE_5
-- PT_TSTRING
-"8163":
-- PR_EMS_AB_XMIT_TIMEOUT_NON_URGENT
-- PT_LONG
-81af:
-- PR_EMS_AB_INCOMING_PASSWORD
-- PT_BINARY
-812b:
-- PR_EMS_AB_S_SELECTOR
-- PT_BINARY
-400f:
-- PR_EC_WARNING
-- PT_LONG
-668d:
-- PR_INBOUND_NEWSFEED_DN
-- PT_TSTRING
-80b5:
-- PR_EMS_AB_HAS_FULL_REPLICA_NCS
-- PT_OBJECT|PT_MV_TSTRING
-"8032":
-- PR_EMS_AB_EXTENSION_ATTRIBUTE_6
-- PT_TSTRING
-"8164":
-- PR_EMS_AB_XMIT_TIMEOUT_NORMAL
-- PT_LONG
-812c:
-- PR_EMS_AB_S_SELECTOR_INBOUND
-- PT_BINARY
-668e:
-- PR_OUTBOUND_NEWSFEED_DN
-- PT_TSTRING
-80b6:
-- PR_EMS_AB_HAS_MASTER_NCS
-- PT_OBJECT|PT_MV_TSTRING
-"8033":
-- PR_EMS_AB_EXTENSION_ATTRIBUTE_7
-- PT_TSTRING
-"8165":
-- PR_EMS_AB_XMIT_TIMEOUT_URGENT
-- PT_LONG
-812d:
-- PR_EMS_AB_SEARCH_FLAGS
-- PT_LONG
-668f:
-- PR_DELETED_ON
-- PT_SYSTIME
-80b7:
-- PR_EMS_AB_HEURISTICS
-- PT_LONG
-"8034":
-- PR_EMS_AB_EXTENSION_ATTRIBUTE_8
-- PT_TSTRING
-"8166":
-- PR_EMS_AB_SITE_FOLDER_GUID
-- PT_BINARY
-812e:
-- PR_EMS_AB_SEARCH_GUIDE
-- PT_MV_BINARY
-80b8:
-- PR_EMS_AB_HIDE_DL_MEMBERSHIP
-- PT_BOOLEAN
-"8035":
-- PR_EMS_AB_EXTENSION_ATTRIBUTE_9
-- PT_TSTRING
-"8167":
-- PR_EMS_AB_SITE_FOLDER_SERVER
-- PT_OBJECT|PT_MV_TSTRING
-812f:
-- PR_EMS_AB_SEE_ALSO
-- PT_OBJECT|PT_MV_TSTRING
-80b9:
-- PR_EMS_AB_HIDE_FROM_ADDRESS_BOOK
-- PT_BOOLEAN
-"8036":
-- PR_EMS_AB_EXTENSION_ATTRIBUTE_10
-- PT_TSTRING
-"8168":
-- PR_EMS_AB_REPLICATION_MAIL_MSG_SIZE
-- PT_LONG
-"8037":
-- PR_EMS_AB_SECURITY_PROTOCOL
-- PT_MV_BINARY
-"8169":
-- PR_EMS_AB_MAXIMUM_OBJECT_ID
-- PT_BINARY
-"8038":
-- PR_EMS_AB_PF_CONTACTS
-- PT_OBJECT|PT_MV_TSTRING
-401a:
-- PR_SENT_REPRESENTING_FLAGS
-- PT_LONG
-65ea:
-- PR_RULE_MSG_USER_FLAGS
-- PT_LONG
-80c0:
-- PR_EMS_AB_IS_DELETED
-- PT_BOOLEAN
-81ba:
-- PR_EMS_AB_GROUP_BY_ATTR_4
-- PT_TSTRING
-401b:
-- PR_RCVD_BY_FLAGS
-- PT_LONG
-65eb:
-- PR_RULE_MSG_PROVIDER
-- PT_UNICODE
-80c1:
-- PR_EMS_AB_IS_SINGLE_VALUED
-- PT_BOOLEAN
-"8170":
-- PR_EMS_AB_NETWORK_ADDRESS
-- PT_MV_TSTRING
-401c:
-- PR_RCVD_REPRESENTING_FLAGS
-- PT_LONG
-65ec:
-- PR_RULE_MSG_NAME
-- PT_UNICODE
-669a:
-- PR_INTERNET_CHARSET
-- PT_TSTRING
-80c2:
-- PR_EMS_AB_KCC_STATUS
-- PT_MV_BINARY
-"8171":
-- PR_EMS_AB_LDAP_DISPLAY_NAME
-- PT_MV_TSTRING
-401d:
-- PR_ORIGINAL_SENDER_FLAGS
-- PT_LONG
-65ed:
-- PR_RULE_MSG_LEVEL
-- PT_LONG
-669b:
-- PR_DELETED_MESSAGE_SIZE
-- PT_I8
-80c3:
-- PR_EMS_AB_KNOWLEDGE_INFORMATION
-- PT_MV_TSTRING
-"8040":
-- PR_EMS_AB_ENCRYPT_ALG_LIST_NA
-- PT_MV_TSTRING
-401e:
-- PR_ORIGINAL_SENT_REPRESENTING_FLAGS
-- PT_LONG
-65ee:
-- PR_RULE_MSG_PROVIDER_DATA
-- PT_BINARY
-669c:
-- PR_DELETED_NORMAL_MESSAGE_SIZE
-- PT_I8
-80c4:
-- PR_EMS_AB_LINE_WRAP
-- PT_LONG
-"8041":
-- PR_EMS_AB_ENCRYPT_ALG_LIST_OTHER
-- PT_MV_TSTRING
-"8173":
-- PR_EMS_AB_SCHEMA_FLAGS
-- PT_LONG
-81be:
-- PR_EMS_AB_VIEW_SITE
-- PT_TSTRING
-813a:
-- PR_EMS_AB_STREET_ADDRESS
-- PT_TSTRING
-401f:
-- PR_REPORT_FLAGS
-- PT_LONG
-669d:
-- PR_DELETED_ASSOC_MESSAGE_SIZE
-- PT_I8
-80c5:
-- PR_EMS_AB_LINK_ID
-- PT_LONG
-"8042":
-- PR_EMS_AB_IMPORTED_FROM
-- PT_TSTRING
-"8174":
-- PR_EMS_AB_BRIDGEHEAD_SERVERS
-- PT_OBJECT|PT_MV_TSTRING
-81bf:
-- PR_EMS_AB_VIEW_CONTAINER_1
-- PT_TSTRING
-813b:
-- PR_EMS_AB_SUB_REFS
-- PT_OBJECT|PT_MV_TSTRING
-65ef:
-- PR_RULE_MSG_ACTIONS
-- PT_BINARY
-669e:
-- PR_SECURE_IN_SITE
-- PT_BOOLEAN
-80c6:
-- PR_EMS_AB_LOCAL_BRIDGE_HEAD
-- PT_TSTRING
-"8043":
-- PR_EMS_AB_ENCRYPT_ALG_SELECTED_NA
-- PT_TSTRING
-"8175":
-- PR_EMS_AB_WWW_HOME_PAGE
-- PT_TSTRING
-3e00:
-- PR_IDENTITY_DISPLAY
-- PT_TSTRING
-800a:
-- PR_EMS_AB_AUTOREPLY_MESSAGE
-- PT_TSTRING
-813c:
-- PR_EMS_AB_SUBMISSION_CONT_LENGTH
-- PT_LONG
-80c7:
-- PR_EMS_AB_LOCAL_BRIDGE_HEAD_ADDRESS
-- PT_TSTRING
-"8044":
-- PR_EMS_AB_ACCESS_CATEGORY
-- PT_LONG
-"8176":
-- PR_EMS_AB_NNTP_CONTENT_FORMAT
-- PT_TSTRING
-3e01:
-- PR_IDENTITY_ENTRYID
-- PT_BINARY
-800b:
-- PR_EMS_AB_AUTOREPLY
-- PT_BOOLEAN
-813d:
-- PR_EMS_AB_SUPPORTED_APPLICATION_CONTEXT
-- PT_MV_BINARY
-"4059":
-- PR_CREATOR_FLAGS
-- PT_LONG
-80c8:
-- PR_EMS_AB_LOCAL_INITIAL_TURN
-- PT_BOOLEAN
-"8045":
-- PR_EMS_AB_ACTIVATION_SCHEDULE
-- PT_BINARY
-"8177":
-- PR_EMS_AB_POP_CONTENT_FORMAT
-- PT_TSTRING
-3e02:
-- PR_RESOURCE_METHODS
-- PT_LONG
-800c:
-- PR_EMS_AB_OWNER
-- PT_OBJECT|PT_MV_TSTRING
-813e:
-- PR_EMS_AB_SUPPORTING_STACK
-- PT_OBJECT|PT_MV_TSTRING
-80c9:
-- PR_EMS_AB_LOCAL_SCOPE
-- PT_OBJECT|PT_MV_TSTRING
-"8046":
-- PR_EMS_AB_ACTIVATION_STYLE
-- PT_LONG
-"8178":
-- PR_EMS_AB_LANGUAGE
-- PT_LONG
-3e03:
-- PR_RESOURCE_TYPE
-- PT_LONG
-800d:
-- PR_EMS_AB_KM_SERVER
-- PT_OBJECT|PT_MV_TSTRING
-813f:
-- PR_EMS_AB_SUPPORTING_STACK_BL
-- PT_OBJECT|PT_MV_TSTRING
-"8047":
-- PR_EMS_AB_ADDRESS_ENTRY_DISPLAY_TABLE_MSDOS
-- PT_BINARY
-"8179":
-- PR_EMS_AB_POP_CHARACTER_SET
-- PT_TSTRING
-"4061":
-- PR_ORIGINATOR_SEARCH_KEY
-- PT_BINARY
-3e04:
-- PR_STATUS_CODE
-- PT_LONG
-800e:
-- PR_EMS_AB_REPORTS
-- PT_OBJECT
-"8048":
-- PR_EMS_AB_ADDRESS_TYPE
-- PT_TSTRING
-3e05:
-- PR_IDENTITY_SEARCH_KEY
-- PT_BINARY
-800f:
-- PR_EMS_AB_PROXY_ADDRESSES
-- PT_MV_TSTRING
-80d0:
-- PR_EMS_AB_MDB_MSG_TIME_OUT_PERIOD
-- PT_LONG
-"8049":
-- PR_EMS_AB_ADMD
-- PT_TSTRING
-3e06:
-- PR_OWN_STORE_ENTRYID
-- PT_BINARY
-80d1:
-- PR_EMS_AB_MDB_OVER_QUOTA_LIMIT
-- PT_LONG
-"8180":
-- PR_EMS_AB_CONNECTION_LIST_FILTER
-- PT_BINARY
-"4064":
-- PR_REPORT_DESTINATION_SEARCH_KEY
-- PT_BINARY
-3e07:
-- PR_RESOURCE_PATH
-- PT_TSTRING
-80d2:
-- PR_EMS_AB_MDB_STORAGE_QUOTA
-- PT_LONG
-"8181":
-- PR_EMS_AB_AVAILABLE_AUTHORIZATION_PACKAGES
-- PT_MV_TSTRING
-"4065":
-- PR_ER_FLAG
-- PT_LONG
-3e08:
-- PR_STATUS_STRING
-- PT_TSTRING
-402c:
-- PR_MESSAGE_SUBMISSION_ID_FROM_CLIENT
-- PT_BINARY
-80d3:
-- PR_EMS_AB_MDB_UNREAD_LIMIT
-- PT_LONG
-"8050":
-- PR_EMS_AB_ANCESTOR_ID
-- PT_BINARY
-"8182":
-- PR_EMS_AB_CHARACTER_SET_LIST
-- PT_MV_TSTRING
-3e09:
-- PR_X400_DEFERRED_DELIVERY_CANCEL
-- PT_BOOLEAN
-"8051":
-- PR_EMS_AB_ASSOC_REMOTE_DXA
-- PT_OBJECT|PT_MV_TSTRING
-"8183":
-- PR_EMS_AB_USE_SITE_VALUES
-- PT_BOOLEAN
-814a:
-- PR_EMS_AB_TRANSFER_TIMEOUT_NON_URGENT
-- PT_LONG
-80d4:
-- PR_EMS_AB_MDB_USE_DEFAULTS
-- PT_BOOLEAN
-"8052":
-- PR_EMS_AB_ASSOCIATION_LIFETIME
-- PT_LONG
-"8184":
-- PR_EMS_AB_ENABLED_AUTHORIZATION_PACKAGES
-- PT_MV_TSTRING
-814b:
-- PR_EMS_AB_TRANSFER_TIMEOUT_NORMAL
-- PT_LONG
-"4068":
-- PR_INTERNET_SUBJECT
-- PT_BINARY
-80d5:
-- PR_EMS_AB_MESSAGE_TRACKING_ENABLED
-- PT_BOOLEAN
-"8053":
-- PR_EMS_AB_AUTH_ORIG_BL
-- PT_OBJECT|PT_MV_TSTRING
-"8185":
-- PR_EMS_AB_CHARACTER_SET
-- PT_TSTRING
-814c:
-- PR_EMS_AB_TRANSFER_TIMEOUT_URGENT
-- PT_LONG
-"4069":
-- PR_INTERNET_SENT_REPRESENTING_NAME
-- PT_BINARY
-80d6:
-- PR_EMS_AB_MONITOR_CLOCK
-- PT_BOOLEAN
-"8054":
-- PR_EMS_AB_AUTHORIZED_DOMAIN
-- PT_TSTRING
-"8186":
-- PR_EMS_AB_CONTENT_TYPE
-- PT_LONG
-814d:
-- PR_EMS_AB_TRANSLATION_TABLE_USED
-- PT_LONG
-80d7:
-- PR_EMS_AB_MONITOR_SERVERS
-- PT_BOOLEAN
-"8187":
-- PR_EMS_AB_ANONYMOUS_ACCESS
-- PT_BOOLEAN
-814e:
-- PR_EMS_AB_TRANSPORT_EXPEDITED_DATA
-- PT_BOOLEAN
-8c18:
-- PR_EMS_AB_VIEW_FLAGS
-- PT_LONG
-80d8:
-- PR_EMS_AB_MONITOR_SERVICES
-- PT_BOOLEAN
-"8055":
-- PR_EMS_AB_AUTHORIZED_PASSWORD
-- PT_BINARY
-"8188":
-- PR_EMS_AB_CONTROL_MSG_FOLDER_ID
-- PT_BINARY
-814f:
-- PR_EMS_AB_TRUST_LEVEL
-- PT_LONG
-8c19:
-- PR_EMS_AB_GROUP_BY_ATTR_VALUE_STR
-- PT_TSTRING
-80d9:
-- PR_EMS_AB_MONITORED_CONFIGURATIONS
-- PT_OBJECT|PT_MV_TSTRING
-"8056":
-- PR_EMS_AB_AUTHORIZED_USER
-- PT_TSTRING
-"8189":
-- PR_EMS_AB_USENET_SITE_NAME
-- PT_TSTRING
-3fc9:
-- PR_LAST_CONFLICT
-- PT_BINARY
-"8057":
-- PR_EMS_AB_BUSINESS_CATEGORY
-- PT_MV_TSTRING
-"8058":
-- PR_EMS_AB_CAN_CREATE_PF
-- PT_OBJECT|PT_MV_TSTRING
-8c20:
-- PR_EMS_AB_INBOUND_ACCEPT_ALL
-- PT_BOOLEAN
-80e0:
-- PR_EMS_AB_MONITORING_CACHED_VIA_MAIL
-- PT_OBJECT|PT_MV_TSTRING
-3fd0:
-- PR_REPL_HEADER
-- PT_BINARY
-"8059":
-- PR_EMS_AB_CAN_CREATE_PF_BL
-- PT_OBJECT|PT_MV_TSTRING
-8c21:
-- PR_EMS_AB_ENABLED
-- PT_BOOLEAN
-80e1:
-- PR_EMS_AB_MONITORING_CACHED_VIA_RPC
-- PT_OBJECT|PT_MV_TSTRING
-"8190":
-- PR_EMS_AB_INCOMING_MSG_SIZE_LIMIT
-- PT_LONG
-8c22:
-- PR_EMS_AB_PRESERVE_INTERNET_CONTENT
-- PT_BOOLEAN
-80e2:
-- PR_EMS_AB_MONITORING_ESCALATION_PROCEDURE
-- PT_MV_BINARY
-"8191":
-- PR_EMS_AB_SEND_TNEF
-- PT_BOOLEAN
-3fd1:
-- PR_REPL_STATUS
-- PT_BINARY
-80aa:
-- PR_EMS_AB_EXTENSION_NAME_INHERITED
-- PT_MV_TSTRING
-403d:
-- PR_ORG_ADDR_TYPE
-- PT_UNICODE
-8c23:
-- PR_EMS_AB_DISABLE_DEFERRED_COMMIT
-- PT_BOOLEAN
-80e3:
-- PR_EMS_AB_MONITORING_HOTSITE_POLL_INTERVAL
-- PT_LONG
-"8060":
-- PR_EMS_AB_CAN_PRESERVE_DNS
-- PT_BOOLEAN
-"8192":
-- PR_EMS_AB_AUTHORIZED_PASSWORD_CONFIRM
-- PT_BINARY
-3fd2:
-- PR_REPL_CHANGES
-- PT_BINARY
-80ab:
-- PR_EMS_AB_FACSIMILE_TELEPHONE_NUMBER
-- PT_MV_BINARY
-403e:
-- PR_ORG_EMAIL_ADDR
-- PT_UNICODE
-8c24:
-- PR_EMS_AB_CLIENT_ACCESS_ENABLED
-- PT_BOOLEAN
-80e4:
-- PR_EMS_AB_MONITORING_HOTSITE_POLL_UNITS
-- PT_LONG
-"8061":
-- PR_EMS_AB_CLOCK_ALERT_OFFSET
-- PT_LONG
-"8193":
-- PR_EMS_AB_INBOUND_NEWSFEED
-- PT_TSTRING
-3fd3:
-- PR_REPL_RGM
-- PT_BINARY
-80ac:
-- PR_EMS_AB_FILE_VERSION
-- PT_BINARY
-815a:
-- PR_EMS_AB_X25_CALL_USER_DATA_OUTGOING
-- PT_BINARY
-8c25:
-- PR_EMS_AB_REQUIRE_SSL
-- PT_BOOLEAN
-80e5:
-- PR_EMS_AB_MONITORING_MAIL_UPDATE_INTERVAL
-- PT_LONG
-"8062":
-- PR_EMS_AB_CLOCK_ALERT_REPAIR
-- PT_BOOLEAN
-"8194":
-- PR_EMS_AB_NEWSFEED_TYPE
-- PT_LONG
-3fd4:
-- PR_RMI
-- PT_BINARY
-80ad:
-- PR_EMS_AB_FILTER_LOCAL_ADDRESSES
-- PT_BOOLEAN
-815b:
-- PR_EMS_AB_X25_FACILITIES_DATA_INCOMING
-- PT_BINARY
-"8063":
-- PR_EMS_AB_CLOCK_WARNING_OFFSET
-- PT_LONG
-80ae:
-- PR_EMS_AB_FOLDERS_CONTAINER
-- PT_OBJECT|PT_MV_TSTRING
-80e6:
-- PR_EMS_AB_MONITORING_MAIL_UPDATE_UNITS
-- PT_LONG
-815c:
-- PR_EMS_AB_X25_FACILITIES_DATA_OUTGOING
-- PT_BINARY
-"8195":
-- PR_EMS_AB_OUTBOUND_NEWSFEED
-- PT_TSTRING
-8c26:
-- PR_EMS_AB_ANONYMOUS_ACCOUNT
-- PT_TSTRING
-3fd5:
-- PR_INTERNAL_POST_REPLY
-- PT_BINARY
-"8064":
-- PR_EMS_AB_CLOCK_WARNING_REPAIR
-- PT_BOOLEAN
-80af:
-- PR_EMS_AB_GARBAGE_COLL_PERIOD
-- PT_LONG
-80e7:
-- PR_EMS_AB_MONITORING_NORMAL_POLL_INTERVAL
-- PT_LONG
-815d:
-- PR_EMS_AB_X25_LEASED_LINE_PORT
-- PT_BINARY
-"8196":
-- PR_EMS_AB_NEWSGROUP_LIST
-- PT_BINARY
-8c27:
-- PR_EMS_AB_CERTIFICATE_CHAIN_V3
-- PT_BINARY
-3fd6:
-- PR_NTSD_MODIFICATION_TIME
-- PT_SYSTIME
-"8065":
-- PR_EMS_AB_COMPUTER_NAME
-- PT_TSTRING
-80e8:
-- PR_EMS_AB_MONITORING_NORMAL_POLL_UNITS
-- PT_LONG
-815e:
-- PR_EMS_AB_X25_LEASED_OR_SWITCHED
-- PT_BOOLEAN
-"8197":
-- PR_EMS_AB_NNTP_DISTRIBUTIONS
-- PT_MV_TSTRING
-8c28:
-- PR_EMS_AB_CERTIFICATE_REVOCATION_LIST_V3
-- PT_BINARY
-802d:
-- PR_EMS_AB_EXTENSION_ATTRIBUTE_1
-- PT_TSTRING
-3fd8:
-- PR_PREVIEW_UNREAD
-- PT_TSTRING
-"8066":
-- PR_EMS_AB_CONNECTED_DOMAINS
-- PT_MV_TSTRING
-80e9:
-- PR_EMS_AB_MONITORING_RECIPIENTS
-- PT_OBJECT|PT_MV_TSTRING
-815f:
-- PR_EMS_AB_X25_REMOTE_MTA_PHONE
-- PT_TSTRING
-"8198":
-- PR_EMS_AB_NEWSGROUP
-- PT_TSTRING
-8c29:
-- PR_EMS_AB_CERTIFICATE_REVOCATION_LIST_V1
-- PT_BINARY
-802e:
-- PR_EMS_AB_EXTENSION_ATTRIBUTE_2
-- PT_TSTRING
-3fd9:
-- PR_PREVIEW
-- PT_TSTRING
-"8067":
-- PR_EMS_AB_CONTAINER_INFO
-- PT_LONG
-"8199":
-- PR_EMS_AB_MODERATOR
-- PT_TSTRING
-802f:
-- PR_EMS_AB_EXTENSION_ATTRIBUTE_3
-- PT_TSTRING
-"8068":
-- PR_EMS_AB_COST
-- PT_LONG
-"8069":
-- PR_EMS_AB_COUNTRY_NAME
-- PT_TSTRING
-80f0:
-- PR_EMS_AB_MTA_LOCAL_DESIG
-- PT_TSTRING
-8c30:
-- PR_EMS_AB_CROSS_CERTIFICATE_CRL
-- PT_MV_BINARY
-"3000":
-- PR_ROWID
-- PT_LONG
-80f1:
-- PR_EMS_AB_N_ADDRESS
-- PT_BINARY
-8c31:
-- PR_EMS_AB_SEND_EMAIL_MESSAGE
-- PT_BOOLEAN
-"3001":
-- PR_DISPLAY_NAME
-- PT_TSTRING
-80ba:
-- PR_EMS_AB_IMPORT_CONTAINER
-- PT_OBJECT|PT_MV_TSTRING
-80f2:
-- PR_EMS_AB_N_ADDRESS_TYPE
-- PT_LONG
-8c32:
-- PR_EMS_AB_ENABLE_COMPATIBILITY
-- PT_BOOLEAN
-"3002":
-- PR_ADDRTYPE
-- PT_TSTRING
-3fe2:
-- PR_FOLDER_DESIGN_FLAGS
-- PT_LONG
-"8070":
-- PR_EMS_AB_DESTINATION_INDICATOR
-- PT_MV_TSTRING
-80bb:
-- PR_EMS_AB_IMPORT_SENSITIVITY
-- PT_LONG
-80f3:
-- PR_EMS_AB_NT_MACHINE_NAME
-- PT_TSTRING
-8c33:
-- PR_EMS_AB_SMIME_ALG_LIST_NA
-- PT_MV_TSTRING
-3fe3:
-- PR_DELEGATED_BY_RULE
-- PT_BOOLEAN
-"8071":
-- PR_EMS_AB_DIAGNOSTIC_REG_KEY
-- PT_TSTRING
-80bc:
-- PR_EMS_AB_INBOUND_SITES
-- PT_OBJECT|PT_MV_TSTRING
-80f4:
-- PR_EMS_AB_NUM_OF_OPEN_RETRIES
-- PT_LONG
-8c34:
-- PR_EMS_AB_SMIME_ALG_LIST_OTHER
-- PT_MV_TSTRING
-"3003":
-- PR_EMAIL_ADDRESS
-- PT_TSTRING
-3fe4:
-- PR_DESIGN_IN_PROGRESS
-- PT_BOOLEAN
-"8072":
-- PR_EMS_AB_DL_MEM_REJECT_PERMS_BL
-- PT_OBJECT|PT_MV_TSTRING
-80bd:
-- PR_EMS_AB_INSTANCE_TYPE
-- PT_LONG
-80f5:
-- PR_EMS_AB_NUM_OF_TRANSFER_RETRIES
-- PT_LONG
-8c35:
-- PR_EMS_AB_SMIME_ALG_SELECTED_NA
-- PT_TSTRING
-"3004":
-- PR_COMMENT
-- PT_TSTRING
-803a:
-- PR_EMS_AB_HELP_DATA16
-- PT_BINARY
-3fe5:
-- PR_SECURE_ORIGINATION
-- PT_BOOLEAN
-"8073":
-- PR_EMS_AB_DL_MEM_SUBMIT_PERMS_BL
-- PT_OBJECT|PT_MV_TSTRING
-80be:
-- PR_EMS_AB_INTERNATIONAL_ISDN_NUMBER
-- PT_MV_TSTRING
-80f6:
-- PR_EMS_AB_OBJECT_CLASS_CATEGORY
-- PT_LONG
-8c36:
-- PR_EMS_AB_SMIME_ALG_SELECTED_OTHER
-- PT_TSTRING
-"3005":
-- PR_DEPTH
-- PT_LONG
-803b:
-- PR_EMS_AB_HELP_FILE_NAME
-- PT_TSTRING
-3fe6:
-- PR_PUBLISH_IN_ADDRESS_BOOK
-- PT_BOOLEAN
-"8074":
-- PR_EMS_AB_DL_MEMBER_RULE
-- PT_MV_BINARY
-80bf:
-- PR_EMS_AB_INVOCATION_ID
-- PT_BINARY
-80f7:
-- PR_EMS_AB_OBJECT_VERSION
-- PT_LONG
-8c37:
-- PR_EMS_AB_DEFAULT_MESSAGE_FORMAT
-- PT_BOOLEAN
-"3006":
-- PR_PROVIDER_DISPLAY
-- PT_TSTRING
-803c:
-- PR_EMS_AB_OBJ_DIST_NAME
-- PT_OBJECT|PT_MV_TSTRING
-3fe7:
-- PR_RESOLVE_METHOD
-- PT_LONG
-3d00:
-- PR_STORE_PROVIDERS
-- PT_BINARY
-"8075":
-- PR_EMS_AB_DOMAIN_DEF_ALT_RECIP
-- PT_OBJECT|PT_MV_TSTRING
-80f8:
-- PR_EMS_AB_OFF_LINE_AB_CONTAINERS
-- PT_OBJECT|PT_MV_TSTRING
-8c38:
-- PR_EMS_AB_TYPE
-- PT_TSTRING
-"3007":
-- PR_CREATION_TIME
-- PT_SYSTIME
-803d:
-- PR_EMS_AB_ENCRYPT_ALG_SELECTED_OTHER
-- PT_TSTRING
-3fe8:
-- PR_ADDRESS_BOOK_DISPLAY_NAME
-- PT_TSTRING
-3d01:
-- PR_AB_PROVIDERS
-- PT_BINARY
-"8076":
-- PR_EMS_AB_DOMAIN_NAME
-- PT_TSTRING
-80f9:
-- PR_EMS_AB_OFF_LINE_AB_SCHEDULE
-- PT_BINARY
-"3008":
-- PR_LAST_MODIFICATION_TIME
-- PT_SYSTIME
-803e:
-- PR_EMS_AB_AUTOREPLY_SUBJECT
-- PT_TSTRING
-3fe9:
-- PR_EFORMS_LOCALE_ID
-- PT_LONG
-3d02:
-- PR_TRANSPORT_PROVIDERS
-- PT_BINARY
-"8077":
-- PR_EMS_AB_DSA_SIGNATURE
-- PT_BINARY
-"3009":
-- PR_RESOURCE_FLAGS
-- PT_LONG
-803f:
-- PR_EMS_AB_HOME_PUBLIC_SERVER
-- PT_OBJECT|PT_MV_TSTRING
-"8078":
-- PR_EMS_AB_DXA_ADMIN_COPY
-- PT_BOOLEAN
-3d04:
-- PR_DEFAULT_PROFILE
-- PT_BOOLEAN
-"8079":
-- PR_EMS_AB_DXA_ADMIN_FORWARD
-- PT_BOOLEAN
-3ff0:
-- PR_CONFLICT_ENTRYID
-- PT_BINARY
-8c40:
-- PR_EMS_AB_VOICE_MAIL_FLAGS
-- PT_MV_BINARY
-405a:
-- PR_MODIFIER_FLAGS
-- PT_LONG
-3d05:
-- PR_AB_SEARCH_PATH
-- PT_MV_BINARY
-3ff1:
-- PR_MESSAGE_LOCALE_ID
-- PT_LONG
-8c41:
-- PR_EMS_AB_VOICE_MAIL_VOLUME
-- PT_LONG
-405b:
-- PR_ORIGINATOR_FLAGS
-- PT_LONG
-3d06:
-- PR_AB_DEFAULT_DIR
-- PT_BINARY
-3ff2:
-- PR_RULE_TRIGGER_HISTORY
-- PT_BINARY
-80ca:
-- PR_EMS_AB_LOG_FILENAME
-- PT_TSTRING
-8c42:
-- PR_EMS_AB_VOICE_MAIL_SPEED
-- PT_LONG
-405c:
-- PR_REPORT_DESTINATION_FLAGS
-- PT_LONG
-"8080":
-- PR_EMS_AB_DXA_EXCHANGE_OPTIONS
-- PT_LONG
-3d07:
-- PR_AB_DEFAULT_PAB
-- PT_BINARY
-80cb:
-- PR_EMS_AB_LOG_ROLLOVER_INTERVAL
-- PT_LONG
-8c43:
-- PR_EMS_AB_VOICE_MAIL_RECORDING_LENGTH
-- PT_MV_LONG
-405d:
-- PR_ORIGINAL_AUTHOR_FLAGS
-- PT_LONG
-"6800":
-- PR_MAILBEAT_BOUNCE_SERVER
-- PT_UNICODE
-3ff3:
-- PR_MOVE_TO_STORE_ENTRYID
-- PT_BINARY
-"8081":
-- PR_EMS_AB_DXA_EXPORT_NOW
-- PT_BOOLEAN
-3d08:
-- PR_FILTERING_HOOKS
-- PT_BINARY
-80cc:
-- PR_EMS_AB_MAINTAIN_AUTOREPLY_HISTORY
-- PT_BOOLEAN
-817a:
-- PR_EMS_AB_USN_INTERSITE
-- PT_LONG
-8c44:
-- PR_EMS_AB_DISPLAY_NAME_SUFFIX
-- PT_TSTRING
-"6801":
-- PR_MAILBEAT_REQUEST_SENT
-- PT_SYSTIME
-3ff4:
-- PR_MOVE_TO_FOLDER_ENTRYID
-- PT_BINARY
-"8082":
-- PR_EMS_AB_DXA_FLAGS
-- PT_LONG
-3d09:
-- PR_SERVICE_NAME
-- PT_TSTRING
-80cd:
-- PR_EMS_AB_MAPI_DISPLAY_TYPE
-- PT_LONG
-817b:
-- PR_EMS_AB_SUB_SITE
-- PT_TSTRING
-8c45:
-- PR_EMS_AB_ATTRIBUTE_CERTIFICATE
-- PT_MV_BINARY
-"6802":
-- PR_USENET_SITE_NAME
-- PT_UNICODE
-3ff5:
-- PR_STORAGE_QUOTA_LIMIT
-- PT_LONG
-"8083":
-- PR_EMS_AB_DXA_IMP_SEQ
-- PT_TSTRING
-804a:
-- PR_EMS_AB_ADMIN_DESCRIPTION
-- PT_TSTRING
-80ce:
-- PR_EMS_AB_MAPI_ID
-- PT_LONG
-817c:
-- PR_EMS_AB_SCHEMA_VERSION
-- PT_MV_LONG
-8c46:
-- PR_EMS_AB_DELTA_REVOCATION_LIST
-- PT_MV_BINARY
-"6803":
-- PR_MAILBEAT_REQUEST_RECEIVED
-- PT_SYSTIME
-3ff6:
-- PR_EXCESS_STORAGE_USED
-- PT_LONG
-"3700":
-- PR_ATTACHMENT_X400_PARAMETERS
-- PT_BINARY
-"8084":
-- PR_EMS_AB_DXA_IMP_SEQ_TIME
-- PT_SYSTIME
-0e00:
-- PR_CURRENT_VERSION
-- PT_I8
-804b:
-- PR_EMS_AB_ADMIN_DISPLAY_NAME
-- PT_TSTRING
-80cf:
-- PR_EMS_AB_MDB_BACKOFF_INTERVAL
-- PT_LONG
-817d:
-- PR_EMS_AB_NNTP_CHARACTER_SET
-- PT_TSTRING
-8c47:
-- PR_EMS_AB_SECURITY_POLICY
-- PT_MV_BINARY
-"6804":
-- PR_MAILBEAT_REQUEST_PROCESSED
-- PT_SYSTIME
-3ff7:
-- PR_SVR_GENERATING_QUOTA_MSG
-- PT_TSTRING
-3d10:
-- PR_SERVICE_DELETE_FILES
-- PT_MV_TSTRING
-"8085":
-- PR_EMS_AB_DXA_IMP_SEQ_USN
-- PT_LONG
-0e01:
-- PR_DELETE_AFTER_SUBMIT
-- PT_BOOLEAN
-804c:
-- PR_EMS_AB_ADMIN_EXTENSION_DLL
-- PT_TSTRING
-817e:
-- PR_EMS_AB_USE_SERVER_VALUES
-- PT_BOOLEAN
-8c48:
-- PR_EMS_AB_SUPPORT_SMIME_SIGNATURES
-- PT_BOOLEAN
-"3701":
-- PR_ATTACH_DATA
-- PT_OBJECT|PT_BINARY
-3ff8:
-- PR_CREATOR_NAME
-- PT_TSTRING
-"3702":
-- PR_ATTACH_ENCODING
-- PT_BINARY
-3d11:
-- PR_AB_SEARCH_PATH_UPDATE
-- PT_BINARY
-"8086":
-- PR_EMS_AB_DXA_IMPORT_NOW
-- PT_BOOLEAN
-0e02:
-- PR_DISPLAY_BCC
-- PT_TSTRING
-3e0a:
-- PR_HEADER_FOLDER_ENTRYID
-- PT_BINARY
-804d:
-- PR_EMS_AB_ALIASED_OBJECT_NAME
-- PT_OBJECT|PT_MV_TSTRING
-817f:
-- PR_EMS_AB_ENABLED_PROTOCOLS
-- PT_LONG
-8c49:
-- PR_EMS_AB_DELEGATE_USER
-- PT_BOOLEAN
-"6806":
-- PR_MAILBEAT_REPLY_SENT
-- PT_SYSTIME
-3ff9:
-- PR_CREATOR_ENTRYID
-- PT_BINARY
-"3703":
-- PR_ATTACH_EXTENSION
-- PT_TSTRING
-3d12:
-- PR_PROFILE_NAME
-- PT_TSTRING
-"8087":
-- PR_EMS_AB_DXA_IN_TEMPLATE_MAP
-- PT_MV_TSTRING
-0e03:
-- PR_DISPLAY_CC
-- PT_TSTRING
-3e0b:
-- PR_REMOTE_PROGRESS
-- PT_LONG
-804e:
-- PR_EMS_AB_ALT_RECIPIENT
-- PT_OBJECT|PT_MV_TSTRING
-"6807":
-- PR_MAILBEAT_REPLY_SUBMIT
-- PT_SYSTIME
-"3704":
-- PR_ATTACH_FILENAME
-- PT_TSTRING
-"8088":
-- PR_EMS_AB_DXA_LOCAL_ADMIN
-- PT_OBJECT|PT_MV_TSTRING
-0e04:
-- PR_DISPLAY_TO
-- PT_TSTRING
-3e0c:
-- PR_REMOTE_PROGRESS_TEXT
-- PT_TSTRING
-804f:
-- PR_EMS_AB_ALT_RECIPIENT_BL
-- PT_OBJECT|PT_MV_TSTRING
-"6808":
-- PR_MAILBEAT_REPLY_RECEIVED
-- PT_SYSTIME
-"3705":
-- PR_ATTACH_METHOD
-- PT_LONG
-"8089":
-- PR_EMS_AB_DXA_LOGGING_LEVEL
-- PT_LONG
-0e05:
-- PR_PARENT_DISPLAY
-- PT_TSTRING
-3e0d:
-- PR_REMOTE_VALIDATE_OK
-- PT_BOOLEAN
-8c50:
-- PR_EMS_AB_LIST_PUBLIC_FOLDERS
-- PT_BOOLEAN
-"6809":
-- PR_MAILBEAT_REPLY_PROCESSED
-- PT_SYSTIME
-0e06:
-- PR_MESSAGE_DELIVERY_TIME
-- PT_SYSTIME
-8c51:
-- PR_EMS_AB_LABELEDURI
-- PT_TSTRING
-"3707":
-- PR_ATTACH_LONG_FILENAME
-- PT_TSTRING
-0e07:
-- PR_MESSAGE_FLAGS
-- PT_LONG
-80da:
-- PR_EMS_AB_MONITORED_SERVERS
-- PT_OBJECT|PT_MV_TSTRING
-8c1a:
-- PR_EMS_AB_GROUP_BY_ATTR_VALUE_DN
-- PT_OBJECT|PT_MV_TSTRING
-8c52:
-- PR_EMS_AB_RETURN_EXACT_MSG_SIZE
-- PT_BOOLEAN
-"3708":
-- PR_ATTACH_PATHNAME
-- PT_TSTRING
-"8090":
-- PR_EMS_AB_DXA_PREV_REMOTE_ENTRIES
-- PT_OBJECT|PT_MV_TSTRING
-80db:
-- PR_EMS_AB_MONITORED_SERVICES
-- PT_MV_TSTRING
-8c1b:
-- PR_EMS_AB_VIEW_DEFINITION
-- PT_MV_BINARY
-8c53:
-- PR_EMS_AB_GENERATION_QUALIFIER
-- PT_TSTRING
-3fca:
-- PR_CONFLICT_MSG_KEY
-- PT_BINARY
-0e08:
-- PR_MESSAGE_SIZE
-- PT_LONG|PT_I8
-"3709":
-- PR_ATTACH_RENDERING
-- PT_BINARY
-0e09:
-- PR_PARENT_ENTRYID
-- PT_BINARY
-"8091":
-- PR_EMS_AB_DXA_PREV_REPLICATION_SENSITIVITY
-- PT_LONG
-80dc:
-- PR_EMS_AB_MONITORING_ALERT_DELAY
-- PT_LONG
-818a:
-- PR_EMS_AB_CONTROL_MSG_RULES
-- PT_BINARY
-8c1c:
-- PR_EMS_AB_MIME_TYPES
-- PT_BINARY
-8c54:
-- PR_EMS_AB_HOUSE_IDENTIFIER
-- PT_TSTRING
-3f80:
-- PR_DID
-- PT_I8
-"8092":
-- PR_EMS_AB_DXA_PREV_TEMPLATE_OPTIONS
-- PT_LONG
-80dd:
-- PR_EMS_AB_MONITORING_ALERT_UNITS
-- PT_LONG
-818b:
-- PR_EMS_AB_AVAILABLE_DISTRIBUTIONS
-- PT_TSTRING
-8c1d:
-- PR_EMS_AB_LDAP_SEARCH_CFG
-- PT_LONG
-8c55:
-- PR_EMS_AB_SUPPORTED_ALGORITHMS
-- PT_BINARY
-3f81:
-- PR_SEQID
-- PT_I8
-805a:
-- PR_EMS_AB_CAN_CREATE_PF_DL
-- PT_OBJECT|PT_MV_TSTRING
-"8093":
-- PR_EMS_AB_DXA_PREV_TYPES
-- PT_LONG
-80de:
-- PR_EMS_AB_MONITORING_AVAILABILITY_STYLE
-- PT_LONG
-8c1e:
-- PR_EMS_AB_INBOUND_DN
-- PT_OBJECT|PT_MV_TSTRING
-8c56:
-- PR_EMS_AB_DMD_NAME
-- PT_TSTRING
-3f82:
-- PR_DRAFTID
-- PT_I8
-805b:
-- PR_EMS_AB_CAN_CREATE_PF_DL_BL
-- PT_OBJECT|PT_MV_TSTRING
-"3710":
-- PR_ATTACH_MIME_SEQUENCE
-- PT_LONG
-0e10:
-- PR_SPOOLER_STATUS
-- PT_LONG
-"8094":
-- PR_EMS_AB_DXA_RECIPIENT_CP
-- PT_TSTRING
-80df:
-- PR_EMS_AB_MONITORING_AVAILABILITY_WINDOW
-- PT_BINARY
-818d:
-- PR_EMS_AB_OUTBOUND_HOST
-- PT_BINARY
-8c57:
-- PR_EMS_AB_EXTENSION_ATTRIBUTE_11
-- PT_TSTRING
-3f83:
-- PR_CHECK_IN_TIME
-- PT_SYSTIME
-805c:
-- PR_EMS_AB_CAN_NOT_CREATE_PF
-- PT_OBJECT|PT_MV_TSTRING
-0e11:
-- PR_TRANSPORT_STATUS
-- PT_LONG
-"8095":
-- PR_EMS_AB_DXA_REMOTE_CLIENT
-- PT_OBJECT|PT_MV_TSTRING
-818e:
-- PR_EMS_AB_INBOUND_HOST
-- PT_MV_TSTRING
-8c1f:
-- PR_EMS_AB_INBOUND_NEWSFEED_TYPE
-- PT_BOOLEAN
-8c58:
-- PR_EMS_AB_EXTENSION_ATTRIBUTE_12
-- PT_TSTRING
-3f84:
-- PR_CHECK_IN_COMMENT
-- PT_UNICODE
-805d:
-- PR_EMS_AB_CAN_NOT_CREATE_PF_BL
-- PT_OBJECT|PT_MV_TSTRING
-"3712":
-- PR_ATTACH_CONTENT_ID
-- PT_TSTRING
-0e12:
-- PR_MESSAGE_RECIPIENTS
-- PT_OBJECT
-"8096":
-- PR_EMS_AB_DXA_REQ_SEQ
-- PT_TSTRING
-818f:
-- PR_EMS_AB_OUTGOING_MSG_SIZE_LIMIT
-- PT_LONG
-8c59:
-- PR_EMS_AB_EXTENSION_ATTRIBUTE_13
-- PT_TSTRING
-3d21:
-- PR_ADMIN_SECURITY_DESCRIPTOR
-- PT_BINARY
-3f85:
-- PR_VERSION_OP_CODE
-- PT_LONG
-805e:
-- PR_EMS_AB_CAN_NOT_CREATE_PF_DL
-- PT_OBJECT|PT_MV_TSTRING
-"3713":
-- PR_ATTACH_CONTENT_LOCATION
-- PT_TSTRING
-0e13:
-- PR_MESSAGE_ATTACHMENTS
-- PT_OBJECT
-"8097":
-- PR_EMS_AB_DXA_REQ_SEQ_TIME
-- PT_SYSTIME
-3f86:
-- PR_VERSION_OP_DATA
-- PT_BINARY
-805f:
-- PR_EMS_AB_CAN_NOT_CREATE_PF_DL_BL
-- PT_OBJECT|PT_MV_TSTRING
-"3714":
-- PR_ATTACH_FLAGS
-- PT_LONG
-0e14:
-- PR_SUBMIT_FLAGS
-- PT_LONG
-"8098":
-- PR_EMS_AB_DXA_REQ_SEQ_USN
-- PT_LONG
-3f87:
-- PR_VERSION_SEQUENCE_NUMBER
-- PT_LONG
-0e15:
-- PR_RECIPIENT_STATUS
-- PT_LONG
-"8099":
-- PR_EMS_AB_DXA_REQNAME
-- PT_TSTRING
-8c60:
-- PR_EMS_AB_EXTENSION_ATTRIBUTE_14
-- PT_TSTRING
-3f88:
-- PR_ATTACH_ID
-- PT_I8
-"6001":
-- PR_DOTSTUFF_STATE
-- PT_LONG
-0e16:
-- PR_TRANSPORT_KEY
-- PT_LONG
-8c61:
-- PR_EMS_AB_EXTENSION_ATTRIBUTE_15
-- PT_TSTRING
-"3716":
-- PR_ATTACH_CONTENT_DISPOSITION
-- PT_UNICODE
-0e17:
-- PR_MSG_STATUS
-- PT_LONG
-80ea:
-- PR_EMS_AB_MONITORING_RECIPIENTS_NDR
-- PT_OBJECT|PT_MV_TSTRING
-8c62:
-- PR_EMS_AB_REPLICATED_OBJECT_VERSION
-- PT_LONG
-3fda:
-- PR_ABSTRACT
-- PT_TSTRING
-0e18:
-- PR_MESSAGE_DOWNLOAD_TIME
-- PT_LONG
-80eb:
-- PR_EMS_AB_MONITORING_RPC_UPDATE_INTERVAL
-- PT_LONG
-8c63:
-- PR_EMS_AB_MAIL_DROP
-- PT_TSTRING
-3fdb:
-- PR_DL_REPORT_FLAGS
-- PT_LONG
-0e19:
-- PR_CREATION_VERSION
-- PT_I8
-80ec:
-- PR_EMS_AB_MONITORING_RPC_UPDATE_UNITS
-- PT_LONG
-819a:
-- PR_EMS_AB_AUTHENTICATION_TO_USE
-- PT_TSTRING
-8c64:
-- PR_EMS_AB_FORWARDING_ADDRESS
-- PT_TSTRING
-3f90:
-- PR_VERSIONING_FLAGS
-- PT_SHORT
-3fdc:
-- PR_BILATERAL_INFO
-- PT_BINARY
-80ed:
-- PR_EMS_AB_MONITORING_WARNING_DELAY
-- PT_LONG
-819b:
-- PR_EMS_AB_HTTP_PUB_GAL
-- PT_BOOLEAN
-8c65:
-- PR_EMS_AB_FORM_DATA
-- PT_BINARY
-3f91:
-- PR_PKM_LAST_UNAPPROVED_VID
-- PT_BINARY
-3fdd:
-- PR_MSG_BODY_ID
-- PT_LONG
-806a:
-- PR_EMS_AB_DELIV_CONT_LENGTH
-- PT_LONG
-80ee:
-- PR_EMS_AB_MONITORING_WARNING_UNITS
-- PT_LONG
-819c:
-- PR_EMS_AB_HTTP_PUB_GAL_LIMIT
-- PT_LONG
-8c66:
-- PR_EMS_AB_OWA_SERVER
-- PT_TSTRING
-3f92:
-- PR_MV_PKM_VERSION_LABELS
-- PT_MV_UNICODE
-0e20:
-- PR_ATTACH_SIZE
-- PT_LONG
-3fde:
-- PR_INTERNET_CPID
-- PT_LONG
-806b:
-- PR_EMS_AB_DELIV_EITS
-- PT_MV_BINARY
-80ef:
-- PR_EMS_AB_MTA_LOCAL_CRED
-- PT_TSTRING
-8c67:
-- PR_EMS_AB_EMPLOYEE_NUMBER
-- PT_TSTRING
-3f93:
-- PR_MV_PKM_VERSION_STATUS
-- PT_MV_UNICODE
-0e21:
-- PR_ATTACH_NUM
-- PT_LONG
-3fdf:
-- PR_AUTO_RESPONSE_SUPPRESS
-- PT_LONG
-806c:
-- PR_EMS_AB_DELIV_EXT_CONT_TYPES
-- PT_MV_BINARY
-819e:
-- PR_EMS_AB_HTTP_PUB_PF
-- PT_MV_BINARY
-8c68:
-- PR_EMS_AB_TELEPHONE_PERSONAL_PAGER
-- PT_TSTRING
-3f94:
-- PR_PKM_INTERNAL_DATA
-- PT_BINARY
-0e22:
-- PR_PREPROCESS
-- PT_BOOLEAN
-806d:
-- PR_EMS_AB_DELIVER_AND_REDIRECT
-- PT_BOOLEAN
-8c69:
-- PR_EMS_AB_EMPLOYEE_TYPE
-- PT_TSTRING
-806e:
-- PR_EMS_AB_DELIVERY_MECHANISM
-- PT_LONG
-0e23:
-- PR_INTERNET_ARTICLE_NUMBER
-- PT_LONG
-806f:
-- PR_EMS_AB_DESCRIPTION
-- PT_MV_TSTRING
-0e24:
-- PR_NEWSGROUP_NAME
-- PT_TSTRING
-0e25:
-- PR_ORIGINATING_MTA_CERTIFICATE
-- PT_BINARY
-0e26:
-- PR_PROOF_OF_SUBMISSION
-- PT_BINARY
-80fa:
-- PR_EMS_AB_OFF_LINE_AB_SERVER
-- PT_OBJECT|PT_MV_TSTRING
-8c3a:
-- PR_EMS_AB_DO_OAB_VERSION
-- PT_LONG
-0e27:
-- PR_NT_SECURITY_DESCRIPTOR
-- PT_BINARY
-3fea:
-- PR_HAS_DAMS
-- PT_BOOLEAN
-80fb:
-- PR_EMS_AB_OFF_LINE_AB_STYLE
-- PT_LONG
-8c3b:
-- PR_EMS_AB_VOICE_MAIL_SYSTEM_GUID
-- PT_BINARY
-"0001":
-- PR_ACKNOWLEDGEMENT_MODE
-- PT_LONG
-300a:
-- PR_PROVIDER_DLL_NAME
-- PT_TSTRING
-3feb:
-- PR_DEFERRED_SEND_NUMBER
-- PT_LONG
-80fc:
-- PR_EMS_AB_OID_TYPE
-- PT_LONG
-8c3c:
-- PR_EMS_AB_VOICE_MAIL_USER_ID
-- PT_TSTRING
-"0002":
-- PR_ALTERNATE_RECIPIENT_ALLOWED
-- PT_BOOLEAN
-300b:
-- PR_SEARCH_KEY
-- PT_BINARY
-3fec:
-- PR_DEFERRED_SEND_UNITS
-- PT_LONG
-80fd:
-- PR_EMS_AB_OM_OBJECT_CLASS
-- PT_BINARY
-8c3d:
-- PR_EMS_AB_VOICE_MAIL_PASSWORD
-- PT_TSTRING
-"0003":
-- PR_AUTHORIZING_USERS
-- PT_BINARY
-807a:
-- PR_EMS_AB_DXA_ADMIN_UPDATE
-- PT_LONG
-"6701":
-- PR_PST_REMEMBER_PW
-- PT_BOOLEAN
-300c:
-- PR_PROVIDER_UID
-- PT_BINARY
-3fed:
-- PR_EXPIRY_NUMBER
-- PT_LONG
-80fe:
-- PR_EMS_AB_OM_SYNTAX
-- PT_LONG
-8c3e:
-- PR_EMS_AB_VOICE_MAIL_RECORDED_NAME
-- PT_BINARY
-"0004":
-- PR_AUTO_FORWARD_COMMENT
-- PT_TSTRING
-807b:
-- PR_EMS_AB_DXA_APPEND_REQCN
-- PT_BOOLEAN
-300d:
-- PR_PROVIDER_ORDINAL
-- PT_LONG
-3fee:
-- PR_EXPIRY_UNITS
-- PT_LONG
-80ff:
-- PR_EMS_AB_OOF_REPLY_TO_ORIGINATOR
-- PT_BOOLEAN
-8c3f:
-- PR_EMS_AB_VOICE_MAIL_GREETINGS
-- PT_MV_TSTRING
-"6702":
-- PR_OST_ENCRYPTION
-- PT_LONG
-3fef:
-- PR_DEFERRED_SEND_TIME
-- PT_SYSTIME
-"0005":
-- PR_AUTO_FORWARDED
-- PT_BOOLEAN
-807c:
-- PR_EMS_AB_DXA_CONF_CONTAINER_LIST
-- PT_OBJECT|PT_MV_TSTRING
-"6703":
-- PR_PST_PW_SZ_OLD
-- PT_TSTRING
-"0006":
-- PR_CONTENT_CONFIDENTIALITY_ALGORITHM_ID
-- PT_BINARY
-807d:
-- PR_EMS_AB_DXA_CONF_REQ_TIME
-- PT_SYSTIME
-"6704":
-- PR_PST_PW_SZ_NEW
-- PT_TSTRING
-"3600":
-- PR_CONTAINER_FLAGS
-- PT_LONG
-"0007":
-- PR_CONTENT_CORRELATOR
-- PT_BINARY
-807e:
-- PR_EMS_AB_DXA_CONF_SEQ
-- PT_TSTRING
-"6705":
-- PR_SORT_LOCALE_ID
-- PT_LONG
-"3601":
-- PR_FOLDER_TYPE
-- PT_LONG
-0008:
-- PR_CONTENT_IDENTIFIER
-- PT_TSTRING
-3d0a:
-- PR_SERVICE_DLL_NAME
-- PT_TSTRING
-807f:
-- PR_EMS_AB_DXA_CONF_SEQ_USN
-- PT_LONG
-"3602":
-- PR_CONTENT_COUNT
-- PT_LONG
-0009:
-- PR_CONTENT_LENGTH
-- PT_LONG
-3d0b:
-- PR_SERVICE_ENTRY_NAME
-- PT_TSTRING
-"3603":
-- PR_CONTENT_UNREAD
-- PT_LONG
-"6707":
-- PR_URL_NAME
-- PT_UNICODE
-3d0c:
-- PR_SERVICE_UID
-- PT_BINARY
-"3604":
-- PR_CREATE_TEMPLATES
-- PT_OBJECT
-3d0d:
-- PR_SERVICE_EXTRA_UIDS
-- PT_BINARY
-"0010":
-- PR_DELIVER_TIME
-- PT_SYSTIME
-"3605":
-- PR_DETAILS_TABLE
-- PT_OBJECT
-"6709":
-- PR_LOCAL_COMMIT_TIME
-- PT_SYSTIME
-3d0e:
-- PR_SERVICES
-- PT_BINARY
-3ffa:
-- PR_LAST_MODIFIER_NAME
-- PT_TSTRING
-f000:
-- PR_EMS_AB_OTHER_RECIPS
-- PT_OBJECT
-3d0f:
-- PR_SERVICE_SUPPORT_FILES
-- PT_MV_TSTRING
-3ffb:
-- PR_LAST_MODIFIER_ENTRYID
-- PT_BINARY
-"0011":
-- PR_DISCARD_REASON
-- PT_LONG
-"3607":
-- PR_SEARCH
-- PT_OBJECT
-3ffc:
-- PR_REPLY_RECIPIENT_SMTP_PROXIES
-- PT_TSTRING
-"0012":
-- PR_DISCLOSURE_OF_RECIPIENTS
-- PT_BOOLEAN
-"6710":
-- PR_URL_COMP_NAME_HASH
-- PT_LONG
-3ffd:
-- PR_MESSAGE_CODEPAGE
-- PT_LONG
-"0013":
-- PR_DL_EXPANSION_HISTORY
-- PT_BINARY
-"3609":
-- PR_SELECTABLE
-- PT_BOOLEAN
-808a:
-- PR_EMS_AB_DXA_NATIVE_ADDRESS_TYPE
-- PT_TSTRING
-0ff4:
-- PR_ACCESS
-- PT_LONG
-"6711":
-- PR_MSG_FOLDER_TEMPLATE_RES_2
-- PT_LONG
-3ffe:
-- PR_EXTENDED_ACL_DATA
-- PT_BINARY
-"0014":
-- PR_DL_EXPANSION_PROHIBITED
-- PT_BOOLEAN
-808b:
-- PR_EMS_AB_DXA_OUT_TEMPLATE_MAP
-- PT_MV_TSTRING
-0ff5:
-- PR_ROW_TYPE
-- PT_LONG
-"6712":
-- PR_RANK
-- PT_LONG
-"6844":
-- PR_DELEGATES_DISPLAY_NAMES
-- PT_MV_UNICODE
-"0015":
-- PR_EXPIRY_TIME
-- PT_SYSTIME
-808c:
-- PR_EMS_AB_DXA_PASSWORD
-- PT_TSTRING
-0ff6:
-- PR_INSTANCE_KEY
-- PT_BINARY
-3fff:
-- PR_FROM_I_HAVE
-- PT_BOOLEAN
-"6713":
-- PR_MSG_FOLDER_TEMPLATE_RES_4
-- PT_BOOLEAN
-"6845":
-- PR_DELEGATES_ENTRYIDS
-- PT_MV_BINARY
-"0016":
-- PR_IMPLICIT_CONVERSION_PROHIBITED
-- PT_BOOLEAN
-370a:
-- PR_ATTACH_TAG
-- PT_BINARY
-808d:
-- PR_EMS_AB_DXA_PREV_EXCHANGE_OPTIONS
-- PT_LONG
-"3610":
-- PR_FOLDER_ASSOCIATED_CONTENTS
-- PT_OBJECT
-0ff7:
-- PR_ACCESS_LEVEL
-- PT_LONG
-"6714":
-- PR_MSG_FOLDER_TEMPLATE_RES_5
-- PT_BOOLEAN
-"0017":
-- PR_IMPORTANCE
-- PT_LONG
-370b:
-- PR_RENDERING_POSITION
-- PT_LONG
-808e:
-- PR_EMS_AB_DXA_PREV_EXPORT_NATIVE_ONLY
-- PT_BOOLEAN
-0e0a:
-- PR_SENTMAIL_ENTRYID
-- PT_BINARY
-"3611":
-- PR_DEF_CREATE_DL
-- PT_BINARY
-0ff8:
-- PR_MAPPING_SIGNATURE
-- PT_BINARY
-"6715":
-- PR_MSG_FOLDER_TEMPLATE_RES_6
-- PT_BOOLEAN
-"6847":
-- PR_FREEBUSY_START_RANGE
-- PT_LONG
-0018:
-- PR_IPM_ID
-- PT_BINARY
-370c:
-- PR_ATTACH_TRANSPORT_NAME
-- PT_TSTRING
-808f:
-- PR_EMS_AB_DXA_PREV_IN_EXCHANGE_SENSITIVITY
-- PT_LONG
-"3612":
-- PR_DEF_CREATE_MAILUSER
-- PT_BINARY
-0ff9:
-- PR_RECORD_KEY
-- PT_BINARY
-"6716":
-- PR_MSG_FOLDER_TEMPLATE_RES_7
-- PT_BINARY
-"6848":
-- PR_FREEBUSY_END_RANGE
-- PT_LONG
-0019:
-- PR_LATEST_DELIVERY_TIME
-- PT_SYSTIME
-370d:
-- PR_ATTACH_LONG_PATHNAME
-- PT_TSTRING
-0e0c:
-- PR_CORRELATE
-- PT_BOOLEAN
-"3613":
-- PR_CONTAINER_CLASS
-- PT_TSTRING
-"6717":
-- PR_MSG_FOLDER_TEMPLATE_RES_8
-- PT_BINARY
-"6849":
-- PR_FREEBUSY_EMAIL_ADDRESS
-- PT_UNICODE
-370e:
-- PR_ATTACH_MIME_TAG
-- PT_TSTRING
-0e0d:
-- PR_CORRELATE_MTSID
-- PT_BINARY
-"3614":
-- PR_CONTAINER_MODIFY_VERSION
-- PT_I8
-"6718":
-- PR_MSG_FOLDER_TEMPLATE_RES_9
-- PT_BINARY
-370f:
-- PR_ATTACH_ADDITIONAL_INFO
-- PT_BINARY
-0e0e:
-- PR_DISCRETE_VALUES
-- PT_BOOLEAN
-"0020":
-- PR_ORIGINALLY_INTENDED_RECIPIENT_NAME
-- PT_BINARY
-"3615":
-- PR_AB_PROVIDER_ID
-- PT_BINARY
-"6719":
-- PR_MSG_FOLDER_TEMPLATE_RES_10
-- PT_UNICODE
-"6850":
-- PR_FREEBUSY_ALL_EVENTS
-- PT_MV_BINARY
-0e0f:
-- PR_RESPONSIBILITY
-- PT_BOOLEAN
-"0021":
-- PR_ORIGINAL_EITS
-- PT_BINARY
-"3616":
-- PR_DEFAULT_VIEW_ENTRYID
-- PT_BINARY
-"6851":
-- PR_FREEBUSY_TENTATIVE_MONTHS
-- PT_MV_LONG
-"3617":
-- PR_ASSOC_CONTENT_COUNT
-- PT_LONG
-"3880":
-- PR_SYNCEVENT_SUPPRESS_GUID
-- PT_BINARY
-"6720":
-- PR_INTERNET_FREE_DOC_INFO
-- PT_BINARY
-"6852":
-- PR_FREEBUSY_TENTATIVE_EVENTS
-- PT_MV_BINARY
-"0022":
-- PR_ORIGINATOR_CERTIFICATE
-- PT_BINARY
-"0023":
-- PR_ORIGINATOR_DELIVERY_REPORT_REQUESTED
-- PT_BOOLEAN
-809a:
-- PR_EMS_AB_DXA_SVR_SEQ
-- PT_TSTRING
-39fe:
-- PR_SMTP_ADDRESS
-- PT_UNICODE
-"6721":
-- PR_PF_OVER_HARD_QUOTA_LIMIT
-- PT_LONG
-"6853":
-- PR_FREEBUSY_BUSY_MONTHS
-- PT_MV_LONG
-"0024":
-- PR_ORIGINATOR_RETURN_ADDRESS
-- PT_BINARY
-809b:
-- PR_EMS_AB_DXA_SVR_SEQ_TIME
-- PT_SYSTIME
-"6722":
-- PR_PF_MSG_SIZE_LIMIT
-- PT_LONG
-"6854":
-- PR_FREEBUSY_BUSY_EVENTS
-- PT_MV_BINARY
-39ff:
-- PR_7BIT_DISPLAY_NAME
-- PT_TSTRING
-"0025":
-- PR_PARENT_KEY
-- PT_BINARY
-809c:
-- PR_EMS_AB_DXA_SVR_SEQ_USN
-- PT_LONG
-"6855":
-- PR_FREEBUSY_OOF_MONTHS
-- PT_MV_LONG
-"0026":
-- PR_PRIORITY
-- PT_LONG
-809d:
-- PR_EMS_AB_DXA_TASK
-- PT_LONG
-3f8d:
-- PR_PKM_DOC_STATUS
-- PT_UNICODE
-"6856":
-- PR_FREEBUSY_OOF_EVENTS
-- PT_MV_BINARY
-0e1a:
-- PR_MODIFY_VERSION
-- PT_I8
-"0027":
-- PR_ORIGIN_CHECK
-- PT_BINARY
-809e:
-- PR_EMS_AB_DXA_TEMPLATE_OPTIONS
-- PT_LONG
-fff8:
-- PR_EMS_AB_CHILD_RDNS
-- PT_MV_TSTRING
-0e1b:
-- PR_HASATTACH
-- PT_BOOLEAN
-0028:
-- PR_PROOF_OF_SUBMISSION_REQUESTED
-- PT_BOOLEAN
-809f:
-- PR_EMS_AB_DXA_TEMPLATE_TIMESTAMP
-- PT_SYSTIME
-fff9:
-- PR_EMS_AB_HIERARCHY_PATH
-- PT_TSTRING
-3f8e:
-- PR_MV_PKM_OPERATION_REQ
-- PT_MV_UNICODE
-0e1c:
-- PR_BODY_CRC
-- PT_LONG
-0029:
-- PR_READ_RECEIPT_REQUESTED
-- PT_BOOLEAN
-3f8f:
-- PR_PKM_DOC_INTERNAL_STATE
-- PT_UNICODE
-0e1d:
-- PR_NORMALIZED_SUBJECT
-- PT_TSTRING
-"0030":
-- PR_REPLY_TIME
-- PT_SYSTIME
-8c6a:
-- PR_EMS_AB_TAGGED_X509_CERT
-- PT_MV_BINARY
-0e1f:
-- PR_RTF_IN_SYNC
-- PT_BOOLEAN
-"0031":
-- PR_REPORT_TAG
-- PT_BINARY
-8c6b:
-- PR_EMS_AB_PERSONAL_TITLE
-- PT_TSTRING
-0e58:
-- PR_CREATOR_SID
-- PT_BINARY
-"0032":
-- PR_REPORT_TIME
-- PT_SYSTIME
-8c6c:
-- PR_EMS_AB_LANGUAGE_ISO639
-- PT_TSTRING
-0e59:
-- PR_LAST_MODIFIER_SID
-- PT_BINARY
-"0033":
-- PR_RETURNED_IPM
-- PT_BOOLEAN
-"0034":
-- PR_SECURITY
-- PT_LONG
-"0035":
-- PR_INCOMPLETE_COPY
-- PT_BOOLEAN
-0e61:
-- PR_URL_COMP_NAME_POSTFIX
-- PT_LONG
-"0036":
-- PR_SENSITIVITY
-- PT_LONG
-"6602":
-- PR_PROFILE_HOME_SERVER
-- PT_TSTRING
-0e62:
-- PR_URL_COMP_NAME_SET
-- PT_BOOLEAN
-"0037":
-- PR_SUBJECT
-- PT_TSTRING
-0e63:
-- PR_SUBFOLDER_CT
-- PT_LONG
-0038:
-- PR_SUBJECT_IPM
-- PT_BINARY
-0c00:
-- PR_CONTENT_INTEGRITY_CHECK
-- PT_BINARY
-0e64:
-- PR_DELETED_SUBFOLDER_CT
-- PT_LONG
-"6868":
-- PR_FREEBUSY_LAST_MODIFIED
-- PT_SYSTIME
-0039:
-- PR_CLIENT_SUBMIT_TIME
-- PT_SYSTIME
-0c01:
-- PR_EXPLICIT_CONVERSION
-- PT_LONG
-"6869":
-- PR_FREEBUSY_NUM_MONTHS
-- PT_LONG
-0c02:
-- PR_IPM_RETURN_REQUESTED
-- PT_BOOLEAN
-0e66:
-- PR_DELETE_TIME
-- PT_SYSTIME
-"0040":
-- PR_RECEIVED_BY_NAME
-- PT_TSTRING
-"6607":
-- PR_PROFILE_UNRESOLVED_NAME
-- PT_TSTRING
-0c03:
-- PR_MESSAGE_TOKEN
-- PT_BINARY
-0e67:
-- PR_AGE_LIMIT
-- PT_BINARY
-"0041":
-- PR_SENT_REPRESENTING_ENTRYID
-- PT_BINARY
-"6608":
-- PR_PROFILE_UNRESOLVED_SERVER
-- PT_TSTRING
-0c04:
-- PR_NDR_REASON_CODE
-- PT_LONG
-"0042":
-- PR_SENT_REPRESENTING_NAME
-- PT_TSTRING
-0c05:
-- PR_NDR_DIAG_CODE
-- PT_LONG
-000a:
-- PR_CONTENT_RETURN_REQUESTED
-- PT_BOOLEAN
-"0043":
-- PR_RCVD_REPRESENTING_ENTRYID
-- PT_BINARY
-0c06:
-- PR_NON_RECEIPT_NOTIFICATION_REQUESTED
-- PT_BOOLEAN
-"6610":
-- PR_PROFILE_OFFLINE_STORE_PATH
-- PT_TSTRING
-000b:
-- PR_CONVERSATION_KEY
-- PT_BINARY
-0c07:
-- PR_DELIVERY_POINT
-- PT_LONG
-000c:
-- PR_CONVERSION_EITS
-- PT_BINARY
-"0044":
-- PR_RCVD_REPRESENTING_NAME
-- PT_TSTRING
-0c08:
-- PR_ORIGINATOR_NON_DELIVERY_REPORT_REQUESTED
-- PT_BOOLEAN
-670a:
-- PR_LOCAL_COMMIT_TIME_MAX
-- PT_SYSTIME
-"6611":
-- PR_PROFILE_OFFLINE_INFO
-- PT_BINARY
-000d:
-- PR_CONVERSION_WITH_LOSS_PROHIBITED
-- PT_BOOLEAN
-"0045":
-- PR_REPORT_ENTRYID
-- PT_BINARY
-0c09:
-- PR_ORIGINATOR_REQUESTED_ALTERNATE_RECIPIENT
-- PT_BINARY
-670b:
-- PR_DELETED_COUNT_TOTAL
-- PT_LONG
-"6743":
-- PR_CONNECTION_MODULUS
-- PT_LONG
-"6612":
-- PR_PROFILE_HOME_SERVER_DN
-- PT_TSTRING
-000e:
-- PR_CONVERTED_EITS
-- PT_BINARY
-"0046":
-- PR_READ_RECEIPT_ENTRYID
-- PT_BINARY
-670c:
-- PR_AUTO_RESET
-- PT_CLSID
-"6744":
-- PR_DELIVER_TO_DN
-- PT_UNICODE
-"6613":
-- PR_PROFILE_HOME_SERVER_ADDRS
-- PT_MV_TSTRING
-000f:
-- PR_DEFERRED_DELIVERY_TIME
-- PT_SYSTIME
-"0047":
-- PR_MESSAGE_SUBMISSION_ID
-- PT_BINARY
-"6614":
-- PR_PROFILE_SERVER_DN
-- PT_TSTRING
-0c10:
-- PR_PHYSICAL_RENDITION_ATTRIBUTES
-- PT_BINARY
-0048:
-- PR_PROVIDER_SUBMIT_TIME
-- PT_SYSTIME
-360a:
-- PR_SUBFOLDERS
-- PT_BOOLEAN
-"6746":
-- PR_MIME_SIZE
-- PT_LONG
-"6615":
-- PR_PROFILE_FAVFLD_COMMENT
-- PT_TSTRING
-0c11:
-- PR_PROOF_OF_DELIVERY
-- PT_BINARY
-0049:
-- PR_ORIGINAL_SUBJECT
-- PT_TSTRING
-360b:
-- PR_STATUS
-- PT_LONG
-"6747":
-- PR_FILE_SIZE
-- PT_I8
-"6616":
-- PR_PROFILE_ALLPUB_DISPLAY_NAME
-- PT_TSTRING
-0c12:
-- PR_PROOF_OF_DELIVERY_REQUESTED
-- PT_BOOLEAN
-360c:
-- PR_ANR
-- PT_TSTRING
-"6748":
-- PR_FID
-- PT_I8
-"0050":
-- PR_REPLY_RECIPIENT_NAMES
-- PT_TSTRING
-"6617":
-- PR_PROFILE_ALLPUB_COMMENT
-- PT_TSTRING
-0c13:
-- PR_RECIPIENT_CERTIFICATE
-- PT_BINARY
-360d:
-- PR_CONTENTS_SORT_ORDER
-- PT_MV_LONG
-"6749":
-- PR_PARENT_FID
-- PT_I8
-"0051":
-- PR_RECEIVED_BY_SEARCH_KEY
-- PT_BINARY
-0c14:
-- PR_RECIPIENT_NUMBER_FOR_ADVICE
-- PT_TSTRING
-66a0:
-- PR_NT_USER_NAME
-- PT_TSTRING
-360e:
-- PR_CONTAINER_HIERARCHY
-- PT_OBJECT
-"0052":
-- PR_RCVD_REPRESENTING_SEARCH_KEY
-- PT_BINARY
-0ffa:
-- PR_STORE_RECORD_KEY
-- PT_BINARY
-0c15:
-- PR_RECIPIENT_TYPE
-- PT_LONG
-66a1:
-- PR_LOCALE_ID
-- PT_LONG
-360f:
-- PR_CONTAINER_CONTENTS
-- PT_OBJECT
-0e79:
-- PR_TRUST_SENDER
-- PT_LONG
-"6750":
-- PR_ICS_NOTIF
-- PT_LONG
-"0053":
-- PR_READ_RECEIPT_SEARCH_KEY
-- PT_BINARY
-0ffb:
-- PR_STORE_ENTRYID
-- PT_BINARY
-0c16:
-- PR_REGISTERED_MAIL_TYPE
-- PT_LONG
-66a2:
-- PR_LAST_LOGON_TIME
-- PT_SYSTIME
-"1100":
-- PR_P1_CONTENT
-- PT_BINARY
-001a:
-- PR_MESSAGE_CLASS
-- PT_TSTRING
-36d0:
-- PR_IPM_APPOINTMENT_ENTRYID
-- PT_BINARY
-"6751":
-- PR_ARTICLE_NUM_NEXT
-- PT_LONG
-"0054":
-- PR_REPORT_SEARCH_KEY
-- PT_BINARY
-0ffc:
-- PR_MINI_ICON
-- PT_BINARY
-0c17:
-- PR_REPLY_REQUESTED
-- PT_BOOLEAN
-66a3:
-- PR_LAST_LOGOFF_TIME
-- PT_SYSTIME
-"1101":
-- PR_P1_CONTENT_TYPE
-- PT_BINARY
-001b:
-- PR_MESSAGE_DELIVERY_ID
-- PT_BINARY
-36d1:
-- PR_IPM_CONTACT_ENTRYID
-- PT_BINARY
-"6752":
-- PR_IMAP_LAST_ARTICLE_ID
-- PT_LONG
-0ffd:
-- PR_ICON
-- PT_BINARY
-0c18:
-- PR_REQUESTED_DELIVERY_METHOD
-- PT_LONG
-66a4:
-- PR_STORAGE_LIMIT_INFORMATION
-- PT_LONG
-36d2:
-- PR_IPM_JOURNAL_ENTRYID
-- PT_BINARY
-671a:
-- PR_MSG_FOLDER_TEMPLATE_RES_11
-- PT_UNICODE
-"6753":
-- PR_NOT_822_RENDERABLE
-- PT_BOOLEAN
-"0055":
-- PR_ORIGINAL_DELIVERY_TIME
-- PT_SYSTIME
-0ffe:
-- PR_OBJECT_TYPE
-- PT_LONG
-0c19:
-- PR_SENDER_ENTRYID
-- PT_BINARY
-66a5:
-- PR_NEWSGROUP_COMPONENT
-- PT_TSTRING
-36d3:
-- PR_IPM_NOTE_ENTRYID
-- PT_BINARY
-671b:
-- PR_MSG_FOLDER_TEMPLATE_RES_12
-- PT_UNICODE
-"0056":
-- PR_ORIGINAL_AUTHOR_SEARCH_KEY
-- PT_BINARY
-66a6:
-- PR_NEWSFEED_INFO
-- PT_BINARY
-"1000":
-- PR_BODY
-- PT_TSTRING
-001e:
-- PR_MESSAGE_SECURITY_LABEL
-- PT_BINARY
-36d4:
-- PR_IPM_TASK_ENTRYID
-- PT_BINARY
-0fff:
-- PR_ENTRYID
-- PT_BINARY
-"0057":
-- PR_MESSAGE_TO_ME
-- PT_BOOLEAN
-66a7:
-- PR_INTERNET_NEWSGROUP_NAME
-- PT_TSTRING
-001f:
-- PR_OBSOLETED_IPMS
-- PT_BINARY
-36d5:
-- PR_REMINDERS_ONLINE_ENTRYID
-- PT_BINARY
-684f:
-- PR_FREEBUSY_ALL_MONTHS
-- PT_MV_LONG
-0058:
-- PR_MESSAGE_CC_ME
-- PT_BOOLEAN
-66a8:
-- PR_FOLDER_FLAGS
-- PT_LONG
-"1001":
-- PR_REPORT_TEXT
-- PT_TSTRING
-36d6:
-- PR_REMINDERS_OFFLINE_ENTRYID
-- PT_BINARY
-671e:
-- PR_PF_PLATINUM_HOME_MDB
-- PT_BOOLEAN
-0059:
-- PR_MESSAGE_RECIP_ME
-- PT_BOOLEAN
-66a9:
-- PR_LAST_ACCESS_TIME
-- PT_SYSTIME
-"1002":
-- PR_ORIGINATOR_AND_DL_EXPANSION_HISTORY
-- PT_BINARY
-36d7:
-- PR_IPM_DRAFTS_ENTRYID
-- PT_BINARY
-671f:
-- PR_PF_PROXY_REQUIRED
-- PT_BOOLEAN
-"6626":
-- PR_ADDRBOOK_FOR_LOCAL_SITE_ENTRYID
-- PT_BINARY
-"1003":
-- PR_REPORTING_DL_NAME
-- PT_BINARY
-361c:
-- PR_PACKED_NAME_PROPS
-- PT_BINARY
-36d8:
-- PR_OUTLOOK_2003_ENTRYIDS
-- PT_MV_BINARY
-"6758":
-- PR_LTID
-- PT_BINARY
-"0060":
-- PR_START_DATE
-- PT_SYSTIME
-"6627":
-- PR_OFFLINE_MESSAGE_ENTRYID
-- PT_BINARY
-3a00:
-- PR_ACCOUNT
-- PT_TSTRING
-"1004":
-- PR_REPORTING_MTA_CERTIFICATE
-- PT_BINARY
-"6759":
-- PR_CN_EXPORT
-- PT_BINARY
diff --git a/vendor/ruby-msg/data/named_map.yaml b/vendor/ruby-msg/data/named_map.yaml
deleted file mode 100644
index 8de27d13f..000000000
--- a/vendor/ruby-msg/data/named_map.yaml
+++ /dev/null
@@ -1,114 +0,0 @@
-# this file provides for the mapping of the keys of named properties
-# to symbolic names (as opposed to mapitags.yaml, which is currently
-# in a different format, has a different source, and is only fixed
-# code properties)
-#
-# essentially the symbols are slightly munged versions of the names
-# given to these properties by CDO, or Outlook's object model.
-# it was parsed out of cdo10.htm, and neatened up a bit.
-#
-# interestingly, despite having separate guids, the codes are picked not to
-# clash. further the names themselves have only 3 clashes in all the below.
-{
-[0x8005, PSETID_Address]: file_under,
-[0x8017, PSETID_Address]: last_name_and_first_name,
-[0x8018, PSETID_Address]: company_and_full_name,
-[0x8019, PSETID_Address]: full_name_and_company,
-[0x801a, PSETID_Address]: home_address,
-[0x801b, PSETID_Address]: business_address,
-[0x801c, PSETID_Address]: other_address,
-[0x8022, PSETID_Address]: selected_address,
-[0x802b, PSETID_Address]: web_page,
-[0x802c, PSETID_Address]: yomi_first_name,
-[0x802d, PSETID_Address]: yomi_last_name,
-[0x802e, PSETID_Address]: yomi_company_name,
-[0x8030, PSETID_Address]: last_first_no_space,
-[0x8031, PSETID_Address]: last_first_space_only,
-[0x8032, PSETID_Address]: company_last_first_no_space,
-[0x8033, PSETID_Address]: company_last_first_space_only,
-[0x8034, PSETID_Address]: last_first_no_space_company,
-[0x8035, PSETID_Address]: last_first_space_only_company,
-[0x8036, PSETID_Address]: last_first_and_suffix,
-[0x8045, PSETID_Address]: business_address_street,
-[0x8046, PSETID_Address]: business_address_city,
-[0x8047, PSETID_Address]: business_address_state,
-[0x8048, PSETID_Address]: business_address_postal_code,
-[0x8049, PSETID_Address]: business_address_country,
-[0x804a, PSETID_Address]: business_address_post_office_box,
-[0x804f, PSETID_Address]: user_field1,
-[0x8050, PSETID_Address]: user_field2,
-[0x8051, PSETID_Address]: user_field3,
-[0x8052, PSETID_Address]: user_field4,
-[0x8062, PSETID_Address]: imaddress,
-[0x8082, PSETID_Address]: email_addr_type,
-[0x8083, PSETID_Address]: email_email_address,
-[0x8084, PSETID_Address]: email_original_display_name,
-[0x8085, PSETID_Address]: email_original_entry_id,
-[0x8092, PSETID_Address]: email2_addr_type,
-[0x8093, PSETID_Address]: email2_email_address,
-[0x8094, PSETID_Address]: email2_original_display_name,
-[0x8095, PSETID_Address]: email2_original_entry_id,
-[0x80a2, PSETID_Address]: email3_addr_type,
-[0x80a3, PSETID_Address]: email3_email_address,
-[0x80a4, PSETID_Address]: email3_original_display_name,
-[0x80a5, PSETID_Address]: email3_original_entry_id,
-[0x80d8, PSETID_Address]: internet_free_busy_address,
-[0x8101, PSETID_Task]: status,
-[0x8102, PSETID_Task]: percent_complete,
-[0x8103, PSETID_Task]: team_task,
-[0x8104, PSETID_Task]: start_date,
-[0x8105, PSETID_Task]: due_date,
-[0x8106, PSETID_Task]: duration,
-[0x810f, PSETID_Task]: date_completed,
-[0x8110, PSETID_Task]: actual_work,
-[0x8111, PSETID_Task]: total_work,
-[0x811c, PSETID_Task]: complete,
-[0x811f, PSETID_Task]: owner,
-[0x8126, PSETID_Task]: is_recurring,
-[0x8205, PSETID_Appointment]: busy_status,
-[0x8208, PSETID_Appointment]: location,
-[0x820d, PSETID_Appointment]: start_date,
-[0x820e, PSETID_Appointment]: end_date,
-[0x8213, PSETID_Appointment]: duration,
-[0x8214, PSETID_Appointment]: colors,
-[0x8216, PSETID_Appointment]: recurrence_state,
-[0x8218, PSETID_Appointment]: response_status,
-[0x8222, PSETID_Appointment]: reply_time,
-[0x8223, PSETID_Appointment]: is_recurring,
-[0x822e, PSETID_Appointment]: organizer,
-[0x8231, PSETID_Appointment]: recurrence_type,
-[0x8232, PSETID_Appointment]: recurrence_pattern,
-# also had CdoPR_FLAG_DUE_BY, when applied to messages. i don't currently
-# use message class specific names
-[0x8502, PSETID_Common]: reminder_time,
-[0x8503, PSETID_Common]: reminder_set,
-[0x8516, PSETID_Common]: common_start,
-[0x8517, PSETID_Common]: common_end,
-[0x851c, PSETID_Common]: reminder_override,
-[0x851e, PSETID_Common]: reminder_sound,
-[0x851f, PSETID_Common]: reminder_file,
-# this one only listed as CdoPR_FLAG_TEXT. maybe should be
-# reminder_text
-[0x8530, PSETID_Common]: flag_text,
-[0x8534, PSETID_Common]: mileage,
-[0x8535, PSETID_Common]: billing_information,
-[0x8539, PSETID_Common]: companies,
-[0x853a, PSETID_Common]: contact_names,
-# had CdoPR_FLAG_DUE_BY_NEXT for this one also
-[0x8560, PSETID_Common]: reminder_next_time,
-[0x8700, PSETID_Log]: entry,
-[0x8704, PSETID_Log]: start_date,
-[0x8705, PSETID_Log]: start_time,
-[0x8706, PSETID_Log]: start,
-[0x8707, PSETID_Log]: duration,
-[0x8708, PSETID_Log]: end,
-[0x870e, PSETID_Log]: doc_printed,
-[0x870f, PSETID_Log]: doc_saved,
-[0x8710, PSETID_Log]: doc_routed,
-[0x8711, PSETID_Log]: doc_posted,
-[0x8712, PSETID_Log]: entry_type,
-[0x8b00, PSETID_Note]: color,
-[0x8b02, PSETID_Note]: width,
-[0x8b03, PSETID_Note]: height,
-["Keywords", PS_PUBLIC_STRINGS]: categories
-}
diff --git a/vendor/ruby-msg/data/types.yaml b/vendor/ruby-msg/data/types.yaml
deleted file mode 100644
index b2c9024b8..000000000
--- a/vendor/ruby-msg/data/types.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
----
-# grep ' PT_' mapitags.yaml | sort -u > types.yaml
-- PT_BINARY
-- PT_BOOLEAN
-- PT_CLSID
-- PT_I8
-- PT_LONG
-- PT_MV_BINARY
-- PT_MV_LONG
-- PT_MV_TSTRING
-- PT_OBJECT
-- PT_SHORT
-- PT_STRING8
-- PT_SYSTIME
-- PT_TSTRING
diff --git a/vendor/ruby-msg/lib/mapi.rb b/vendor/ruby-msg/lib/mapi.rb
deleted file mode 100644
index b9d3413f7..000000000
--- a/vendor/ruby-msg/lib/mapi.rb
+++ /dev/null
@@ -1,109 +0,0 @@
-require 'mapi/types'
-require 'mapi/property_set'
-
-module Mapi
- VERSION = '1.4.0'
-
- #
- # Mapi::Item is the base class used for all mapi objects, and is purely a
- # property set container
- #
- class Item
- attr_reader :properties
- alias props properties
-
- # +properties+ should be a PropertySet instance.
- def initialize properties
- @properties = properties
- end
- end
-
- # a general attachment class. is subclassed by Msg and Pst attachment classes
- class Attachment < Item
- def filename
- props.attach_long_filename || props.attach_filename
- end
-
- def data
- @embedded_msg || @embedded_ole || props.attach_data
- end
-
- # with new stream work, its possible to not have the whole thing in memory at one time,
- # just to save an attachment
- #
- # a = msg.attachments.first
- # a.save open(File.basename(a.filename || 'attachment'), 'wb')
- def save io
- raise "can only save binary data blobs, not ole dirs" if @embedded_ole
- data.each_read { |chunk| io << chunk }
- end
-
- def inspect
- "#<#{self.class.to_s[/\w+$/]}" +
- (filename ? " filename=#{filename.inspect}" : '') +
- (@embedded_ole ? " embedded_type=#{@embedded_ole.embedded_type.inspect}" : '') + ">"
- end
- end
-
- class Recipient < Item
- # some kind of best effort guess for converting to standard mime style format.
- # there are some rules for encoding non 7bit stuff in mail headers. should obey
- # that here, as these strings could be unicode
- # email_address will be an EX:/ address (X.400?), unless external recipient. the
- # other two we try first.
- # consider using entry id for this too.
- def name
- name = props.transmittable_display_name || props.display_name
- # dequote
- name[/^'(.*)'/, 1] or name rescue nil
- end
-
- def email
- props.smtp_address || props.org_email_addr || props.email_address
- end
-
- RECIPIENT_TYPES = { 0 => :orig, 1 => :to, 2 => :cc, 3 => :bcc }
- def type
- RECIPIENT_TYPES[props.recipient_type]
- end
-
- def to_s
- if name = self.name and !name.empty? and email && name != email
- %{"#{name}" <#{email}>}
- else
- email || name
- end
- end
-
- def inspect
- "#<#{self.class.to_s[/\w+$/]}:#{self.to_s.inspect}>"
- end
- end
-
- # i refer to it as a message (as does mapi), although perhaps Item is better, as its a more general
- # concept than a message, as used in Pst files. though maybe i'll switch to using
- # Mapi::Object as the base class there.
- #
- # IMessage essentially, but there's also stuff like IMAPIFolder etc. so, for this to form
- # basis for PST Item, it'd need to be more general.
- class Message < Item
- # these 2 collections should be provided by our subclasses
- def attachments
- raise NotImplementedError
- end
-
- def recipients
- raise NotImplementedError
- end
-
- def inspect
- str = %w[message_class from to subject].map do |key|
- " #{key}=#{props.send(key).inspect}"
- end.compact.join
- str << " recipients=#{recipients.inspect}"
- str << " attachments=#{attachments.inspect}"
- "#<#{self.class.to_s[/\w+$/]}#{str}>"
- end
- end
-end
-
diff --git a/vendor/ruby-msg/lib/mapi/convert.rb b/vendor/ruby-msg/lib/mapi/convert.rb
deleted file mode 100644
index 4c7a0d298..000000000
--- a/vendor/ruby-msg/lib/mapi/convert.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-# we have two different "backends" for note conversion. we're sticking with
-# the current (home grown) mime one until the tmail version is suitably
-# polished.
-require 'mapi/convert/note-mime'
-require 'mapi/convert/contact'
-
-module Mapi
- class Message
- CONVERSION_MAP = {
- 'text/x-vcard' => [:to_vcard, 'vcf'],
- 'message/rfc822' => [:to_mime, 'eml'],
- 'text/plain' => [:to_post, 'txt']
- # ...
- }
-
- # get the mime type of the message.
- def mime_type
- case props.message_class #.downcase <- have a feeling i saw other cased versions
- when 'IPM.Contact'
- # apparently "text/directory; profile=vcard" is what you're supposed to use
- 'text/x-vcard'
- when 'IPM.Note'
- 'message/rfc822'
- when 'IPM.Post'
- 'text/plain'
- when 'IPM.StickyNote'
- 'text/plain' # hmmm....
- else
- Mapi::Log.warn 'unknown message_class - %p' % props.message_class
- nil
- end
- end
-
- def convert
- type = mime_type
- unless pair = CONVERSION_MAP[type]
- raise 'unable to convert message with mime type - %p' % type
- end
- send pair.first
- end
-
- # should probably be moved to mapi/convert/post
- class Post
- # not really sure what the pertinent properties are. we just do nothing for now...
- def initialize message
- @message = message
- end
-
- def to_s
- # should maybe handle other types, like html body. need a better format for post
- # probably anyway, cause a lot of meta data is getting chucked.
- @message.props.body
- end
- end
-
- def to_post
- Post.new self
- end
- end
-end
-
diff --git a/vendor/ruby-msg/lib/mapi/convert/contact.rb b/vendor/ruby-msg/lib/mapi/convert/contact.rb
deleted file mode 100644
index 838ae6498..000000000
--- a/vendor/ruby-msg/lib/mapi/convert/contact.rb
+++ /dev/null
@@ -1,142 +0,0 @@
-require 'rubygems'
-require 'vpim/vcard'
-
-# patch Vpim. TODO - fix upstream, or verify old behaviour was ok
-def Vpim.encode_text v
- # think the regexp was wrong
- v.to_str.gsub(/(.)/m) do
- case $1
- when "\n"
- "\\n"
- when "\\", ",", ";"
- "\\#{$1}"
- else
- $1
- end
- end
-end
-
-module Mapi
- class Message
- class VcardConverter
- include Vpim
-
- # a very incomplete mapping, but its a start...
- # can't find where to set a lot of stuff, like zipcode, jobtitle etc
- VCARD_MAP = {
- # these are all standard mapi properties
- :name => [
- {
- :given => :given_name,
- :family => :surname,
- :fullname => :subject
- }
- ],
- # outlook seems to eschew the mapi properties this time,
- # like postal_address, street_address, home_address_city
- # so we use the named properties
- :addr => [
- {
- :location => 'work',
- :street => :business_address_street,
- :locality => proc do |props|
- [props.business_address_city, props.business_address_state].compact * ', '
- end
- }
- ],
-
- # right type? maybe date
- :birthday => :birthday,
- :nickname => :nickname
-
- # photo available?
- # FIXME finish, emails, telephones etc
- }
-
- attr_reader :msg
- def initialize msg
- @msg = msg
- end
-
- def field name, *args
- DirectoryInfo::Field.create name, Vpim.encode_text_list(args)
- end
-
- def get_property key
- if String === key
- return key
- elsif key.respond_to? :call
- value = key.call msg.props
- else
- value = msg.props[key]
- end
- if String === value and value.empty?
- nil
- else
- value
- end
- end
-
- def get_properties hash
- constants = {}
- others = {}
- hash.each do |to, from|
- if String === from
- constants[to] = from
- else
- value = get_property from
- others[to] = value if value
- end
- end
- return nil if others.empty?
- others.merge constants
- end
-
- def convert
- Vpim::Vcard::Maker.make2 do |m|
- # handle name
- [:name, :addr].each do |type|
- VCARD_MAP[type].each do |hash|
- next unless props = get_properties(hash)
- m.send "add_#{type}" do |n|
- props.each { |key, value| n.send "#{key}=", value }
- end
- end
- end
-
- (VCARD_MAP.keys - [:name, :addr]).each do |key|
- value = get_property VCARD_MAP[key]
- m.send "#{key}=", value if value
- end
-
- # the rest of the stuff is custom
-
- url = get_property(:webpage) || get_property(:business_home_page)
- m.add_field field('URL', url) if url
- m.add_field field('X-EVOLUTION-FILE-AS', get_property(:file_under)) if get_property(:file_under)
-
- addr = get_property(:email_email_address) || get_property(:email_original_display_name)
- if addr
- m.add_email addr do |e|
- e.format ='x400' unless msg.props.email_addr_type == 'SMTP'
- end
- end
-
- if org = get_property(:company_name)
- m.add_field field('ORG', get_property(:company_name))
- end
-
- # TODO: imaddress
- end
- end
- end
-
- def to_vcard
- #p props.raw.reject { |key, value| key.guid.inspect !~ /00062004-0000-0000-c000-000000000046/ }.
- # map { |key, value| [key.to_sym, value] }.reject { |a, b| b.respond_to? :read }
- #y props.to_h.reject { |a, b| b.respond_to? :read }
- VcardConverter.new(self).convert
- end
- end
-end
-
diff --git a/vendor/ruby-msg/lib/mapi/convert/note-mime.rb b/vendor/ruby-msg/lib/mapi/convert/note-mime.rb
deleted file mode 100644
index deb035f2c..000000000
--- a/vendor/ruby-msg/lib/mapi/convert/note-mime.rb
+++ /dev/null
@@ -1,274 +0,0 @@
-require 'base64'
-require 'mime'
-require 'time'
-
-# there is still some Msg specific stuff in here.
-
-module Mapi
- class Message
- def mime
- return @mime if @mime
- # if these headers exist at all, they can be helpful. we may however get a
- # application/ms-tnef mime root, which means there will be little other than
- # headers. we may get nothing.
- # and other times, when received from external, we get the full cigar, boundaries
- # etc and all.
- # sometimes its multipart, with no boundaries. that throws an error. so we'll be more
- # forgiving here
- @mime = SimpleMime.new props.transport_message_headers.to_s, true
- populate_headers
- @mime
- end
-
- def headers
- mime.headers
- end
-
- # copy data from msg properties storage to standard mime. headers
- # i've now seen it where the existing headers had heaps on stuff, and the msg#props had
- # practically nothing. think it was because it was a tnef - msg conversion done by exchange.
- def populate_headers
- # construct a From value
- # should this kind of thing only be done when headers don't exist already? maybe not. if its
- # sent, then modified and saved, the headers could be wrong?
- # hmmm. i just had an example where a mail is sent, from an internal user, but it has transport
- # headers, i think because one recipient was external. the only place the senders email address
- # exists is in the transport headers. so its maybe not good to overwrite from.
- # recipients however usually have smtp address available.
- # maybe we'll do it for all addresses that are smtp? (is that equivalent to
- # sender_email_address !~ /^\//
- name, email = props.sender_name, props.sender_email_address
- if props.sender_addrtype == 'SMTP'
- headers['From'] = if name and email and name != email
- [%{"#{name}" <#{email}>}]
- else
- [email || name]
- end
- elsif !headers.has_key?('From')
- # some messages were never sent, so that sender stuff isn't filled out. need to find another
- # way to get something
- # what about marking whether we thing the email was sent or not? or draft?
- # for partition into an eventual Inbox, Sent, Draft mbox set?
- # i've now seen cases where this stuff is missing, but exists in transport message headers,
- # so maybe i should inhibit this in that case.
- if email
- # disabling this warning for now
- #Log.warn "* no smtp sender email address available (only X.400). creating fake one"
- # this is crap. though i've specially picked the logic so that it generates the correct
- # email addresses in my case (for my organisation).
- # this user stuff will give valid email i think, based on alias.
- user = name ? name.sub(/(.*), (.*)/, "\\2.\\1") : email[/\w+$/].downcase
- domain = (email[%r{^/O=([^/]+)}i, 1].downcase + '.com' rescue email)
- headers['From'] = [name ? %{"#{name}" <#{user}@#{domain}>} : "<#{user}@#{domain}>" ]
- elsif name
- # we only have a name? thats screwed up.
- # disabling this warning for now
- #Log.warn "* no smtp sender email address available (only name). creating fake one"
- headers['From'] = [%{"#{name}"}]
- else
- # disabling this warning for now
- #Log.warn "* no sender email address available at all. FIXME"
- end
- # else we leave the transport message header version
- end
-
- # for all of this stuff, i'm assigning in utf8 strings.
- # thats ok i suppose, maybe i can say its the job of the mime class to handle that.
- # but a lot of the headers are overloaded in different ways. plain string, many strings
- # other stuff. what happens to a person who has a " in their name etc etc. encoded words
- # i suppose. but that then happens before assignment. and can't be automatically undone
- # until the header is decomposed into recipients.
- recips_by_type = recipients.group_by { |r| r.type }
- # i want to the the types in a specific order.
- [:to, :cc, :bcc].each do |type|
- # don't know why i bother, but if we can, we try to sort recipients by the numerical part
- # of the ole name, or just leave it if we can't
- recips = recips_by_type[type]
- recips = (recips.sort_by { |r| r.obj.name[/\d{8}$/].hex } rescue recips)
- # switched to using , for separation, not ;. see issue #4
- # recips.empty? is strange. i wouldn't have thought it possible, but it was right?
- headers[type.to_s.sub(/^(.)/) { $1.upcase }] = [recips.join(', ')] if recips and !recips.empty?
- end
- headers['Subject'] = [props.subject] if props.subject
-
- # fill in a date value. by default, we won't mess with existing value hear
- if !headers.has_key?('Date')
- # we want to get a received date, as i understand it.
- # use this preference order, or pull the most recent?
- keys = %w[message_delivery_time client_submit_time last_modification_time creation_time]
- time = keys.each { |key| break time if time = props.send(key) }
- time = nil unless Date === time
-
- # now convert and store
- # this is a little funky. not sure about time zone stuff either?
- # actually seems ok. maybe its always UTC and interpreted anyway. or can be timezoneless.
- # i have no timezone info anyway.
- # in gmail, i see stuff like 15 Jan 2007 00:48:19 -0000, and it displays as 11:48.
- # can also add .localtime here if desired. but that feels wrong.
- headers['Date'] = [Time.iso8601(time.to_s).rfc2822] if time
- end
-
- # some very simplistic mapping between internet message headers and the
- # mapi properties
- # any of these could be causing duplicates due to case issues. the hack in #to_mime
- # just stops re-duplication at that point. need to move some smarts into the mime
- # code to handle it.
- mapi_header_map = [
- [:internet_message_id, 'Message-ID'],
- [:in_reply_to_id, 'In-Reply-To'],
- # don't set these values if they're equal to the defaults anyway
- [:importance, 'Importance', proc { |val| val.to_s == '1' ? nil : val }],
- [:priority, 'Priority', proc { |val| val.to_s == '1' ? nil : val }],
- [:sensitivity, 'Sensitivity', proc { |val| val.to_s == '0' ? nil : val }],
- # yeah?
- [:conversation_topic, 'Thread-Topic'],
- # not sure of the distinction here
- # :originator_delivery_report_requested ??
- [:read_receipt_requested, 'Disposition-Notification-To', proc { |val| from }]
- ]
- mapi_header_map.each do |mapi, mime, *f|
- next unless q = val = props.send(mapi) or headers.has_key?(mime)
- next if f[0] and !(val = f[0].call(val))
- headers[mime] = [val.to_s]
- end
- end
-
- # redundant?
- def type
- props.message_class[/IPM\.(.*)/, 1].downcase rescue nil
- end
-
- # shortcuts to some things from the headers
- %w[From To Cc Bcc Subject].each do |key|
- define_method(key.downcase) { headers[key].join(' ') if headers.has_key?(key) }
- end
-
- def body_to_mime
- # to create the body
- # should have some options about serializing rtf. and possibly options to check the rtf
- # for rtf2html conversion, stripping those html tags or other similar stuff. maybe want to
- # ignore it in the cases where it is generated from incoming html. but keep it if it was the
- # source for html and plaintext.
- if props.body_rtf or props.body_html
- # should plain come first?
- mime = SimpleMime.new "Content-Type: multipart/alternative\r\n\r\n"
- # its actually possible for plain body to be empty, but the others not.
- # if i can get an html version, then maybe a callout to lynx can be made...
- mime.parts << SimpleMime.new("Content-Type: text/plain\r\n\r\n" + props.body) if props.body
- # this may be automatically unwrapped from the rtf if the rtf includes the html
- mime.parts << SimpleMime.new("Content-Type: text/html\r\n\r\n" + props.body_html) if props.body_html
- # temporarily disabled the rtf. its just showing up as an attachment anyway.
- #mime.parts << SimpleMime.new("Content-Type: text/rtf\r\n\r\n" + props.body_rtf) if props.body_rtf
- # its thus currently possible to get no body at all if the only body is rtf. that is not
- # really acceptable FIXME
- mime
- else
- # check no header case. content type? etc?. not sure if my SimpleMime class will accept
- Log.debug "taking that other path"
- # body can be nil, hence the to_s
- SimpleMime.new "Content-Type: text/plain\r\n\r\n" + props.body.to_s
- end
- end
-
- def to_mime
- # intended to be used for IPM.note, which is the email type. can use it for others if desired,
- # YMMV
- Log.warn "to_mime used on a #{props.message_class}" unless props.message_class == 'IPM.Note'
- # we always have a body
- mime = body = body_to_mime
-
- # If we have attachments, we take the current mime root (body), and make it the first child
- # of a new tree that will contain body and attachments.
- unless attachments.empty?
- mime = SimpleMime.new "Content-Type: multipart/mixed\r\n\r\n"
- mime.parts << body
- # i don't know any better way to do this. need multipart/related for inline images
- # referenced by cid: urls to work, but don't want to use it otherwise...
- related = false
- attachments.each do |attach|
- part = attach.to_mime
- related = true if part.headers.has_key?('Content-ID') or part.headers.has_key?('Content-Location')
- mime.parts << part
- end
- mime.headers['Content-Type'] = ['multipart/related'] if related
- end
-
- # at this point, mime is either
- # - a single text/plain, consisting of the body ('taking that other path' above. rare)
- # - a multipart/alternative, consiting of a few bodies (plain and html body. common)
- # - a multipart/mixed, consisting of 1 of the above 2 types of bodies, and attachments.
- # we add this standard preamble if its multipart
- # FIXME preamble.replace, and body.replace both suck.
- # preamble= is doable. body= wasn't being done because body will get rewritten from parts
- # if multipart, and is only there readonly. can do that, or do a reparse...
- # The way i do this means that only the first preamble will say it, not preambles of nested
- # multipart chunks.
- mime.preamble.replace "This is a multi-part message in MIME format.\r\n" if mime.multipart?
-
- # now that we have a root, we can mix in all our headers
- headers.each do |key, vals|
- # don't overwrite the content-type, encoding style stuff
- next if mime.headers.has_key? key
- # some new temporary hacks
- next if key =~ /content-type/i and vals[0] =~ /base64/
- next if mime.headers.keys.map(&:downcase).include? key.downcase
- mime.headers[key] += vals
- end
- # just a stupid hack to make the content-type header last, when using OrderedHash
- mime.headers['Content-Type'] = mime.headers.delete 'Content-Type'
-
- mime
- end
- end
-
- class Attachment
- def to_mime
- # TODO: smarter mime typing.
- mimetype = props.attach_mime_tag || 'application/octet-stream'
- mime = SimpleMime.new "Content-Type: #{mimetype}\r\n\r\n"
- mime.headers['Content-Disposition'] = [%{attachment; filename="#{filename}"}]
- mime.headers['Content-Transfer-Encoding'] = ['base64']
- mime.headers['Content-Location'] = [props.attach_content_location] if props.attach_content_location
- mime.headers['Content-ID'] = [props.attach_content_id] if props.attach_content_id
- # data.to_s for now. data was nil for some reason.
- # perhaps it was a data object not correctly handled?
- # hmmm, have to use read here. that assumes that the data isa stream.
- # but if the attachment data is a string, then it won't work. possible?
- data_str = if @embedded_msg
- mime.headers['Content-Type'] = 'message/rfc822'
- # lets try making it not base64 for now
- mime.headers.delete 'Content-Transfer-Encoding'
- # not filename. rather name, or something else right?
- # maybe it should be inline?? i forget attach_method / access meaning
- mime.headers['Content-Disposition'] = [%{attachment; filename="#{@embedded_msg.subject}"}]
- @embedded_msg.to_mime.to_s
- elsif @embedded_ole
- # kind of hacky
- io = StringIO.new
- Ole::Storage.new io do |ole|
- ole.root.type = :dir
- Ole::Storage::Dirent.copy @embedded_ole, ole.root
- end
- io.string
- else
- # FIXME: shouldn't be required
- data.read.to_s rescue ''
- end
- mime.body.replace @embedded_msg ? data_str : Base64.encode64(data_str).gsub(/\n/, "\r\n")
- mime
- end
- end
-
- class Msg < Message
- def populate_headers
- super
- if !headers.has_key?('Date')
- # can employ other methods for getting a time. heres one in a similar vein to msgconvert.pl,
- # ie taking the time from an ole object
- time = @root.ole.dirents.map { |dirent| dirent.modify_time || dirent.create_time }.compact.sort.last
- headers['Date'] = [Time.iso8601(time.to_s).rfc2822] if time
- end
- end
- end
-end
-
diff --git a/vendor/ruby-msg/lib/mapi/convert/note-tmail.rb b/vendor/ruby-msg/lib/mapi/convert/note-tmail.rb
deleted file mode 100644
index 9ccc9e0b3..000000000
--- a/vendor/ruby-msg/lib/mapi/convert/note-tmail.rb
+++ /dev/null
@@ -1,287 +0,0 @@
-require 'rubygems'
-require 'tmail'
-
-# these will be removed later
-require 'time'
-require 'mime'
-
-# there is some Msg specific stuff in here.
-
-class TMail::Mail
- def quoted_body= str
- body_port.wopen { |f| f.write str }
- str
- end
-end
-
-module Mapi
- class Message
- def mime
- return @mime if @mime
- # if these headers exist at all, they can be helpful. we may however get a
- # application/ms-tnef mime root, which means there will be little other than
- # headers. we may get nothing.
- # and other times, when received from external, we get the full cigar, boundaries
- # etc and all.
- # sometimes its multipart, with no boundaries. that throws an error. so we'll be more
- # forgiving here
- @mime = Mime.new props.transport_message_headers.to_s, true
- populate_headers
- @mime
- end
-
- def headers
- mime.headers
- end
-
- # copy data from msg properties storage to standard mime. headers
- # i've now seen it where the existing headers had heaps on stuff, and the msg#props had
- # practically nothing. think it was because it was a tnef - msg conversion done by exchange.
- def populate_headers
- # construct a From value
- # should this kind of thing only be done when headers don't exist already? maybe not. if its
- # sent, then modified and saved, the headers could be wrong?
- # hmmm. i just had an example where a mail is sent, from an internal user, but it has transport
- # headers, i think because one recipient was external. the only place the senders email address
- # exists is in the transport headers. so its maybe not good to overwrite from.
- # recipients however usually have smtp address available.
- # maybe we'll do it for all addresses that are smtp? (is that equivalent to
- # sender_email_address !~ /^\//
- name, email = props.sender_name, props.sender_email_address
- if props.sender_addrtype == 'SMTP'
- headers['From'] = if name and email and name != email
- [%{"#{name}" <#{email}>}]
- else
- [email || name]
- end
- elsif !headers.has_key?('From')
- # some messages were never sent, so that sender stuff isn't filled out. need to find another
- # way to get something
- # what about marking whether we thing the email was sent or not? or draft?
- # for partition into an eventual Inbox, Sent, Draft mbox set?
- # i've now seen cases where this stuff is missing, but exists in transport message headers,
- # so maybe i should inhibit this in that case.
- if email
- # disabling this warning for now
- #Log.warn "* no smtp sender email address available (only X.400). creating fake one"
- # this is crap. though i've specially picked the logic so that it generates the correct
- # email addresses in my case (for my organisation).
- # this user stuff will give valid email i think, based on alias.
- user = name ? name.sub(/(.*), (.*)/, "\\2.\\1") : email[/\w+$/].downcase
- domain = (email[%r{^/O=([^/]+)}i, 1].downcase + '.com' rescue email)
- headers['From'] = [name ? %{"#{name}" <#{user}@#{domain}>} : "<#{user}@#{domain}>" ]
- elsif name
- # we only have a name? thats screwed up.
- # disabling this warning for now
- #Log.warn "* no smtp sender email address available (only name). creating fake one"
- headers['From'] = [%{"#{name}"}]
- else
- # disabling this warning for now
- #Log.warn "* no sender email address available at all. FIXME"
- end
- # else we leave the transport message header version
- end
-
- # for all of this stuff, i'm assigning in utf8 strings.
- # thats ok i suppose, maybe i can say its the job of the mime class to handle that.
- # but a lot of the headers are overloaded in different ways. plain string, many strings
- # other stuff. what happens to a person who has a " in their name etc etc. encoded words
- # i suppose. but that then happens before assignment. and can't be automatically undone
- # until the header is decomposed into recipients.
- recips_by_type = recipients.group_by { |r| r.type }
- # i want to the the types in a specific order.
- [:to, :cc, :bcc].each do |type|
- # don't know why i bother, but if we can, we try to sort recipients by the numerical part
- # of the ole name, or just leave it if we can't
- recips = recips_by_type[type]
- recips = (recips.sort_by { |r| r.obj.name[/\d{8}$/].hex } rescue recips)
- # switched to using , for separation, not ;. see issue #4
- # recips.empty? is strange. i wouldn't have thought it possible, but it was right?
- headers[type.to_s.sub(/^(.)/) { $1.upcase }] = [recips.join(', ')] unless recips.empty?
- end
- headers['Subject'] = [props.subject] if props.subject
-
- # fill in a date value. by default, we won't mess with existing value hear
- if !headers.has_key?('Date')
- # we want to get a received date, as i understand it.
- # use this preference order, or pull the most recent?
- keys = %w[message_delivery_time client_submit_time last_modification_time creation_time]
- time = keys.each { |key| break time if time = props.send(key) }
- time = nil unless Date === time
-
- # now convert and store
- # this is a little funky. not sure about time zone stuff either?
- # actually seems ok. maybe its always UTC and interpreted anyway. or can be timezoneless.
- # i have no timezone info anyway.
- # in gmail, i see stuff like 15 Jan 2007 00:48:19 -0000, and it displays as 11:48.
- # can also add .localtime here if desired. but that feels wrong.
- headers['Date'] = [Time.iso8601(time.to_s).rfc2822] if time
- end
-
- # some very simplistic mapping between internet message headers and the
- # mapi properties
- # any of these could be causing duplicates due to case issues. the hack in #to_mime
- # just stops re-duplication at that point. need to move some smarts into the mime
- # code to handle it.
- mapi_header_map = [
- [:internet_message_id, 'Message-ID'],
- [:in_reply_to_id, 'In-Reply-To'],
- # don't set these values if they're equal to the defaults anyway
- [:importance, 'Importance', proc { |val| val.to_s == '1' ? nil : val }],
- [:priority, 'Priority', proc { |val| val.to_s == '1' ? nil : val }],
- [:sensitivity, 'Sensitivity', proc { |val| val.to_s == '0' ? nil : val }],
- # yeah?
- [:conversation_topic, 'Thread-Topic'],
- # not sure of the distinction here
- # :originator_delivery_report_requested ??
- [:read_receipt_requested, 'Disposition-Notification-To', proc { |val| from }]
- ]
- mapi_header_map.each do |mapi, mime, *f|
- next unless q = val = props.send(mapi) or headers.has_key?(mime)
- next if f[0] and !(val = f[0].call(val))
- headers[mime] = [val.to_s]
- end
- end
-
- # redundant?
- def type
- props.message_class[/IPM\.(.*)/, 1].downcase rescue nil
- end
-
- # shortcuts to some things from the headers
- %w[From To Cc Bcc Subject].each do |key|
- define_method(key.downcase) { headers[key].join(' ') if headers.has_key?(key) }
- end
-
- def body_to_tmail
- # to create the body
- # should have some options about serializing rtf. and possibly options to check the rtf
- # for rtf2html conversion, stripping those html tags or other similar stuff. maybe want to
- # ignore it in the cases where it is generated from incoming html. but keep it if it was the
- # source for html and plaintext.
- if props.body_rtf or props.body_html
- # should plain come first?
- part = TMail::Mail.new
- # its actually possible for plain body to be empty, but the others not.
- # if i can get an html version, then maybe a callout to lynx can be made...
- part.parts << TMail::Mail.parse("Content-Type: text/plain\r\n\r\n" + props.body) if props.body
- # this may be automatically unwrapped from the rtf if the rtf includes the html
- part.parts << TMail::Mail.parse("Content-Type: text/html\r\n\r\n" + props.body_html) if props.body_html
- # temporarily disabled the rtf. its just showing up as an attachment anyway.
- #mime.parts << Mime.new("Content-Type: text/rtf\r\n\r\n" + props.body_rtf) if props.body_rtf
- # its thus currently possible to get no body at all if the only body is rtf. that is not
- # really acceptable FIXME
- part['Content-Type'] = 'multipart/alternative'
- part
- else
- # check no header case. content type? etc?. not sure if my Mime class will accept
- Log.debug "taking that other path"
- # body can be nil, hence the to_s
- TMail::Mail.parse "Content-Type: text/plain\r\n\r\n" + props.body.to_s
- end
- end
-
- def to_tmail
- # intended to be used for IPM.note, which is the email type. can use it for others if desired,
- # YMMV
- Log.warn "to_mime used on a #{props.message_class}" unless props.message_class == 'IPM.Note'
- # we always have a body
- mail = body = body_to_tmail
-
- # If we have attachments, we take the current mime root (body), and make it the first child
- # of a new tree that will contain body and attachments.
- unless attachments.empty?
- raise NotImplementedError
- mime = Mime.new "Content-Type: multipart/mixed\r\n\r\n"
- mime.parts << body
- # i don't know any better way to do this. need multipart/related for inline images
- # referenced by cid: urls to work, but don't want to use it otherwise...
- related = false
- attachments.each do |attach|
- part = attach.to_mime
- related = true if part.headers.has_key?('Content-ID') or part.headers.has_key?('Content-Location')
- mime.parts << part
- end
- mime.headers['Content-Type'] = ['multipart/related'] if related
- end
-
- # at this point, mime is either
- # - a single text/plain, consisting of the body ('taking that other path' above. rare)
- # - a multipart/alternative, consiting of a few bodies (plain and html body. common)
- # - a multipart/mixed, consisting of 1 of the above 2 types of bodies, and attachments.
- # we add this standard preamble if its multipart
- # FIXME preamble.replace, and body.replace both suck.
- # preamble= is doable. body= wasn't being done because body will get rewritten from parts
- # if multipart, and is only there readonly. can do that, or do a reparse...
- # The way i do this means that only the first preamble will say it, not preambles of nested
- # multipart chunks.
- mail.quoted_body = "This is a multi-part message in MIME format.\r\n" if mail.multipart?
-
- # now that we have a root, we can mix in all our headers
- headers.each do |key, vals|
- # don't overwrite the content-type, encoding style stuff
- next if mail[key]
- # some new temporary hacks
- next if key =~ /content-type/i and vals[0] =~ /base64/
- #next if mime.headers.keys.map(&:downcase).include? key.downcase
- mail[key] = vals.first
- end
- # just a stupid hack to make the content-type header last, when using OrderedHash
- #mime.headers['Content-Type'] = mime.headers.delete 'Content-Type'
-
- mail
- end
- end
-
- class Attachment
- def to_tmail
- # TODO: smarter mime typing.
- mimetype = props.attach_mime_tag || 'application/octet-stream'
- part = TMail::Mail.parse "Content-Type: #{mimetype}\r\n\r\n"
- part['Content-Disposition'] = %{attachment; filename="#{filename}"}
- part['Content-Transfer-Encoding'] = 'base64'
- part['Content-Location'] = props.attach_content_location if props.attach_content_location
- part['Content-ID'] = props.attach_content_id if props.attach_content_id
- # data.to_s for now. data was nil for some reason.
- # perhaps it was a data object not correctly handled?
- # hmmm, have to use read here. that assumes that the data isa stream.
- # but if the attachment data is a string, then it won't work. possible?
- data_str = if @embedded_msg
- raise NotImplementedError
- mime.headers['Content-Type'] = 'message/rfc822'
- # lets try making it not base64 for now
- mime.headers.delete 'Content-Transfer-Encoding'
- # not filename. rather name, or something else right?
- # maybe it should be inline?? i forget attach_method / access meaning
- mime.headers['Content-Disposition'] = [%{attachment; filename="#{@embedded_msg.subject}"}]
- @embedded_msg.to_mime.to_s
- elsif @embedded_ole
- raise NotImplementedError
- # kind of hacky
- io = StringIO.new
- Ole::Storage.new io do |ole|
- ole.root.type = :dir
- Ole::Storage::Dirent.copy @embedded_ole, ole.root
- end
- io.string
- else
- data.read.to_s
- end
- part.body = @embedded_msg ? data_str : Base64.encode64(data_str).gsub(/\n/, "\r\n")
- part
- end
- end
-
- class Msg < Message
- def populate_headers
- super
- if !headers.has_key?('Date')
- # can employ other methods for getting a time. heres one in a similar vein to msgconvert.pl,
- # ie taking the time from an ole object
- time = @root.ole.dirents.map { |dirent| dirent.modify_time || dirent.create_time }.compact.sort.last
- headers['Date'] = [Time.iso8601(time.to_s).rfc2822] if time
- end
- end
- end
-end
-
diff --git a/vendor/ruby-msg/lib/mapi/msg.rb b/vendor/ruby-msg/lib/mapi/msg.rb
deleted file mode 100644
index fc30a9170..000000000
--- a/vendor/ruby-msg/lib/mapi/msg.rb
+++ /dev/null
@@ -1,440 +0,0 @@
-require 'rubygems'
-require 'ole/storage'
-require 'mapi'
-require 'mapi/rtf'
-
-module Mapi
- #
- # = Introduction
- #
- # Primary class interface to the vagaries of .msg files.
- #
- # The core of the work is done by the <tt>Msg::PropertyStore</tt> class.
- #
- class Msg < Message
- #
- # = Introduction
- #
- # A big compononent of +Msg+ files is the property store, which holds
- # all the key/value pairs of properties. The message itself, and all
- # its <tt>Attachment</tt>s and <tt>Recipient</tt>s have an instance of
- # this class.
- #
- # = Storage model
- #
- # Property keys (tags?) can be either simple hex numbers, in the
- # range 0x0000 - 0xffff, or they can be named properties. In fact,
- # properties in the range 0x0000 to 0x7fff are supposed to be the non-
- # named properties, and can be considered to be in the +PS_MAPI+
- # namespace. (correct?)
- #
- # Named properties are serialized in the 0x8000 to 0xffff range,
- # and are referenced as a guid and long/string pair.
- #
- # There are key ranges, which can be used to imply things generally
- # about keys.
- #
- # Further, we can give symbolic names to most keys, coming from
- # constants in various places. Eg:
- #
- # 0x0037 => subject
- # {00062002-0000-0000-C000-000000000046}/0x8218 => response_status
- # # displayed as categories in outlook
- # {00020329-0000-0000-C000-000000000046}/"Keywords" => categories
- #
- # Futher, there are completely different names, coming from other
- # object models that get mapped to these things (CDO's model,
- # Outlook's model etc). Eg "urn:schemas:httpmail:subject"
- # I think these can be ignored though, as they aren't defined clearly
- # in terms of mapi properties, and i'm really just trying to make
- # a mapi property store. (It should also be relatively easy to
- # support them later.)
- #
- # = Usage
- #
- # The api is driven by a desire to have the simple stuff "just work", ie
- #
- # properties.subject
- # properties.display_name
- #
- # There also needs to be a way to look up properties more specifically:
- #
- # properties[0x0037] # => gets the subject
- # properties[0x0037, PS_MAPI] # => still gets the subject
- # properties['Keywords', PS_PUBLIC_STRINGS] # => gets outlook's categories array
- #
- # The abbreviated versions work by "resolving" the symbols to full keys:
- #
- # # the guid here is just PS_PUBLIC_STRINGS
- # properties.resolve :keywords # => #<Key {00020329-0000-0000-c000-000000000046}/"Keywords">
- # # the result here is actually also a key
- # k = properties.resolve :subject # => 0x0037
- # # it has a guid
- # k.guid == Msg::Properties::PS_MAPI # => true
- #
- # = Parsing
- #
- # There are three objects that need to be parsed to load a +Msg+ property store:
- #
- # 1. The +nameid+ directory (<tt>Properties.parse_nameid</tt>)
- # 2. The many +substg+ objects, whose names should match <tt>Properties::SUBSTG_RX</tt>
- # (<tt>Properties#parse_substg</tt>)
- # 3. The +properties+ file (<tt>Properties#parse_properties</tt>)
- #
- # Understanding of the formats is by no means perfect.
- #
- # = TODO
- #
- # * While the key objects are sufficient, the value objects are just plain
- # ruby types. It currently isn't possible to write to the values, or to know
- # which encoding the value had.
- # * Update this doc.
- # * Perhaps change from eager loading, to be load-on-demand.
- #
- class PropertyStore
- include PropertySet::Constants
- Key = PropertySet::Key
-
- # note that binary and default both use obj.open. not the block form. this means we should
- # #close it later, which we don't. as we're only reading though, it shouldn't matter right?
- # not really good though FIXME
- # change these to use mapi symbolic const names
- ENCODINGS = {
- 0x000d => proc { |obj| obj }, # seems to be used when its going to be a directory instead of a file. eg nested ole. 3701 usually. in which case we shouldn't get here right?
- 0x001f => proc { |obj| Ole::Types::FROM_UTF16.iconv obj.read }, # unicode
- # ascii
- # FIXME hack did a[0..-2] before, seems right sometimes, but for some others it chopped the text. chomp
- 0x001e => proc { |obj| obj.read.chomp 0.chr },
- 0x0102 => proc { |obj| obj.open }, # binary?
- :default => proc { |obj| obj.open }
- }
-
- SUBSTG_RX = /^__substg1\.0_([0-9A-F]{4})([0-9A-F]{4})(?:-([0-9A-F]{8}))?$/
- PROPERTIES_RX = /^__properties_version1\.0$/
- NAMEID_RX = /^__nameid_version1\.0$/
- VALID_RX = /#{SUBSTG_RX}|#{PROPERTIES_RX}|#{NAMEID_RX}/
-
- attr_reader :nameid
-
- def initialize
- @nameid = nil
- # not exactly a cache currently
- @cache = {}
- end
-
- #--
- # The parsing methods
- #++
-
- def self.load obj
- prop = new
- prop.load obj
- prop
- end
-
- # Parse properties from the +Dirent+ obj
- def load obj
- # we need to do the nameid first, as it provides the map for later user defined properties
- if nameid_obj = obj.children.find { |child| child.name =~ NAMEID_RX }
- @nameid = PropertyStore.parse_nameid nameid_obj
- # hack to make it available to all msg files from the same ole storage object
- # FIXME - come up with a neater way
- class << obj.ole
- attr_accessor :msg_nameid
- end
- obj.ole.msg_nameid = @nameid
- elsif obj.ole
- @nameid = obj.ole.msg_nameid rescue nil
- end
- # now parse the actual properties. i think dirs that match the substg should be decoded
- # as properties to. 0x000d is just another encoding, the dir encoding. it should match
- # whether the object is file / dir. currently only example is embedded msgs anyway
- obj.children.each do |child|
- next unless child.file?
- case child.name
- when PROPERTIES_RX
- parse_properties child
- when SUBSTG_RX
- parse_substg(*($~[1..-1].map { |num| num.hex rescue nil } + [child]))
- end
- end
- end
-
- # Read nameid from the +Dirent+ obj, which is used for mapping of named properties keys to
- # proxy keys in the 0x8000 - 0xffff range.
- # Returns a hash of integer -> Key.
- def self.parse_nameid obj
- remaining = obj.children.dup
- guids_obj, props_obj, names_obj =
- %w[__substg1.0_00020102 __substg1.0_00030102 __substg1.0_00040102].map do |name|
- remaining.delete obj/name
- end
-
- # parse guids
- # this is the guids for named properities (other than builtin ones)
- # i think PS_PUBLIC_STRINGS, and PS_MAPI are builtin.
- guids = [PS_PUBLIC_STRINGS] + guids_obj.read.scan(/.{16}/mn).map do |str|
- Ole::Types.load_guid str
- end
-
- # parse names.
- # the string ids for named properties
- # they are no longer parsed, as they're referred to by offset not
- # index. they are simply sequentially packed, as a long, giving
- # the string length, then padding to 4 byte multiple, and repeat.
- names_data = names_obj.read
-
- # parse actual props.
- # not sure about any of this stuff really.
- # should flip a few bits in the real msg, to get a better understanding of how this works.
- props = props_obj.read.scan(/.{8}/mn).map do |str|
- flags, offset = str[4..-1].unpack 'v2'
- # the property will be serialised as this pseudo property, mapping it to this named property
- pseudo_prop = 0x8000 + offset
- named = flags & 1 == 1
- prop = if named
- str_off = *str.unpack('V')
- len = *names_data[str_off, 4].unpack('V')
- Ole::Types::FROM_UTF16.iconv names_data[str_off + 4, len]
- else
- a, b = str.unpack('v2')
- Log.debug "b not 0" if b != 0
- a
- end
- # a bit sus
- guid_off = flags >> 1
- # missing a few builtin PS_*
- Log.debug "guid off < 2 (#{guid_off})" if guid_off < 2
- guid = guids[guid_off - 2]
- [pseudo_prop, Key.new(prop, guid)]
- end
-
- #Log.warn "* ignoring #{remaining.length} objects in nameid" unless remaining.empty?
- # this leaves a bunch of other unknown chunks of data with completely unknown meaning.
- # pp [:unknown, child.name, child.data.unpack('H*')[0].scan(/.{16}/m)]
- Hash[*props.flatten]
- end
-
- # Parse an +Dirent+, as per <tt>msgconvert.pl</tt>. This is how larger properties, such
- # as strings, binary blobs, and other ole sub-directories (eg nested Msg) are stored.
- def parse_substg key, encoding, offset, obj
- if (encoding & 0x1000) != 0
- if !offset
- # there is typically one with no offset first, whose data is a series of numbers
- # equal to the lengths of all the sub parts. gives an implied array size i suppose.
- # maybe you can initialize the array at this time. the sizes are the same as all the
- # ole object sizes anyway, its to pre-allocate i suppose.
- #p obj.data.unpack('V*')
- # ignore this one
- return
- else
- # remove multivalue flag for individual pieces
- encoding &= ~0x1000
- end
- else
- Log.warn "offset specified for non-multivalue encoding #{obj.name}" if offset
- offset = nil
- end
- # offset is for multivalue encodings.
- unless encoder = ENCODINGS[encoding]
- Log.warn "unknown encoding #{encoding}"
- #encoder = proc { |obj| obj.io } #.read }. maybe not a good idea
- encoder = ENCODINGS[:default]
- end
- add_property key, encoder[obj], offset
- end
-
- # For parsing the +properties+ file. Smaller properties are serialized in one chunk,
- # such as longs, bools, times etc. The parsing has problems.
- def parse_properties obj
- data = obj.read
- # don't really understand this that well...
- pad = data.length % 16
- unless (pad == 0 || pad == 8) and data[0...pad] == "\000" * pad
- Log.warn "padding was not as expected #{pad} (#{data.length}) -> #{data[0...pad].inspect}"
- end
- data[pad..-1].scan(/.{16}/mn).each do |data|
- property, encoding = ('%08x' % data.unpack('V')).scan /.{4}/
- key = property.hex
- # doesn't make any sense to me. probably because its a serialization of some internal
- # outlook structure...
- next if property == '0000'
- case encoding
- when '0102', '001e', '001f', '101e', '101f', '000d'
- # ignore on purpose. not sure what its for
- # multivalue versions ignored also
- when '0003' # long
- # don't know what all the other data is for
- add_property key, *data[8, 4].unpack('V')
- when '000b' # boolean
- # again, heaps more data than needed. and its not always 0 or 1.
- # they are in fact quite big numbers. this is wrong.
-# p [property, data[4..-1].unpack('H*')[0]]
- add_property key, data[8, 4].unpack('V')[0] != 0
- when '0040' # systime
- # seems to work:
- add_property key, Ole::Types.load_time(data[8..-1])
- else
- #Log.warn "ignoring data in __properties section, encoding: #{encoding}"
- #Log << data.unpack('H*').inspect + "\n"
- end
- end
- end
-
- def add_property key, value, pos=nil
- # map keys in the named property range through nameid
- if Integer === key and key >= 0x8000
- if !@nameid
- Log.warn "no nameid section yet named properties used"
- key = Key.new key
- elsif real_key = @nameid[key]
- key = real_key
- else
- # i think i hit these when i have a named property, in the PS_MAPI
- # guid
- Log.warn "property in named range not in nameid #{key.inspect}"
- key = Key.new key
- end
- else
- key = Key.new key
- end
- if pos
- @cache[key] ||= []
- Log.warn "duplicate property" unless Array === @cache[key]
- # ^ this is actually a trickier problem. the issue is more that they must all be of
- # the same type.
- @cache[key][pos] = value
- else
- # take the last.
- Log.warn "duplicate property #{key.inspect}" if @cache[key]
- @cache[key] = value
- end
- end
-
- # delegate to cache
- def method_missing name, *args, &block
- @cache.send name, *args, &block
- end
- end
-
- # these 2 will actually be of the form
- # 1\.0_#([0-9A-Z]{8}), where $1 is the 0 based index number in hex
- # should i parse that and use it as an index, or just return in
- # file order? probably should use it later...
- ATTACH_RX = /^__attach_version1\.0_.*/
- RECIP_RX = /^__recip_version1\.0_.*/
- VALID_RX = /#{PropertyStore::VALID_RX}|#{ATTACH_RX}|#{RECIP_RX}/
-
- attr_reader :root
- attr_accessor :close_parent
-
- # Alternate constructor, to create an +Msg+ directly from +arg+ and +mode+, passed
- # directly to Ole::Storage (ie either filename or seekable IO object).
- def self.open arg, mode=nil
- msg = new Ole::Storage.open(arg, mode).root
- # we will close the ole when we are #closed
- msg.close_parent = true
- if block_given?
- begin yield msg
- ensure; msg.close
- end
- else msg
- end
- end
-
- # Create an Msg from +root+, an <tt>Ole::Storage::Dirent</tt> object
- def initialize root
- @root = root
- @close_parent = false
- super PropertySet.new(PropertyStore.load(@root))
- Msg.warn_unknown @root
- end
-
- def self.warn_unknown obj
- # bit of validation. not important if there is extra stuff, though would be
- # interested to know what it is. doesn't check dir/file stuff.
- unknown = obj.children.reject { |child| child.name =~ VALID_RX }
- Log.warn "skipped #{unknown.length} unknown msg object(s)" unless unknown.empty?
- end
-
- def close
- @root.ole.close if @close_parent
- end
-
- def attachments
- @attachments ||= @root.children.
- select { |child| child.dir? and child.name =~ ATTACH_RX }.
- map { |child| Attachment.new child }.
- select { |attach| attach.valid? }
- end
-
- def recipients
- @recipients ||= @root.children.
- select { |child| child.dir? and child.name =~ RECIP_RX }.
- map { |child| Recipient.new child }
- end
-
- class Attachment < Mapi::Attachment
- attr_reader :obj, :properties
- alias props :properties
-
- def initialize obj
- @obj = obj
- @embedded_ole = nil
- @embedded_msg = nil
-
- super PropertySet.new(PropertyStore.load(@obj))
- Msg.warn_unknown @obj
-
- @obj.children.each do |child|
- # temp hack. PropertyStore doesn't do directory properties atm - FIXME
- if child.dir? and child.name =~ PropertyStore::SUBSTG_RX and
- $1 == '3701' and $2.downcase == '000d'
- @embedded_ole = child
- class << @embedded_ole
- def compobj
- return nil unless compobj = self["\001CompObj"]
- compobj.read[/^.{32}([^\x00]+)/m, 1]
- end
-
- def embedded_type
- temp = compobj and return temp
- # try to guess more
- if children.select { |child| child.name =~ /__(substg|properties|recip|attach|nameid)/ }.length > 2
- return 'Microsoft Office Outlook Message'
- end
- nil
- end
- end
- if @embedded_ole.embedded_type == 'Microsoft Office Outlook Message'
- @embedded_msg = Msg.new @embedded_ole
- end
- end
- end
- end
-
- def valid?
- # something i started to notice when handling embedded ole object attachments is
- # the particularly strange case where there are empty attachments
- not props.raw.keys.empty?
- end
- end
-
- #
- # +Recipient+ serves as a container for the +recip+ directories in the .msg.
- # It has things like office_location, business_telephone_number, but I don't
- # think enough to make a vCard out of?
- #
- class Recipient < Mapi::Recipient
- attr_reader :obj, :properties
- alias props :properties
-
- def initialize obj
- @obj = obj
- super PropertySet.new(PropertyStore.load(@obj))
- Msg.warn_unknown @obj
- end
- end
- end
-end
-
diff --git a/vendor/ruby-msg/lib/mapi/property_set.rb b/vendor/ruby-msg/lib/mapi/property_set.rb
deleted file mode 100644
index 199bca525..000000000
--- a/vendor/ruby-msg/lib/mapi/property_set.rb
+++ /dev/null
@@ -1,269 +0,0 @@
-require 'yaml'
-require 'mapi/types'
-require 'mapi/rtf'
-require 'rtf'
-
-module Mapi
- #
- # 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.
- #
- class PropertySet
- # the property set guid constants
- # these guids are all defined with the macro DEFINE_OLEGUID in mapiguid.h.
- # see http://doc.ddart.net/msdn/header/include/mapiguid.h.html
- oleguid = proc do |prefix|
- Ole::Types::Clsid.parse "{#{prefix}-0000-0000-c000-000000000046}"
- end
-
- 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',
- }
-
- module Constants
- NAMES.each { |guid, name| const_set name, guid }
- end
-
- include Constants
-
- # +Properties+ are accessed by <tt>Key</tt>s, which are coerced to this class.
- # Includes a bunch of methods (hash, ==, eql?) to allow it to work as a key in
- # a +Hash+.
- #
- # Also contains the code that maps keys to symbolic names.
- class Key
- include Constants
-
- attr_reader :code, :guid
- def initialize code, guid=PS_MAPI
- @code, @guid = code, guid
- end
-
- def to_sym
- # hmmm, for some stuff, like, eg, the message class specific range, sym-ification
- # of the key depends on knowing our message class. i don't want to store anything else
- # here though, so if that kind of thing is needed, it can be passed to this function.
- # worry about that when some examples arise.
- case code
- when Integer
- if guid == PS_MAPI # and < 0x8000 ?
- # the hash should be updated now that i've changed the process
- TAGS['%04x' % code].first[/_(.*)/, 1].downcase.to_sym rescue code
- else
- # handle other guids here, like mapping names to outlook properties, based on the
- # outlook object model.
- NAMED_MAP[self].to_sym rescue code
- end
- when String
- # return something like
- # note that named properties don't go through the map at the moment. so #categories
- # doesn't work yet
- code.downcase.to_sym
- end
- end
-
- def to_s
- to_sym.to_s
- end
-
- # FIXME implement these
- def transmittable?
- # etc, can go here too
- end
-
- # this stuff is to allow it to be a useful key
- def hash
- [code, guid].hash
- end
-
- def == other
- hash == other.hash
- end
-
- alias eql? :==
-
- def inspect
- # maybe the way to do this, would be to be able to register guids
- # in a global lookup, which are used by Clsid#inspect itself, to
- # provide symbolic names...
- guid_str = NAMES[guid] || "{#{guid.format}}" rescue "nil"
- if Integer === code
- hex = '0x%04x' % code
- if guid == PS_MAPI
- # just display as plain hex number
- hex
- else
- "#<Key #{guid_str}/#{hex}>"
- end
- else
- # display full guid and code
- "#<Key #{guid_str}/#{code.inspect}>"
- end
- end
- end
-
- # duplicated here for now
- SUPPORT_DIR = File.dirname(__FILE__) + '/../..'
-
- # data files that provide for the code to symbolic name mapping
- # guids in named_map are really constant references to the above
- TAGS = 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
-
- attr_reader :raw
-
- # +raw+ should be an hash-like object that maps <tt>Key</tt>s to values. Should respond_to?
- # [], keys, values, each, and optionally []=, and delete.
- def initialize raw
- @raw = raw
- end
-
- # resolve +arg+ (could be key, code, string, or symbol), and possible +guid+ to a key.
- # returns nil on failure
- 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
-
- # 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.
- 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
-
- def keys
- sym_to_key.keys
- end
-
- def values
- sym_to_key.values.map { |key| raw[key] }
- end
-
- def [] arg, guid=nil
- raw[resolve(arg, guid)]
- end
-
- 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
-
- 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
-
- def to_h
- sym_to_key.inject({}) { |hash, (sym, key)| hash.update sym => raw[key] }
- end
-
- 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
-
- # -----
-
- # temporary pseudo tags
-
- # for providing rtf to plain text conversion. later, html to text too.
- 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
-
- # for providing rtf decompression
- def body_rtf
- return @body_rtf if defined?(@body_rtf)
- @body_rtf = (RTF.rtfdecompr rtf_compressed.read rescue nil)
- end
-
- # for providing rtf to html conversion
- 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
- end
-end
-
diff --git a/vendor/ruby-msg/lib/mapi/pst.rb b/vendor/ruby-msg/lib/mapi/pst.rb
deleted file mode 100644
index 9ac64b097..000000000
--- a/vendor/ruby-msg/lib/mapi/pst.rb
+++ /dev/null
@@ -1,1806 +0,0 @@
-#
-# = Introduction
-#
-# This file is mostly an attempt to port libpst to ruby, and simplify it in the process. It
-# will leverage much of the existing MAPI => MIME conversion developed for Msg files, and as
-# such is purely concerned with the file structure details.
-#
-# = TODO
-#
-# 1. solve recipient table problem (test4).
-# this is done. turns out it was due to id2 clashes. find better solution
-# 2. check parse consistency. an initial conversion of a 30M file to pst, shows
-# a number of messages conveting badly. compare with libpst too.
-# 3. xattribs
-# 4. generalise the Mapi stuff better
-# 5. refactor index load
-# 6. msg serialization?
-#
-
-=begin
-
-quick plan for cleanup.
-
-have working tests for 97 and 03 file formats, so safe.
-
-want to fix up:
-
-64 bit unpacks scattered around. its ugly. not sure how best to handle it, but am slightly tempted
-to override String#unpack to support a 64 bit little endian unpack (like L vs N/V, for Q). one way or
-another need to fix it. Could really slow everything else down if its parsing the unpack strings twice,
-once in ruby, for every single unpack i do :/
-
-the index loading process, and the lack of shared code between normal vs 64 bit variants, and Index vs Desc.
-should be able to reduce code by factor of 4. also think I should move load code into the class too. then
-maybe have something like:
-
-class Header
- def index_class
- version_2003 ? Index64 : Index
- end
-end
-
-def load_idx
- header.index_class.load_index
-end
-
-OR
-
-def initialize
- @header = ...
- extend @header.index_class::Load
- load_idx
-end
-
-need to think about the role of the mapi code, and Pst::Item etc, but that layer can come later.
-
-=end
-
-require 'mapi'
-require 'enumerator'
-require 'ostruct'
-require 'ole/ranges_io'
-
-module Mapi
-class Pst
- class FormatError < StandardError
- end
-
- # unfortunately there is no Q analogue which is little endian only.
- # this translates T as an unsigned quad word, little endian byte order, to
- # not pollute the rest of the code.
- #
- # didn't want to override String#unpack, cause its too hacky, and incomplete.
- def self.unpack str, unpack_spec
- return str.unpack(unpack_spec) unless unpack_spec['T']
- @unpack_cache ||= {}
- t_offsets, new_spec = @unpack_cache[unpack_spec]
- unless t_offsets
- t_offsets = []
- offset = 0
- new_spec = ''
- unpack_spec.scan(/([^\d])_?(\*|\d+)?/o) do
- num_elems = $1.downcase == 'a' ? 1 : ($2 || 1).to_i
- if $1 == 'T'
- num_elems.times { |i| t_offsets << offset + i }
- new_spec << "V#{num_elems * 2}"
- else
- new_spec << $~[0]
- end
- offset += num_elems
- end
- @unpack_cache[unpack_spec] = [t_offsets, new_spec]
- end
- a = str.unpack(new_spec)
- t_offsets.each do |offset|
- low, high = a[offset, 2]
- a[offset, 2] = low && high ? low + (high << 32) : nil
- end
- a
- end
-
- #
- # this is the header and encryption encapsulation code
- # ----------------------------------------------------------------------------
- #
-
- # class which encapsulates the pst header
- class Header
- SIZE = 512
- MAGIC = 0x2142444e
-
- # these are the constants defined in libpst.c, that
- # are referenced in pst_open()
- INDEX_TYPE_OFFSET = 0x0A
- FILE_SIZE_POINTER = 0xA8
- FILE_SIZE_POINTER_64 = 0xB8
- SECOND_POINTER = 0xBC
- INDEX_POINTER = 0xC4
- SECOND_POINTER_64 = 0xE0
- INDEX_POINTER_64 = 0xF0
- ENC_OFFSET = 0x1CD
-
- attr_reader :magic, :index_type, :encrypt_type, :size
- attr_reader :index1_count, :index1, :index2_count, :index2
- attr_reader :version
- def initialize data
- @magic = data.unpack('N')[0]
- @index_type = data[INDEX_TYPE_OFFSET]
- @version = {0x0e => 1997, 0x17 => 2003}[@index_type]
-
- if version_2003?
- # don't know?
- # >> data1.unpack('V*').zip(data2.unpack('V*')).enum_with_index.select { |(c, d), i| c != d and not [46, 56, 60].include?(i) }.select { |(a, b), i| b == 0 }.map { |(a, b), i| [a / 256, i] }
- # [8, 76], [32768, 84], [128, 89]
- # >> data1.unpack('C*').zip(data2.unpack('C*')).enum_with_index.select { |(c, d), i| c != d and not [184..187, 224..227, 240..243].any? { |r| r === i } }.select { |(a, b), i| b == 0 and ((Math.log(a) / Math.log(2)) % 1) < 0.0001 }
- # [[[2, 0], 61], [[2, 0], 76], [[2, 0], 195], [[2, 0], 257], [[8, 0], 305], [[128, 0], 338], [[128, 0], 357]]
- # i have only 2 psts to base this guess on, so i can't really come up with anything that looks reasonable yet. not sure what the offset is. unfortunately there is so much in the header
- # that isn't understood...
- @encrypt_type = 1
-
- @index2_count, @index2 = data[SECOND_POINTER_64 - 4, 8].unpack('V2')
- @index1_count, @index1 = data[INDEX_POINTER_64 - 4, 8].unpack('V2')
-
- @size = data[FILE_SIZE_POINTER_64, 4].unpack('V')[0]
- else
- @encrypt_type = data[ENC_OFFSET]
-
- @index2_count, @index2 = data[SECOND_POINTER - 4, 8].unpack('V2')
- @index1_count, @index1 = data[INDEX_POINTER - 4, 8].unpack('V2')
-
- @size = data[FILE_SIZE_POINTER, 4].unpack('V')[0]
- end
-
- validate!
- end
-
- def version_2003?
- version == 2003
- end
-
- def encrypted?
- encrypt_type != 0
- end
-
- def validate!
- raise FormatError, "bad signature on pst file (#{'0x%x' % magic})" unless magic == MAGIC
- raise FormatError, "only index types 0x0e and 0x17 are handled (#{'0x%x' % index_type})" unless [0x0e, 0x17].include?(index_type)
- raise FormatError, "only encrytion types 0 and 1 are handled (#{encrypt_type.inspect})" unless [0, 1].include?(encrypt_type)
- end
- end
-
- # compressible encryption! :D
- #
- # simple substitution. see libpst.c
- # maybe test switch to using a String#tr!
- class CompressibleEncryption
- DECRYPT_TABLE = [
- 0x47, 0xf1, 0xb4, 0xe6, 0x0b, 0x6a, 0x72, 0x48,
- 0x85, 0x4e, 0x9e, 0xeb, 0xe2, 0xf8, 0x94, 0x53, # 0x0f
- 0xe0, 0xbb, 0xa0, 0x02, 0xe8, 0x5a, 0x09, 0xab,
- 0xdb, 0xe3, 0xba, 0xc6, 0x7c, 0xc3, 0x10, 0xdd, # 0x1f
- 0x39, 0x05, 0x96, 0x30, 0xf5, 0x37, 0x60, 0x82,
- 0x8c, 0xc9, 0x13, 0x4a, 0x6b, 0x1d, 0xf3, 0xfb, # 0x2f
- 0x8f, 0x26, 0x97, 0xca, 0x91, 0x17, 0x01, 0xc4,
- 0x32, 0x2d, 0x6e, 0x31, 0x95, 0xff, 0xd9, 0x23, # 0x3f
- 0xd1, 0x00, 0x5e, 0x79, 0xdc, 0x44, 0x3b, 0x1a,
- 0x28, 0xc5, 0x61, 0x57, 0x20, 0x90, 0x3d, 0x83, # 0x4f
- 0xb9, 0x43, 0xbe, 0x67, 0xd2, 0x46, 0x42, 0x76,
- 0xc0, 0x6d, 0x5b, 0x7e, 0xb2, 0x0f, 0x16, 0x29, # 0x5f
- 0x3c, 0xa9, 0x03, 0x54, 0x0d, 0xda, 0x5d, 0xdf,
- 0xf6, 0xb7, 0xc7, 0x62, 0xcd, 0x8d, 0x06, 0xd3, # 0x6f
- 0x69, 0x5c, 0x86, 0xd6, 0x14, 0xf7, 0xa5, 0x66,
- 0x75, 0xac, 0xb1, 0xe9, 0x45, 0x21, 0x70, 0x0c, # 0x7f
- 0x87, 0x9f, 0x74, 0xa4, 0x22, 0x4c, 0x6f, 0xbf,
- 0x1f, 0x56, 0xaa, 0x2e, 0xb3, 0x78, 0x33, 0x50, # 0x8f
- 0xb0, 0xa3, 0x92, 0xbc, 0xcf, 0x19, 0x1c, 0xa7,
- 0x63, 0xcb, 0x1e, 0x4d, 0x3e, 0x4b, 0x1b, 0x9b, # 0x9f
- 0x4f, 0xe7, 0xf0, 0xee, 0xad, 0x3a, 0xb5, 0x59,
- 0x04, 0xea, 0x40, 0x55, 0x25, 0x51, 0xe5, 0x7a, # 0xaf
- 0x89, 0x38, 0x68, 0x52, 0x7b, 0xfc, 0x27, 0xae,
- 0xd7, 0xbd, 0xfa, 0x07, 0xf4, 0xcc, 0x8e, 0x5f, # 0xbf
- 0xef, 0x35, 0x9c, 0x84, 0x2b, 0x15, 0xd5, 0x77,
- 0x34, 0x49, 0xb6, 0x12, 0x0a, 0x7f, 0x71, 0x88, # 0xcf
- 0xfd, 0x9d, 0x18, 0x41, 0x7d, 0x93, 0xd8, 0x58,
- 0x2c, 0xce, 0xfe, 0x24, 0xaf, 0xde, 0xb8, 0x36, # 0xdf
- 0xc8, 0xa1, 0x80, 0xa6, 0x99, 0x98, 0xa8, 0x2f,
- 0x0e, 0x81, 0x65, 0x73, 0xe4, 0xc2, 0xa2, 0x8a, # 0xef
- 0xd4, 0xe1, 0x11, 0xd0, 0x08, 0x8b, 0x2a, 0xf2,
- 0xed, 0x9a, 0x64, 0x3f, 0xc1, 0x6c, 0xf9, 0xec # 0xff
- ]
-
- ENCRYPT_TABLE = [nil] * 256
- DECRYPT_TABLE.each_with_index { |i, j| ENCRYPT_TABLE[i] = j }
-
- def self.decrypt_alt encrypted
- decrypted = ''
- encrypted.length.times { |i| decrypted << DECRYPT_TABLE[encrypted[i]] }
- decrypted
- end
-
- def self.encrypt_alt decrypted
- encrypted = ''
- decrypted.length.times { |i| encrypted << ENCRYPT_TABLE[decrypted[i]] }
- encrypted
- end
-
- # an alternate implementation that is possibly faster....
- # TODO - bench
- DECRYPT_STR, ENCRYPT_STR = [DECRYPT_TABLE, (0...256)].map do |values|
- values.map { |i| i.chr }.join.gsub(/([\^\-\\])/, "\\\\\\1")
- end
-
- def self.decrypt encrypted
- encrypted.tr ENCRYPT_STR, DECRYPT_STR
- end
-
- def self.encrypt decrypted
- decrypted.tr DECRYPT_STR, ENCRYPT_STR
- end
- end
-
- class RangesIOEncryptable < RangesIO
- def initialize io, mode='r', params={}
- mode, params = 'r', mode if Hash === mode
- @decrypt = !!params[:decrypt]
- super
- end
-
- def encrypted?
- @decrypt
- end
-
- def read limit=nil
- buf = super
- buf = CompressibleEncryption.decrypt(buf) if encrypted?
- buf
- end
- end
-
- attr_reader :io, :header, :idx, :desc, :special_folder_ids
-
- # corresponds to
- # * pst_open
- # * pst_load_index
- def initialize io
- @io = io
- io.pos = 0
- @header = Header.new io.read(Header::SIZE)
-
- # would prefer this to be in Header#validate, but it doesn't have the io size.
- # should perhaps downgrade this to just be a warning...
- raise FormatError, "header size field invalid (#{header.size} != #{io.size}}" unless header.size == io.size
-
- load_idx
- load_desc
- load_xattrib
-
- @special_folder_ids = {}
- end
-
- def encrypted?
- @header.encrypted?
- end
-
- # until i properly fix logging...
- def warn s
- Mapi::Log.warn s
- end
-
- #
- # this is the index and desc record loading code
- # ----------------------------------------------------------------------------
- #
-
- ToTree = Module.new
-
- module Index2
- BLOCK_SIZE = 512
- module RecursiveLoad
- def load_chain
- #...
- end
- end
-
- module Base
- def read
- #...
- end
- end
-
- class Version1997 < Struct.new(:a)#...)
- SIZE = 12
-
- include RecursiveLoad
- include Base
- end
-
- class Version2003 < Struct.new(:a)#...)
- SIZE = 24
-
- include RecursiveLoad
- include Base
- end
- end
-
- module Desc2
- module Base
- def desc
- #...
- end
- end
-
- class Version1997 < Struct.new(:a)#...)
- #include Index::RecursiveLoad
- include Base
- end
-
- class Version2003 < Struct.new(:a)#...)
- #include Index::RecursiveLoad
- include Base
- end
- end
-
- # more constants from libpst.c
- # these relate to the index block
- ITEM_COUNT_OFFSET = 0x1f0 # count byte
- LEVEL_INDICATOR_OFFSET = 0x1f3 # node or leaf
- BACKLINK_OFFSET = 0x1f8 # backlink u1 value
-
- # these 3 classes are used to hold various file records
-
- # pst_index
- class Index < Struct.new(:id, :offset, :size, :u1)
- UNPACK_STR = 'VVvv'
- SIZE = 12
- BLOCK_SIZE = 512 # index blocks was 516 but bogus
- COUNT_MAX = 41 # max active items (ITEM_COUNT_OFFSET / Index::SIZE = 41)
-
- attr_accessor :pst
- def initialize data
- data = Pst.unpack data, UNPACK_STR if String === data
- super(*data)
- end
-
- def type
- @type ||= begin
- if id & 0x2 == 0
- :data
- else
- first_byte, second_byte = read.unpack('CC')
- if first_byte == 1
- raise second_byte unless second_byte == 1
- :data_chain_header
- elsif first_byte == 2
- raise second_byte unless second_byte == 0
- :id2_assoc
- else
- raise FormatError, 'unknown first byte for block - %p' % first_byte
- end
- end
- end
- end
-
- def data?
- (id & 0x2) == 0
- end
-
- def read decrypt=true
- # only data blocks are every encrypted
- decrypt = false unless data?
- pst.pst_read_block_size offset, size, decrypt
- end
-
- # show all numbers in hex
- def inspect
- super.gsub(/=(\d+)/) { '=0x%x' % $1.to_i }.sub(/Index /, "Index type=#{type.inspect}, ")
- end
- end
-
- # mostly guesses.
- ITEM_COUNT_OFFSET_64 = 0x1e8
- LEVEL_INDICATOR_OFFSET_64 = 0x1eb # diff of 3 between these 2 as above...
-
- # will maybe inherit from Index64, in order to get the same #type function.
- class Index64 < Index
- UNPACK_STR = 'TTvvV'
- SIZE = 24
- BLOCK_SIZE = 512
- COUNT_MAX = 20 # bit of a guess really. 512 / 24 = 21, but doesn't leave enough header room
-
- # this is the extra item on the end of the UNPACK_STR above
- attr_accessor :u2
-
- def initialize data
- data = Pst.unpack data, UNPACK_STR if String === data
- @u2 = data.pop
- super data
- end
-
- def inspect
- super.sub(/>$/, ', u2=%p>' % u2)
- end
-
- def self.load_chain io, header
- load_idx_rec io, header.index1, 0, 0
- end
-
- # almost identical to load code for Index, just different offsets and unpack strings.
- # can probably merge them, or write a generic load_tree function or something.
- def self.load_idx_rec io, offset, linku1, start_val
- io.seek offset
- buf = io.read BLOCK_SIZE
- idxs = []
-
- item_count = buf[ITEM_COUNT_OFFSET_64]
- raise "have too many active items in index (#{item_count})" if item_count > COUNT_MAX
-
- #idx = Index.new buf[BACKLINK_OFFSET, Index::SIZE]
- #raise 'blah 1' unless idx.id == linku1
-
- if buf[LEVEL_INDICATOR_OFFSET_64] == 0
- # leaf pointers
- # split the data into item_count index objects
- buf[0, SIZE * item_count].scan(/.{#{SIZE}}/mo).each_with_index do |data, i|
- idx = new data
- # first entry
- raise 'blah 3' if i == 0 and start_val != 0 and idx.id != start_val
- #idx.pst = self
- break if idx.id == 0
- idxs << idx
- end
- else
- # node pointers
- # split the data into item_count table pointers
- buf[0, SIZE * item_count].scan(/.{#{SIZE}}/mo).each_with_index do |data, i|
- start, u1, offset = Pst.unpack data, 'T3'
- # for the first value, we expect the start to be equal
- raise 'blah 3' if i == 0 and start_val != 0 and start != start_val
- break if start == 0
- idxs += load_idx_rec io, offset, u1, start
- end
- end
-
- idxs
- end
- end
-
- # pst_desc
- class Desc64 < Struct.new(:desc_id, :idx_id, :idx2_id, :parent_desc_id, :u2)
- UNPACK_STR = 'T3VV'
- SIZE = 32
- BLOCK_SIZE = 512 # descriptor blocks was 520 but bogus
- COUNT_MAX = 15 # guess as per Index64
-
- include RecursivelyEnumerable
-
- attr_accessor :pst
- attr_reader :children
- def initialize data
- super(*Pst.unpack(data, UNPACK_STR))
- @children = []
- end
-
- def desc
- pst.idx_from_id idx_id
- end
-
- def list_index
- pst.idx_from_id idx2_id
- end
-
- def self.load_chain io, header
- load_desc_rec io, header.index2, 0, 0x21
- end
-
- def self.load_desc_rec io, offset, linku1, start_val
- io.seek offset
- buf = io.read BLOCK_SIZE
- descs = []
- item_count = buf[ITEM_COUNT_OFFSET_64]
-
- # not real desc
- #desc = Desc.new buf[BACKLINK_OFFSET, 4]
- #raise 'blah 1' unless desc.desc_id == linku1
-
- if buf[LEVEL_INDICATOR_OFFSET_64] == 0
- # leaf pointers
- raise "have too many active items in index (#{item_count})" if item_count > COUNT_MAX
- # split the data into item_count desc objects
- buf[0, SIZE * item_count].scan(/.{#{SIZE}}/mo).each_with_index do |data, i|
- desc = new data
- # first entry
- raise 'blah 3' if i == 0 and start_val != 0 and desc.desc_id != start_val
- break if desc.desc_id == 0
- descs << desc
- end
- else
- # node pointers
- raise "have too many active items in index (#{item_count})" if item_count > Index64::COUNT_MAX
- # split the data into item_count table pointers
- buf[0, Index64::SIZE * item_count].scan(/.{#{Index64::SIZE}}/mo).each_with_index do |data, i|
- start, u1, offset = Pst.unpack data, 'T3'
- # for the first value, we expect the start to be equal note that ids -1, so even for the
- # first we expect it to be equal. thats the 0x21 (dec 33) desc record. this means we assert
- # that the first desc record is always 33...
- # thats because 0x21 is the pst root itself...
- raise 'blah 3' if i == 0 and start_val != -1 and start != start_val
- # this shouldn't really happen i'd imagine
- break if start == 0
- descs += load_desc_rec io, offset, u1, start
- end
- end
-
- descs
- end
-
- def each_child(&block)
- @children.each(&block)
- end
- end
-
- # _pst_table_ptr_struct
- class TablePtr < Struct.new(:start, :u1, :offset)
- UNPACK_STR = 'V3'
- SIZE = 12
-
- def initialize data
- data = data.unpack(UNPACK_STR) if String === data
- super(*data)
- end
- end
-
- # pst_desc
- # idx_id is a pointer to an idx record which gets the primary data stream for the Desc record.
- # idx2_id gets you an idx record, that when read gives you an ID2 association list, which just maps
- # another set of ids to index values
- class Desc < Struct.new(:desc_id, :idx_id, :idx2_id, :parent_desc_id)
- UNPACK_STR = 'V4'
- SIZE = 16
- BLOCK_SIZE = 512 # descriptor blocks was 520 but bogus
- COUNT_MAX = 31 # max active desc records (ITEM_COUNT_OFFSET / Desc::SIZE = 31)
-
- include ToTree
-
- attr_accessor :pst
- attr_reader :children
- def initialize data
- super(*data.unpack(UNPACK_STR))
- @children = []
- end
-
- def desc
- pst.idx_from_id idx_id
- end
-
- def list_index
- pst.idx_from_id idx2_id
- end
-
- # show all numbers in hex
- def inspect
- super.gsub(/=(\d+)/) { '=0x%x' % $1.to_i }
- end
- end
-
- # corresponds to
- # * _pst_build_id_ptr
- def load_idx
- @idx = []
- @idx_offsets = []
- if header.version_2003?
- @idx = Index64.load_chain io, header
- @idx.each { |idx| idx.pst = self }
- else
- load_idx_rec header.index1, header.index1_count, 0
- end
-
- # we'll typically be accessing by id, so create a hash as a lookup cache
- @idx_from_id = {}
- @idx.each do |idx|
- warn "there are duplicate idx records with id #{idx.id}" if @idx_from_id[idx.id]
- @idx_from_id[idx.id] = idx
- end
- end
-
- # load the flat idx table, which maps ids to file ranges. this is the recursive helper
- #
- # corresponds to
- # * _pst_build_id_ptr
- def load_idx_rec offset, linku1, start_val
- @idx_offsets << offset
-
- #_pst_read_block_size(pf, offset, BLOCK_SIZE, &buf, 0, 0) < BLOCK_SIZE)
- buf = pst_read_block_size offset, Index::BLOCK_SIZE, false
-
- item_count = buf[ITEM_COUNT_OFFSET]
- raise "have too many active items in index (#{item_count})" if item_count > Index::COUNT_MAX
-
- idx = Index.new buf[BACKLINK_OFFSET, Index::SIZE]
- raise 'blah 1' unless idx.id == linku1
-
- if buf[LEVEL_INDICATOR_OFFSET] == 0
- # leaf pointers
- # split the data into item_count index objects
- buf[0, Index::SIZE * item_count].scan(/.{#{Index::SIZE}}/mo).each_with_index do |data, i|
- idx = Index.new data
- # first entry
- raise 'blah 3' if i == 0 and start_val != 0 and idx.id != start_val
- idx.pst = self
- # this shouldn't really happen i'd imagine
- break if idx.id == 0
- @idx << idx
- end
- else
- # node pointers
- # split the data into item_count table pointers
- buf[0, TablePtr::SIZE * item_count].scan(/.{#{TablePtr::SIZE}}/mo).each_with_index do |data, i|
- table = TablePtr.new data
- # for the first value, we expect the start to be equal
- raise 'blah 3' if i == 0 and start_val != 0 and table.start != start_val
- # this shouldn't really happen i'd imagine
- break if table.start == 0
- load_idx_rec table.offset, table.u1, table.start
- end
- end
- end
-
- # most access to idx objects will use this function
- #
- # corresponds to
- # * _pst_getID
- def idx_from_id id
- @idx_from_id[id]
- end
-
- # corresponds to
- # * _pst_build_desc_ptr
- # * record_descriptor
- def load_desc
- @desc = []
- @desc_offsets = []
- if header.version_2003?
- @desc = Desc64.load_chain io, header
- @desc.each { |desc| desc.pst = self }
- else
- load_desc_rec header.index2, header.index2_count, 0x21
- end
-
- # first create a lookup cache
- @desc_from_id = {}
- @desc.each do |desc|
- desc.pst = self
- warn "there are duplicate desc records with id #{desc.desc_id}" if @desc_from_id[desc.desc_id]
- @desc_from_id[desc.desc_id] = desc
- end
-
- # now turn the flat list of loaded desc records into a tree
-
- # well, they have no parent, so they're more like, the toplevel descs.
- @orphans = []
- # now assign each node to the parents child array, putting the orphans in the above
- @desc.each do |desc|
- parent = @desc_from_id[desc.parent_desc_id]
- # note, besides this, its possible to create other circular structures.
- if parent == desc
- # this actually happens usually, for the root_item it appears.
- #warn "desc record's parent is itself (#{desc.inspect})"
- # maybe add some more checks in here for circular structures
- elsif parent
- parent.children << desc
- next
- end
- @orphans << desc
- end
-
- # maybe change this to some sort of sane-ness check. orphans are expected
-# warn "have #{@orphans.length} orphan desc record(s)." unless @orphans.empty?
- end
-
- # load the flat list of desc records recursively
- #
- # corresponds to
- # * _pst_build_desc_ptr
- # * record_descriptor
- def load_desc_rec offset, linku1, start_val
- @desc_offsets << offset
-
- buf = pst_read_block_size offset, Desc::BLOCK_SIZE, false
- item_count = buf[ITEM_COUNT_OFFSET]
-
- # not real desc
- desc = Desc.new buf[BACKLINK_OFFSET, 4]
- raise 'blah 1' unless desc.desc_id == linku1
-
- if buf[LEVEL_INDICATOR_OFFSET] == 0
- # leaf pointers
- raise "have too many active items in index (#{item_count})" if item_count > Desc::COUNT_MAX
- # split the data into item_count desc objects
- buf[0, Desc::SIZE * item_count].scan(/.{#{Desc::SIZE}}/mo).each_with_index do |data, i|
- desc = Desc.new data
- # first entry
- raise 'blah 3' if i == 0 and start_val != 0 and desc.desc_id != start_val
- # this shouldn't really happen i'd imagine
- break if desc.desc_id == 0
- @desc << desc
- end
- else
- # node pointers
- raise "have too many active items in index (#{item_count})" if item_count > Index::COUNT_MAX
- # split the data into item_count table pointers
- buf[0, TablePtr::SIZE * item_count].scan(/.{#{TablePtr::SIZE}}/mo).each_with_index do |data, i|
- table = TablePtr.new data
- # for the first value, we expect the start to be equal note that ids -1, so even for the
- # first we expect it to be equal. thats the 0x21 (dec 33) desc record. this means we assert
- # that the first desc record is always 33...
- raise 'blah 3' if i == 0 and start_val != -1 and table.start != start_val
- # this shouldn't really happen i'd imagine
- break if table.start == 0
- load_desc_rec table.offset, table.u1, table.start
- end
- end
- end
-
- # as for idx
- #
- # corresponds to:
- # * _pst_getDptr
- def desc_from_id id
- @desc_from_id[id]
- end
-
- # corresponds to
- # * pst_load_extended_attributes
- def load_xattrib
- unless desc = desc_from_id(0x61)
- warn "no extended attributes desc record found"
- return
- end
- unless desc.desc
- warn "no desc idx for extended attributes"
- return
- end
- if desc.list_index
- end
- #warn "skipping loading xattribs"
- # FIXME implement loading xattribs
- end
-
- # corresponds to:
- # * _pst_read_block_size
- # * _pst_read_block ??
- # * _pst_ff_getIDblock_dec ??
- # * _pst_ff_getIDblock ??
- def pst_read_block_size offset, size, decrypt=true
- io.seek offset
- buf = io.read size
- warn "tried to read #{size} bytes but only got #{buf.length}" if buf.length != size
- encrypted? && decrypt ? CompressibleEncryption.decrypt(buf) : buf
- end
-
- #
- # id2
- # ----------------------------------------------------------------------------
- #
-
- class ID2Assoc < Struct.new(:id2, :id, :table2)
- UNPACK_STR = 'V3'
- SIZE = 12
-
- def initialize data
- data = data.unpack(UNPACK_STR) if String === data
- super(*data)
- end
- end
-
- class ID2Assoc64 < Struct.new(:id2, :u1, :id, :table2)
- UNPACK_STR = 'VVT2'
- SIZE = 24
-
- def initialize data
- if String === data
- data = Pst.unpack data, UNPACK_STR
- end
- super(*data)
- end
-
- def self.load_chain idx
- buf = idx.read
- type, count = buf.unpack 'v2'
- unless type == 0x0002
- raise 'unknown id2 type 0x%04x' % type
- #return
- end
- id2 = []
- count.times do |i|
- assoc = new buf[8 + SIZE * i, SIZE]
- id2 << assoc
- if assoc.table2 != 0
- id2 += load_chain idx.pst.idx_from_id(assoc.table2)
- end
- end
- id2
- end
- end
-
- class ID2Mapping
- attr_reader :list
- def initialize pst, list
- @pst = pst
- @list = list
- # create a lookup.
- @id_from_id2 = {}
- @list.each do |id2|
- # NOTE we take the last value seen value if there are duplicates. this "fixes"
- # test4-o1997.pst for the time being.
- warn "there are duplicate id2 records with id #{id2.id2}" if @id_from_id2[id2.id2]
- next if @id_from_id2[id2.id2]
- @id_from_id2[id2.id2] = id2.id
- end
- end
-
- # TODO: fix logging
- def warn s
- Mapi::Log.warn s
- end
-
- # corresponds to:
- # * _pst_getID2
- def [] id
- #id2 = @list.find { |x| x.id2 == id }
- id = @id_from_id2[id]
- id and @pst.idx_from_id(id)
- end
- end
-
- def load_idx2 idx
- if header.version_2003?
- id2 = ID2Assoc64.load_chain idx
- else
- id2 = load_idx2_rec idx
- end
- ID2Mapping.new self, id2
- end
-
- # corresponds to
- # * _pst_build_id2
- def load_idx2_rec idx
- # i should perhaps use a idx chain style read here?
- buf = pst_read_block_size idx.offset, idx.size, false
- type, count = buf.unpack 'v2'
- unless type == 0x0002
- raise 'unknown id2 type 0x%04x' % type
- #return
- end
- id2 = []
- count.times do |i|
- assoc = ID2Assoc.new buf[4 + ID2Assoc::SIZE * i, ID2Assoc::SIZE]
- id2 << assoc
- if assoc.table2 != 0
- id2 += load_idx2_rec idx_from_id(assoc.table2)
- end
- end
- id2
- end
-
- class RangesIOIdxChain < RangesIOEncryptable
- def initialize pst, idx_head
- @idxs = pst.id2_block_idx_chain idx_head
- # whether or not a given idx needs encrypting
- decrypts = @idxs.map do |idx|
- decrypt = (idx.id & 2) != 0 ? false : pst.encrypted?
- end.uniq
- raise NotImplementedError, 'partial encryption in RangesIOID2' if decrypts.length > 1
- decrypt = decrypts.first
- # convert idxs to ranges
- ranges = @idxs.map { |idx| [idx.offset, idx.size] }
- super pst.io, :ranges => ranges, :decrypt => decrypt
- end
- end
-
- class RangesIOID2 < RangesIOIdxChain
- def self.new pst, id2, idx2
- RangesIOIdxChain.new pst, idx2[id2]
- end
- end
-
- # corresponds to:
- # * _pst_ff_getID2block
- # * _pst_ff_getID2data
- # * _pst_ff_compile_ID
- def id2_block_idx_chain idx
- if (idx.id & 0x2) == 0
- [idx]
- else
- buf = idx.read
- type, fdepth, count = buf[0, 4].unpack 'CCv'
- unless type == 1 # libpst.c:3958
- warn 'Error in idx_chain - %p, %p, %p - attempting to ignore' % [type, fdepth, count]
- return [idx]
- end
- # there are 4 unaccounted for bytes here, 4...8
- if header.version_2003?
- ids = buf[8, count * 8].unpack("T#{count}")
- else
- ids = buf[8, count * 4].unpack('V*')
- end
- if fdepth == 1
- ids.map { |id| idx_from_id id }
- else
- ids.map { |id| id2_block_idx_chain idx_from_id(id) }.flatten
- end
- end
- end
-
- #
- # main block parsing code. gets raw properties
- # ----------------------------------------------------------------------------
- #
-
- # the job of this class, is to take a desc record, and be able to enumerate through the
- # mapi properties of the associated thing.
- #
- # corresponds to
- # * _pst_parse_block
- # * _pst_process (in some ways. although perhaps thats more the Item::Properties#add_property)
- class BlockParser
- include Mapi::Types::Constants
-
- TYPES = {
- 0xbcec => 1,
- 0x7cec => 2,
- # type 3 is removed. an artifact of not handling the indirect blocks properly in libpst.
- }
-
- PR_SUBJECT = PropertySet::TAGS.find { |num, (name, type)| name == 'PR_SUBJECT' }.first.hex
- PR_BODY_HTML = PropertySet::TAGS.find { |num, (name, type)| name == 'PR_BODY_HTML' }.first.hex
-
- # this stuff could maybe be moved to Ole::Types? or leverage it somehow?
- # whether or not a type is immeidate is more a property of the pst encoding though i expect.
- # what i probably can add is a generic concept of whether a type is of variadic length or not.
-
- # these lists are very incomplete. think they are largely copied from libpst
-
- IMMEDIATE_TYPES = [
- PT_SHORT, PT_LONG, PT_BOOLEAN
- ]
-
- INDIRECT_TYPES = [
- PT_DOUBLE, PT_OBJECT,
- 0x0014, # whats this? probably something like PT_LONGLONG, given the correspondence with the
- # ole variant types. (= VT_I8)
- PT_STRING8, PT_UNICODE, # unicode isn't in libpst, but added here for outlook 2003 down the track
- PT_SYSTIME,
- 0x0048, # another unknown
- 0x0102, # this is PT_BINARY vs PT_CLSID
- #0x1003, # these are vector types, but they're commented out for now because i'd expect that
- #0x1014, # there's extra decoding needed that i'm not doing. (probably just need a simple
- # # PT_* => unpack string mapping for the immediate types, and just do unpack('V*') etc
- #0x101e,
- #0x1102
- ]
-
- # the attachment and recipient arrays appear to be always stored with these fixed
- # id2 values. seems strange. are there other extra streams? can find out by making higher
- # level IO wrapper, which has the id2 value, and doing the diff of available id2 values versus
- # used id2 values in properties of an item.
- ID2_ATTACHMENTS = 0x671
- ID2_RECIPIENTS = 0x692
-
- attr_reader :desc, :data, :data_chunks, :offset_tables
- def initialize desc
- raise FormatError, "unable to get associated index record for #{desc.inspect}" unless desc.desc
- @desc = desc
- #@data = desc.desc.read
- if Pst::Index === desc.desc
- #@data = RangesIOIdxChain.new(desc.pst, desc.desc).read
- idxs = desc.pst.id2_block_idx_chain desc.desc
- # this gets me the plain index chain.
- else
- # fake desc
- #@data = desc.desc.read
- idxs = [desc.desc]
- end
-
- @data_chunks = idxs.map { |idx| idx.read }
- @data = @data_chunks.first
-
- load_header
-
- @index_offsets = [@index_offset] + @data_chunks[1..-1].map { |chunk| chunk.unpack('v')[0] }
- @offset_tables = []
- @ignored = []
- @data_chunks.zip(@index_offsets).each do |chunk, offset|
- ignore = chunk[offset, 2].unpack('v')[0]
- @ignored << ignore
-# p ignore
- @offset_tables.push offset_table = []
- # maybe its ok if there aren't to be any values ?
- raise FormatError if offset == 0
- offsets = chunk[offset + 2..-1].unpack('v*')
- #p offsets
- offsets[0, ignore + 2].each_cons 2 do |from, to|
- #next if to == 0
- raise FormatError, [from, to].inspect if from > to
- offset_table << [from, to]
- end
- end
-
- @offset_table = @offset_tables.first
- @idxs = idxs
-
- # now, we may have multiple different blocks
- end
-
- # a given desc record may or may not have associated idx2 data. we lazily load it here, so it will never
- # actually be requested unless get_data_indirect actually needs to use it.
- def idx2
- return @idx2 if @idx2
- raise FormatError, 'idx2 requested but no idx2 available' unless desc.list_index
- # should check this can't return nil
- @idx2 = desc.pst.load_idx2 desc.list_index
- end
-
- def load_header
- @index_offset, type, @offset1 = data.unpack 'vvV'
- raise FormatError, 'unknown block type signature 0x%04x' % type unless TYPES[type]
- @type = TYPES[type]
- end
-
- # based on the value of offset, return either some data from buf, or some data from the
- # id2 chain id2, where offset is some key into a lookup table that is stored as the id2
- # chain. i think i may need to create a BlockParser class that wraps up all this mess.
- #
- # corresponds to:
- # * _pst_getBlockOffsetPointer
- # * _pst_getBlockOffset
- def get_data_indirect offset
- return get_data_indirect_io(offset).read
-
- if offset == 0
- nil
- elsif (offset & 0xf) == 0xf
- RangesIOID2.new(desc.pst, offset, idx2).read
- else
- low, high = offset & 0xf, offset >> 4
- raise FormatError if low != 0 or (high & 0x1) != 0 or (high / 2) > @offset_table.length
- from, to = @offset_table[high / 2]
- data[from...to]
- end
- end
-
- def get_data_indirect_io offset
- if offset == 0
- nil
- elsif (offset & 0xf) == 0xf
- if idx2[offset]
- RangesIOID2.new desc.pst, offset, idx2
- else
- warn "tried to get idx2 record for #{offset} but failed"
- return StringIO.new('')
- end
- else
- low, high = offset & 0xf, offset >> 4
- if low != 0 or (high & 0x1) != 0
-# raise FormatError,
- warn "bad - #{low} #{high} (1)"
- return StringIO.new('')
- end
- # lets see which block it should come from.
- block_idx, i = high.divmod 4096
- unless block_idx < @data_chunks.length
- warn "bad - block_idx to high (not #{block_idx} < #{@data_chunks.length})"
- return StringIO.new('')
- end
- data_chunk, offset_table = @data_chunks[block_idx], @offset_tables[block_idx]
- if i / 2 >= offset_table.length
- warn "bad - #{low} #{high} - #{i / 2} >= #{offset_table.length} (2)"
- return StringIO.new('')
- end
- #warn "ok - #{low} #{high} #{offset_table.length}"
- from, to = offset_table[i / 2]
- StringIO.new data_chunk[from...to]
- end
- end
-
- def handle_indirect_values key, type, value
- case type
- when PT_BOOLEAN
- value = value != 0
- when *IMMEDIATE_TYPES # not including PT_BOOLEAN which we just did above
- # no processing current applied (needed?).
- when *INDIRECT_TYPES
- # the value is a pointer
- if String === value # ie, value size > 4 above
- value = StringIO.new value
- else
- value = get_data_indirect_io(value)
- end
- # keep strings as immediate values for now, for compatability with how i set up
- # Msg::Properties::ENCODINGS
- if value
- if type == PT_STRING8
- value = value.read
- elsif type == PT_UNICODE
- value = Ole::Types::FROM_UTF16.iconv value.read
- end
- end
- # special subject handling
- if key == PR_BODY_HTML and value
- # to keep the msg code happy, which thinks body_html will be an io
- # although, in 2003 version, they are 0102 already
- value = StringIO.new value unless value.respond_to?(:read)
- end
- if key == PR_SUBJECT and value
- ignore, offset = value.unpack 'C2'
- offset = (offset == 1 ? nil : offset - 3)
- value = value[2..-1]
-=begin
- index = value =~ /^[A-Z]*:/ ? $~[0].length - 1 : nil
- unless ignore == 1 and offset == index
- warn 'something wrong with subject hack'
- $x = [ignore, offset, value]
- require 'irb'
- IRB.start
- exit
- end
-=end
-=begin
-new idea:
-
-making sense of the \001\00[156] i've seen prefixing subject. i think its to do with the placement
-of the ':', or the ' '. And perhaps an optimization to do with thread topic, and ignoring the prefixes
-added by mailers. thread topic is equal to subject with all that crap removed.
-
-can test by creating some mails with bizarre subjects.
-
-subject="\001\005RE: blah blah"
-subject="\001\001blah blah"
-subject="\001\032Out of Office AutoReply: blah blah"
-subject="\001\020Undeliverable: blah blah"
-
-looks like it
-
-=end
-
- # now what i think, is that perhaps, value[offset..-1] ...
- # or something like that should be stored as a special tag. ie, do a double yield
- # for this case. probably PR_CONVERSATION_TOPIC, in which case i'd write instead:
- # yield [PR_SUBJECT, ref_type, value]
- # yield [PR_CONVERSATION_TOPIC, ref_type, value[offset..-1]
- # next # to skip the yield.
- end
-
- # special handling for embedded objects
- # used for attach_data for attached messages. in which case attach_method should == 5,
- # for embedded object.
- if type == PT_OBJECT and value
- value = value.read if value.respond_to?(:read)
- id2, unknown = value.unpack 'V2'
- io = RangesIOID2.new desc.pst, id2, idx2
-
- # hacky
- desc2 = OpenStruct.new(:desc => io, :pst => desc.pst, :list_index => desc.list_index, :children => [])
- # put nil instead of desc.list_index, otherwise the attachment is attached to itself ad infinitum.
- # should try and fix that FIXME
- # this shouldn't be done always. for an attached message, yes, but for an attached
- # meta file, for example, it shouldn't. difference between embedded_ole vs embedded_msg
- # really.
- # note that in the case where its a embedded ole, you actually get a regular serialized ole
- # object, so i need to create an ole storage object on a rangesioidxchain!
- # eg:
-=begin
-att.props.display_name # => "Picture (Metafile)"
-io = att.props.attach_data
-io.read(32).unpack('H*') # => ["d0cf11e0a1b11ae100000.... note the docfile signature.
-# plug some missing rangesio holes:
-def io.rewind; seek 0; end
-def io.flush; raise IOError; end
-ole = Ole::Storage.open io
-puts ole.root.to_tree
-
-- #<Dirent:"Root Entry">
- |- #<Dirent:"\001Ole" size=20 data="\001\000\000\002\000...">
- |- #<Dirent:"CONTENTS" size=65696 data="\327\315\306\232\000...">
- \- #<Dirent:"\003MailStream" size=12 data="\001\000\000\000[...">
-=end
- # until properly fixed, i have disabled this code here, so this will break
- # nested messages temporarily.
- #value = Item.new desc2, RawPropertyStore.new(desc2).to_a
- #desc2.list_index = nil
- value = io
- end
- # this is PT_MV_STRING8, i guess.
- # should probably have the 0x1000 flag, and do the or-ring.
- # example of 0x1102 is PR_OUTLOOK_2003_ENTRYIDS. less sure about that one.
- when 0x101e, 0x1102
- # example data:
- # 0x802b "\003\000\000\000\020\000\000\000\030\000\000\000#\000\000\000BusinessCompetitionFavorites"
- # this 0x802b would be an extended attribute for categories / keywords.
- value = get_data_indirect_io(value).read unless String === value
- num = value.unpack('V')[0]
- offsets = value[4, 4 * num].unpack("V#{num}")
- value = (offsets + [value.length]).to_enum(:each_cons, 2).map { |from, to| value[from...to] }
- value.map! { |str| StringIO.new str } if type == 0x1102
- else
- name = Mapi::Types::DATA[type].first rescue nil
- warn '0x%04x %p' % [key, get_data_indirect_io(value).read]
- raise NotImplementedError, 'unsupported mapi property type - 0x%04x (%p)' % [type, name]
- end
- [key, type, value]
- end
- end
-
-=begin
-* recipients:
-
- affects: ["0x200764", "0x2011c4", "0x201b24", "0x201b44", "0x201ba4", "0x201c24", "0x201cc4", "0x202504"]
-
-after adding the rawpropertystoretable fix, all except the second parse properly, and satisfy:
-
- item.props.display_to == item.recipients.map { |r| r.props.display_name if r.props.recipient_type == 1 }.compact * '; '
-
-only the second still has a problem
-
-#[#<struct Pst::Desc desc_id=0x2011c4, idx_id=0x397c, idx2_id=0x398a, parent_desc_id=0x8082>]
-
-think this is related to a multi block #data3. ie, when you use @x * rec_size, and it
-goes > 8190, or there abouts, then it stuffs up. probably there is header gunk, or something,
-similar to when #data is multi block.
-
-same problem affects the attachment table in test4.
-
-fixed that issue. round data3 ranges to rec_size.
-
-fix other issue with attached objects.
-
-all recipients and attachments in test2 are fine.
-
-only remaining issue is test4 recipients of 200044. strange.
-
-=end
-
- # RawPropertyStore is used to iterate through the properties of an item, or the auxiliary
- # data for an attachment. its just a parser for the way the properties are serialized, when the
- # properties don't have to conform to a column structure.
- #
- # structure of this chunk of data is often
- # header, property keys, data values, and then indexes.
- # the property keys has value in it. value can be the actual value if its a short type,
- # otherwise you lookup the value in the indicies, where you get the offsets to use in the
- # main data body. due to the indirect thing though, any of these parts could actually come
- # from a separate stream.
- class RawPropertyStore < BlockParser
- include Enumerable
-
- attr_reader :length
- def initialize desc
- super
- raise FormatError, "expected type 1 - got #{@type}" unless @type == 1
-
- # the way that offset works, data1 may be a subset of buf, or something from id2. if its from buf,
- # it will be offset based on index_offset and offset. so it could be some random chunk of data anywhere
- # in the thing.
- header_data = get_data_indirect @offset1
- raise FormatError if header_data.length < 8
- signature, offset2 = header_data.unpack 'V2'
- #p [@type, signature]
- raise FormatError, 'unhandled block signature 0x%08x' % @type if signature != 0x000602b5
- # this is actually a big chunk of tag tuples.
- @index_data = get_data_indirect offset2
- @length = @index_data.length / 8
- end
-
- # iterate through the property tuples
- def each
- length.times do |i|
- key, type, value = handle_indirect_values(*@index_data[8 * i, 8].unpack('vvV'))
- yield key, type, value
- end
- end
- end
-
- # RawPropertyStoreTable is kind of like a database table.
- # it has a fixed set of columns.
- # #[] is kind of like getting a row from the table.
- # those rows are currently encapsulated by Row, which has #each like
- # RawPropertyStore.
- # only used for the recipients array, and the attachments array. completely lazy, doesn't
- # load any of the properties upon creation.
- class RawPropertyStoreTable < BlockParser
- class Column < Struct.new(:ref_type, :type, :ind2_off, :size, :slot)
- def initialize data
- super(*data.unpack('v3CC'))
- end
-
- def nice_type_name
- Mapi::Types::DATA[ref_type].first[/_(.*)/, 1].downcase rescue '0x%04x' % ref_type
- end
-
- def nice_prop_name
- Mapi::PropertyStore::TAGS['%04x' % type].first[/_(.*)/, 1].downcase rescue '0x%04x' % type
- end
-
- def inspect
- "#<#{self.class} name=#{nice_prop_name.inspect}, type=#{nice_type_name.inspect}>"
- end
- end
-
- include Enumerable
-
- attr_reader :length, :index_data, :data2, :data3, :rec_size
- def initialize desc
- super
- raise FormatError, "expected type 2 - got #{@type}" unless @type == 2
-
- header_data = get_data_indirect @offset1
- # seven_c_blk
- # often: u1 == u2 and u3 == u2 + 2, then rec_size == u3 + 4. wtf
- seven_c, @num_list, u1, u2, u3, @rec_size, b_five_offset,
- ind2_offset, u7, u8 = header_data[0, 22].unpack('CCv4V2v2')
- @index_data = header_data[22..-1]
-
- raise FormatError if @num_list != schema.length or seven_c != 0x7c
- # another check
- min_size = schema.inject(0) { |total, col| total + col.size }
- # seem to have at max, 8 padding bytes on the end of the record. not sure if it means
- # anything. maybe its just space that hasn't been reclaimed due to columns being
- # removed or something. probably should just check lower bound.
- range = (min_size..min_size + 8)
- warn "rec_size seems wrong (#{range} !=== #{rec_size})" unless range === rec_size
-
- header_data2 = get_data_indirect b_five_offset
- raise FormatError if header_data2.length < 8
- signature, offset2 = header_data2.unpack 'V2'
- # ??? seems a bit iffy
- # there's probably more to the differences than this, and the data2 difference below
- expect = desc.pst.header.version_2003? ? 0x000404b5 : 0x000204b5
- raise FormatError, 'unhandled block signature 0x%08x' % signature if signature != expect
-
- # this holds all the row data
- # handle multiple block issue.
- @data3_io = get_data_indirect_io ind2_offset
- if RangesIOIdxChain === @data3_io
- @data3_idxs =
- # modify ranges
- ranges = @data3_io.ranges.map { |offset, size| [offset, size / @rec_size * @rec_size] }
- @data3_io.instance_variable_set :@ranges, ranges
- end
- @data3 = @data3_io.read
-
- # there must be something to the data in data2. i think data2 is the array of objects essentially.
- # currently its only used to imply a length
- # actually, at size 6, its just some auxiliary data. i'm thinking either Vv/vV, for 97, and something
- # wider for 03. the second value is just the index (0...length), and the first value is
- # some kind of offset i expect. actually, they were all id2 values, in another case.
- # so maybe they're get_data_indirect values too?
- # actually, it turned out they were identical to the PR_ATTACHMENT_ID2 values...
- # id2_values = ie, data2.unpack('v*').to_enum(:each_slice, 3).transpose[0]
- # table[i].assoc(PR_ATTACHMENT_ID2).last == id2_values[i], for all i.
- @data2 = get_data_indirect(offset2) rescue nil
- #if data2
- # @length = (data2.length / 6.0).ceil
- #else
- # the above / 6, may have been ok for 97 files, but the new 0x0004 style block must have
- # different size records... just use this instead:
- # hmmm, actually, we can still figure it out:
- @length = @data3.length / @rec_size
- #end
-
- # lets try and at least use data2 for a warning for now
- if data2
- data2_rec_size = desc.pst.header.version_2003? ? 8 : 6
- warn 'somthing seems wrong with data3' unless @length == (data2.length / data2_rec_size)
- end
- end
-
- def schema
- @schema ||= index_data.scan(/.{8}/m).map { |data| Column.new data }
- end
-
- def [] idx
- # handle funky rounding
- Row.new self, idx * @rec_size
- end
-
- def each
- length.times { |i| yield self[i] }
- end
-
- class Row
- include Enumerable
-
- def initialize array_parser, x
- @array_parser, @x = array_parser, x
- end
-
- # iterate through the property tuples
- def each
- (@array_parser.index_data.length / 8).times do |i|
- ref_type, type, ind2_off, size, slot = @array_parser.index_data[8 * i, 8].unpack 'v3CC'
- # check this rescue too
- value = @array_parser.data3[@x + ind2_off, size]
-# if INDIRECT_TYPES.include? ref_type
- if size <= 4
- value = value.unpack('V')[0]
- end
- #p ['0x%04x' % ref_type, '0x%04x' % type, (Msg::Properties::MAPITAGS['%04x' % type].first[/^.._(.*)/, 1].downcase rescue nil),
- # value_orig, value, (get_data_indirect(value_orig.unpack('V')[0]) rescue nil), size, ind2_off, slot]
- key, type, value = @array_parser.handle_indirect_values type, ref_type, value
- yield key, type, value
- end
- end
- end
- end
-
- class AttachmentTable < BlockParser
- # a "fake" MAPI property name for this constant. if you get a mapi property with
- # this value, it is the id2 value to use to get attachment data.
- PR_ATTACHMENT_ID2 = 0x67f2
-
- attr_reader :desc, :table
- def initialize desc
- @desc = desc
- # no super, we only actually want BlockParser2#idx2
- @table = nil
- return unless desc.list_index
- return unless idx = idx2[ID2_ATTACHMENTS]
- # FIXME make a fake desc.
- @desc2 = OpenStruct.new :desc => idx, :pst => desc.pst, :list_index => desc.list_index
- @table = RawPropertyStoreTable.new @desc2
- end
-
- def to_a
- return [] if !table
- table.map do |attachment|
- attachment = attachment.to_a
- #p attachment
- # potentially merge with yet more properties
- # this still seems pretty broken - especially the property overlap
- if attachment_id2 = attachment.assoc(PR_ATTACHMENT_ID2)
- #p attachment_id2.last
- #p idx2[attachment_id2.last]
- @desc2.desc = idx2[attachment_id2.last]
- RawPropertyStore.new(@desc2).each do |a, b, c|
- record = attachment.assoc a
- attachment << record = [] unless record
- record.replace [a, b, c]
- end
- end
- attachment
- end
- end
- end
-
- # there is no equivalent to this in libpst. ID2_RECIPIENTS was just guessed given the above
- # AttachmentTable.
- class RecipientTable < BlockParser
- attr_reader :desc, :table
- def initialize desc
- @desc = desc
- # no super, we only actually want BlockParser2#idx2
- @table = nil
- return unless desc.list_index
- return unless idx = idx2[ID2_RECIPIENTS]
- # FIXME make a fake desc.
- desc2 = OpenStruct.new :desc => idx, :pst => desc.pst, :list_index => desc.list_index
- @table = RawPropertyStoreTable.new desc2
- end
-
- def to_a
- return [] if !table
- table.map { |x| x.to_a }
- end
- end
-
- #
- # higher level item code. wraps up the raw properties above, and gives nice
- # objects to work with. handles item relationships too.
- # ----------------------------------------------------------------------------
- #
-
- def self.make_property_set property_list
- hash = property_list.inject({}) do |hash, (key, type, value)|
- hash.update PropertySet::Key.new(key) => value
- end
- PropertySet.new hash
- end
-
- class Attachment < Mapi::Attachment
- def initialize list
- super Pst.make_property_set(list)
-
- @embedded_msg = props.attach_data if Item === props.attach_data
- end
- end
-
- class Recipient < Mapi::Recipient
- def initialize list
- super Pst.make_property_set(list)
- end
- end
-
- class Item < Mapi::Message
- class EntryID < Struct.new(:u1, :entry_id, :id)
- UNPACK_STR = 'VA16V'
-
- def initialize data
- data = data.unpack(UNPACK_STR) if String === data
- super(*data)
- end
- end
-
- include RecursivelyEnumerable
-
- attr_accessor :type, :parent
-
- def initialize desc, list, type=nil
- @desc = desc
- super Pst.make_property_set(list)
-
- # this is kind of weird, but the ids of the special folders are stored in a hash
- # when the root item is loaded
- if ipm_wastebasket_entryid
- desc.pst.special_folder_ids[ipm_wastebasket_entryid] = :wastebasket
- end
-
- if finder_entryid
- desc.pst.special_folder_ids[finder_entryid] = :finder
- end
-
- # and then here, those are used, along with a crappy heuristic to determine if we are an
- # item
-=begin
-i think the low bits of the desc_id can give some info on the type.
-
-it seems that 0x4 is for regular messages (and maybe contacts etc)
-0x2 is for folders, and 0x8 is for special things like rules etc, that aren't visible.
-=end
- unless type
- type = props.valid_folder_mask || ipm_subtree_entryid || props.content_count || props.subfolders ? :folder : :message
- if type == :folder
- type = desc.pst.special_folder_ids[desc.desc_id] || type
- end
- end
-
- @type = type
- end
-
- def each_child
- id = ipm_subtree_entryid
- if id
- root = @desc.pst.desc_from_id id
- raise "couldn't find root" unless root
- raise 'both kinds of children' unless @desc.children.empty?
- children = root.children
- # lets look up the other ids we have.
- # typically the wastebasket one "deleted items" is in the children already, but
- # the search folder isn't.
- extras = [ipm_wastebasket_entryid, finder_entryid].compact.map do |id|
- root = @desc.pst.desc_from_id id
- warn "couldn't find root for id #{id}" unless root
- root
- end.compact
- # i do this instead of union, so as not to mess with the order of the
- # existing children.
- children += (extras - children)
- children
- else
- @desc.children
- end.each do |desc|
- item = @desc.pst.pst_parse_item(desc)
- item.parent = self
- yield item
- end
- end
-
- def path
- parents, item = [], self
- parents.unshift item while item = item.parent
- # remove root
- parents.shift
- parents.map { |item| item.props.display_name or raise 'unable to construct path' } * '/'
- end
-
- def children
- to_enum(:each_child).to_a
- end
-
- # these are still around because they do different stuff
-
- # Top of Personal Folder Record
- def ipm_subtree_entryid
- @ipm_subtree_entryid ||= EntryID.new(props.ipm_subtree_entryid.read).id rescue nil
- end
-
- # Deleted Items Folder Record
- def ipm_wastebasket_entryid
- @ipm_wastebasket_entryid ||= EntryID.new(props.ipm_wastebasket_entryid.read).id rescue nil
- end
-
- # Search Root Record
- def finder_entryid
- @finder_entryid ||= EntryID.new(props.finder_entryid.read).id rescue nil
- end
-
- # all these have been replaced with the method_missing below
-=begin
- # States which folders are valid for this message store
- #def valid_folder_mask
- # props[0x35df]
- #end
-
- # Number of emails stored in a folder
- def content_count
- props[0x3602]
- end
-
- # Has children
- def subfolders
- props[0x360a]
- end
-=end
-
- # i think i will change these, so they can inherit the lazyness from RawPropertyStoreTable.
- # so if you want the last attachment, you can get it without creating the others perhaps.
- # it just has to handle the no table at all case a bit more gracefully.
-
- def attachments
- @attachments ||= AttachmentTable.new(@desc).to_a.map { |list| Attachment.new list }
- end
-
- def recipients
- #[]
- @recipients ||= RecipientTable.new(@desc).to_a.map { |list| Recipient.new list }
- end
-
- def each_recursive(&block)
- #p :self => self
- children.each do |child|
- #p :child => child
- block[child]
- child.each_recursive(&block)
- end
- end
-
- def inspect
- attrs = %w[display_name subject sender_name subfolders]
-# attrs = %w[display_name valid_folder_mask ipm_wastebasket_entryid finder_entryid content_count subfolders]
- str = attrs.map { |a| b = props.send a; " #{a}=#{b.inspect}" if b }.compact * ','
-
- type_s = type == :message ? 'Message' : type == :folder ? 'Folder' : type.to_s.capitalize + 'Folder'
- str2 = 'desc_id=0x%x' % @desc.desc_id
-
- !str.empty? ? "#<Pst::#{type_s} #{str2}#{str}>" : "#<Pst::#{type_s} #{str2} props=#{props.inspect}>" #\n" + props.transport_message_headers + ">"
- end
- end
-
- # corresponds to
- # * _pst_parse_item
- def pst_parse_item desc
- Item.new desc, RawPropertyStore.new(desc).to_a
- end
-
- #
- # other random code
- # ----------------------------------------------------------------------------
- #
-
- def dump_debug_info
- puts "* pst header"
- p header
-
-=begin
-Looking at the output of this, for blank-o1997.pst, i see this part:
-...
-- (26624,516) desc block data (overlap of 4 bytes)
-- (27136,516) desc block data (gap of 508 bytes)
-- (28160,516) desc block data (gap of 2620 bytes)
-...
-
-which confirms my belief that the block size for idx and desc is more likely 512
-=end
- if 0 + 0 == 0
- puts '* file range usage'
- file_ranges =
- # these 3 things, should account for most of the data in the file.
- [[0, Header::SIZE, 'pst file header']] +
- @idx_offsets.map { |offset| [offset, Index::BLOCK_SIZE, 'idx block data'] } +
- @desc_offsets.map { |offset| [offset, Desc::BLOCK_SIZE, 'desc block data'] } +
- @idx.map { |idx| [idx.offset, idx.size, 'idx id=0x%x (%s)' % [idx.id, idx.type]] }
- (file_ranges.sort_by { |idx| idx.first } + [nil]).to_enum(:each_cons, 2).each do |(offset, size, name), next_record|
- # i think there is a padding of the size out to 64 bytes
- # which is equivalent to padding out the final offset, because i think the offset is
- # similarly oriented
- pad_amount = 64
- warn 'i am wrong about the offset padding' if offset % pad_amount != 0
- # so, assuming i'm not wrong about that, then we can calculate how much padding is needed.
- pad = pad_amount - (size % pad_amount)
- pad = 0 if pad == pad_amount
- gap = next_record ? next_record.first - (offset + size + pad) : 0
- extra = case gap <=> 0
- when -1; ["overlap of #{gap.abs} bytes)"]
- when 0; []
- when +1; ["gap of #{gap} bytes"]
- end
- # how about we check that padding
- @io.pos = offset + size
- pad_bytes = @io.read(pad)
- extra += ["padding not all zero"] unless pad_bytes == 0.chr * pad
- puts "- #{offset}:#{size}+#{pad} #{name.inspect}" + (extra.empty? ? '' : ' [' + extra * ', ' + ']')
- end
- end
-
- # i think the idea of the idx, and indeed the idx2, is just to be able to
- # refer to data indirectly, which means it can get moved around, and you just update
- # the idx table. it is simply a list of file offsets and sizes.
- # not sure i get how id2 plays into it though....
- # the sizes seem to be all even. is that a co-incidence? and the ids are all even. that
- # seems to be related to something else (see the (id & 2) == 1 stuff)
- puts '* idx entries'
- @idx.each { |idx| puts "- #{idx.inspect}" }
-
- # if you look at the desc tree, you notice a few things:
- # 1. there is a desc that seems to be the parent of all the folders, messages etc.
- # it is the one whose parent is itself.
- # one of its children is referenced as the subtree_entryid of the first desc item,
- # the root.
- # 2. typically only 2 types of desc records have idx2_id != 0. messages themselves,
- # and the desc with id = 0x61 - the xattrib container. everything else uses the
- # regular ids to find its data. i think it should be reframed as small blocks and
- # big blocks, but i'll look into it more.
- #
- # idx_id and idx2_id are for getting to the data. desc_id and parent_desc_id just define
- # the parent <-> child relationship, and the desc_ids are how the items are referred to in
- # entryids.
- # note that these aren't unique! eg for 0, 4 etc. i expect these'd never change, as the ids
- # are stored in entryids. whereas the idx and idx2 could be a bit more volatile.
- puts '* desc tree'
- # make a dummy root hold everything just for convenience
- root = Desc.new ''
- def root.inspect; "#<Pst::Root>"; end
- root.children.replace @orphans
- # this still loads the whole thing as a string for gsub. should use directo output io
- # version.
- puts root.to_tree.gsub(/, (parent_desc_id|idx2_id)=0x0(?!\d)/, '')
-
- # this is fairly easy to understand, its just an attempt to display the pst items in a tree form
- # which resembles what you'd see in outlook.
- puts '* item tree'
- # now streams directly
- root_item.to_tree STDOUT
- end
-
- def root_desc
- @desc.first
- end
-
- def root_item
- item = pst_parse_item root_desc
- item.type = :root
- item
- end
-
- def root
- root_item
- end
-
- # depth first search of all items
- include Enumerable
-
- def each(&block)
- root = self.root
- block[root]
- root.each_recursive(&block)
- end
-
- def name
- @name ||= root_item.props.display_name
- end
-
- def inspect
- "#<Pst name=#{name.inspect} io=#{io.inspect}>"
- end
-end
-end
-
diff --git a/vendor/ruby-msg/lib/mapi/rtf.rb b/vendor/ruby-msg/lib/mapi/rtf.rb
deleted file mode 100644
index 9fa133fac..000000000
--- a/vendor/ruby-msg/lib/mapi/rtf.rb
+++ /dev/null
@@ -1,169 +0,0 @@
-require 'stringio'
-require 'strscan'
-require 'rtf'
-
-module Mapi
- #
- # = Introduction
- #
- # The +RTF+ module contains a few helper functions for dealing with rtf
- # in mapi messages: +rtfdecompr+, and <tt>rtf2html</tt>.
- #
- # Both were ported from their original C versions for simplicity's sake.
- #
- module RTF
- RTF_PREBUF =
- "{\\rtf1\\ansi\\mac\\deff0\\deftab720{\\fonttbl;}" \
- "{\\f0\\fnil \\froman \\fswiss \\fmodern \\fscript " \
- "\\fdecor MS Sans SerifSymbolArialTimes New RomanCourier" \
- "{\\colortbl\\red0\\green0\\blue0\n\r\\par " \
- "\\pard\\plain\\f0\\fs20\\b\\i\\u\\tab\\tx"
-
- # Decompresses compressed rtf +data+, as found in the mapi property
- # +PR_RTF_COMPRESSED+. Code converted from my C version, which in turn
- # I wrote from a Java source, in JTNEF I believe.
- #
- # C version was modified to use circular buffer for back references,
- # instead of the optimization of the Java version to index directly into
- # output buffer. This was in preparation to support streaming in a
- # read/write neutral fashion.
- def rtfdecompr data
- io = StringIO.new data
- buf = RTF_PREBUF + "\x00" * (4096 - RTF_PREBUF.length)
- wp = RTF_PREBUF.length
- rtf = ''
-
- # get header fields (as defined in RTFLIB.H)
- compr_size, uncompr_size, magic, crc32 = io.read(16).unpack 'V*'
- #warn "compressed-RTF data size mismatch" unless io.size == data.compr_size + 4
-
- # process the data
- case magic
- when 0x414c454d # "MELA" magic number that identifies the stream as a uncompressed stream
- rtf = io.read uncompr_size
- when 0x75465a4c # "LZFu" magic number that identifies the stream as a compressed stream
- flag_count = -1
- flags = nil
- while rtf.length < uncompr_size and !io.eof?
- # each flag byte flags 8 literals/references, 1 per bit
- flags = ((flag_count += 1) % 8 == 0) ? io.getc : flags >> 1
- if 1 == (flags & 1) # each flag bit is 1 for reference, 0 for literal
- rp, l = io.getc, io.getc
- # offset is a 12 byte number. 2^12 is 4096, so thats fine
- rp = (rp << 4) | (l >> 4) # the offset relative to block start
- l = (l & 0xf) + 2 # the number of bytes to copy
- l.times do
- rtf << buf[wp] = buf[rp]
- wp = (wp + 1) % 4096
- rp = (rp + 1) % 4096
- end
- else
- rtf << buf[wp] = io.getc
- wp = (wp + 1) % 4096
- end
- end
- else # unknown magic number
- raise "Unknown compression type (magic number 0x%08x)" % magic
- end
-
- # not sure if its due to a bug in the above code. doesn't seem to be
- # in my tests, but sometimes there's a trailing null. we chomp it here,
- # which actually makes the resultant rtf smaller than its advertised
- # size (+uncompr_size+).
- rtf.chomp! 0.chr
- rtf
- end
-
- # Note, this is a conversion of the original C code. Not great - needs tests and
- # some refactoring, and an attempt to correct some inaccuracies. Hacky but works.
- #
- # Returns +nil+ if it doesn't look like an rtf encapsulated rtf.
- #
- # Some cases that the original didn't deal with have been patched up, eg from
- # this chunk, where there are tags outside of the htmlrtf ignore block.
- #
- # "{\\*\\htmltag116 <br />}\\htmlrtf \\line \\htmlrtf0 \\line {\\*\\htmltag84 <a href..."
- #
- # We take the approach of ignoring all rtf tags not explicitly handled. A proper
- # parse tree would be nicer to work with. will need to look for ruby rtf library
- #
- # Some of the original comment to the c code is excerpted here:
- #
- # Sometimes in MAPI, the PR_BODY_HTML property contains the HTML of a message.
- # But more usually, the HTML is encoded inside the RTF body (which you get in the
- # PR_RTF_COMPRESSED property). These routines concern the decoding of the HTML
- # from this RTF body.
- #
- # An encoded htmlrtf file is a valid RTF document, but which contains additional
- # html markup information in its comments, and sometimes contains the equivalent
- # rtf markup outside the comments. Therefore, when it is displayed by a plain
- # simple RTF reader, the html comments are ignored and only the rtf markup has
- # effect. Typically, this rtf markup is not as rich as the html markup would have been.
- # But for an html-aware reader (such as the code below), we can ignore all the
- # rtf markup, and extract the html markup out of the comments, and get a valid
- # html document.
- #
- # There are actually two kinds of html markup in comments. Most of them are
- # prefixed by "\*\htmltagNNN", for some number NNN. But sometimes there's one
- # prefixed by "\*\mhtmltagNNN" followed by "\*\htmltagNNN". In this case,
- # the two are equivalent, but the m-tag is for a MIME Multipart/Mixed Message
- # and contains tags that refer to content-ids (e.g. img src="cid:072344a7")
- # while the normal tag just refers to a name (e.g. img src="fred.jpg")
- # The code below keeps the m-tag and discards the normal tag.
- # If there are any m-tags like this, then the message also contains an
- # attachment with a PR_CONTENT_ID property e.g. "072344a7". Actually,
- # sometimes the m-tag is e.g. img src="http://outlook/welcome.html" and the
- # attachment has a PR_CONTENT_LOCATION "http://outlook/welcome.html" instead
- # of a PR_CONTENT_ID.
- #
- def rtf2html rtf
- scan = StringScanner.new rtf
- # require \fromhtml. is this worth keeping? apparently you see \\fromtext if it
- # was converted from plain text.
- return nil unless rtf["\\fromhtml"]
- html = ''
- ignore_tag = nil
- # skip up to the first htmltag. return nil if we don't ever find one
- return nil unless scan.scan_until /(?=\{\\\*\\htmltag)/
- until scan.empty?
- if scan.scan /\{/
- elsif scan.scan /\}/
- elsif scan.scan /\\\*\\htmltag(\d+) ?/
- #p scan[1]
- if ignore_tag == scan[1]
- scan.scan_until /\}/
- ignore_tag = nil
- end
- elsif scan.scan /\\\*\\mhtmltag(\d+) ?/
- ignore_tag = scan[1]
- elsif scan.scan /\\par ?/
- html << "\r\n"
- elsif scan.scan /\\tab ?/
- html << "\t"
- elsif scan.scan /\\'([0-9A-Za-z]{2})/
- html << scan[1].hex.chr
- elsif scan.scan /\\pntext/
- scan.scan_until /\}/
- elsif scan.scan /\\htmlrtf/
- scan.scan_until /\\htmlrtf0 ?/
- # a generic throw away unknown tags thing.
- # the above 2 however, are handled specially
- elsif scan.scan /\\[a-z-]+(\d+)? ?/
- #elsif scan.scan /\\li(\d+) ?/
- #elsif scan.scan /\\fi-(\d+) ?/
- elsif scan.scan /[\r\n]/
- elsif scan.scan /\\([{}\\])/
- html << scan[1]
- elsif scan.scan /(.)/
- html << scan[1]
- else
- p :wtf
- end
- end
- html.strip.empty? ? nil : html
- end
-
- module_function :rtf2html, :rtfdecompr
- end
-end
-
diff --git a/vendor/ruby-msg/lib/mapi/types.rb b/vendor/ruby-msg/lib/mapi/types.rb
deleted file mode 100644
index 71416afd5..000000000
--- a/vendor/ruby-msg/lib/mapi/types.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-require 'rubygems'
-require 'ole/types'
-
-module Mapi
- Log = Logger.new_with_callstack
-
- module Types
- #
- # Mapi property types, taken from http://msdn2.microsoft.com/en-us/library/bb147591.aspx.
- #
- # The fields are [mapi name, variant name, description]. Maybe I should just make it a
- # struct.
- #
- # seen some synonyms here, like PT_I8 vs PT_LONG. seen stuff like PT_SRESTRICTION, not
- # sure what that is. look at `grep ' PT_' data/mapitags.yaml | sort -u`
- # also, it has stuff like PT_MV_BINARY, where _MV_ probably means multi value, and is
- # likely just defined to | in 0x1000.
- #
- # Note that the last 2 are the only ones where the Mapi value differs from the Variant value
- # for the corresponding variant type. Odd. Also, the last 2 are currently commented out here
- # because of the clash.
- #
- # Note 2 - the strings here say VT_BSTR, but I don't have that defined in Ole::Types. Should
- # maybe change them to match. I've also seen reference to PT_TSTRING, which is defined as some
- # sort of get unicode first, and fallback to ansii or something.
- #
- DATA = {
- 0x0001 => ['PT_NULL', 'VT_NULL', 'Null (no valid data)'],
- 0x0002 => ['PT_SHORT', 'VT_I2', '2-byte integer (signed)'],
- 0x0003 => ['PT_LONG', 'VT_I4', '4-byte integer (signed)'],
- 0x0004 => ['PT_FLOAT', 'VT_R4', '4-byte real (floating point)'],
- 0x0005 => ['PT_DOUBLE', 'VT_R8', '8-byte real (floating point)'],
- 0x0006 => ['PT_CURRENCY', 'VT_CY', '8-byte integer (scaled by 10,000)'],
- 0x000a => ['PT_ERROR', 'VT_ERROR', 'SCODE value; 32-bit unsigned integer'],
- 0x000b => ['PT_BOOLEAN', 'VT_BOOL', 'Boolean'],
- 0x000d => ['PT_OBJECT', 'VT_UNKNOWN', 'Data object'],
- 0x001e => ['PT_STRING8', 'VT_BSTR', 'String'],
- 0x001f => ['PT_UNICODE', 'VT_BSTR', 'String'],
- 0x0040 => ['PT_SYSTIME', 'VT_DATE', '8-byte real (date in integer, time in fraction)'],
- #0x0102 => ['PT_BINARY', 'VT_BLOB', 'Binary (unknown format)'],
- #0x0102 => ['PT_CLSID', 'VT_CLSID', 'OLE GUID']
- }
-
- module Constants
- DATA.each { |num, (mapi_name, variant_name, desc)| const_set mapi_name, num }
- end
-
- include Constants
- end
-end
-
diff --git a/vendor/ruby-msg/lib/mime.rb b/vendor/ruby-msg/lib/mime.rb
deleted file mode 100644
index 4340e4901..000000000
--- a/vendor/ruby-msg/lib/mime.rb
+++ /dev/null
@@ -1,165 +0,0 @@
-#
-# = Introduction
-#
-# A *basic* mime class for _really_ _basic_ and probably non-standard parsing
-# and construction of MIME messages.
-#
-# Intended for two main purposes in this project:
-# 1. As the container that is used to build up the message for eventual
-# serialization as an eml.
-# 2. For assistance in parsing the +transport_message_headers+ provided in .msg files,
-# which are then kept through to the final eml.
-#
-# = TODO
-#
-# * Better streaming support, rather than an all-in-string approach.
-# * Add +OrderedHash+ optionally, to not lose ordering in headers.
-# * A fair bit remains to be done for this class, its fairly immature. But generally I'd like
-# to see it be more generally useful.
-# * All sorts of correctness issues, encoding particular.
-# * Duplication of work in net/http.rb's +HTTPHeader+? Don't know if the overlap is sufficient.
-# I don't want to lower case things, just for starters.
-# * Mime was the original place I wrote #to_tree, intended as a quick debug hack.
-#
-class SimpleMime
- Hash = begin
- require 'orderedhash'
- OrderedHash
- rescue LoadError
- Hash
- end
-
- attr_reader :headers, :body, :parts, :content_type, :preamble, :epilogue
-
- # Create a SimpleMime object using +str+ as an initial serialization, which must contain headers
- # and a body (even if empty). Needs work.
- def initialize str, ignore_body=false
- headers, @body = $~[1..-1] if str[/(.*?\r?\n)(?:\r?\n(.*))?\Z/m]
-
- @headers = Hash.new { |hash, key| hash[key] = [] }
- @body ||= ''
- headers.to_s.scan(/^\S+:\s*.*(?:\n\t.*)*/).each do |header|
- @headers[header[/(\S+):/, 1]] << header[/\S+:\s*(.*)/m, 1].gsub(/\s+/m, ' ').strip # this is kind of wrong
- end
-
- # don't have to have content type i suppose
- @content_type, attrs = nil, {}
- if content_type = @headers['Content-Type'][0]
- @content_type, attrs = SimpleMime.split_header content_type
- end
-
- return if ignore_body
-
- if multipart?
- if body.empty?
- @preamble = ''
- @epilogue = ''
- @parts = []
- else
- # we need to split the message at the boundary
- boundary = attrs['boundary'] or raise "no boundary for multipart message"
-
- # splitting the body:
- parts = body.split(/--#{Regexp.quote boundary}/m)
- unless parts[-1] =~ /^--/; warn "bad multipart boundary (missing trailing --)"
- else parts[-1][0..1] = ''
- end
- parts.each_with_index do |part, i|
- part =~ /^(\r?\n)?(.*?)(\r?\n)?\Z/m
- part.replace $2
- warn "bad multipart boundary" if (1...parts.length-1) === i and !($1 && $3)
- end
- @preamble = parts.shift
- @epilogue = parts.pop
- @parts = parts.map { |part| SimpleMime.new part }
- end
- end
- end
-
- def multipart?
- @content_type && @content_type =~ /^multipart/ ? true : false
- end
-
- def inspect
- # add some extra here.
- "#<SimpleMime content_type=#{@content_type.inspect}>"
- end
-
- def to_tree
- if multipart?
- str = "- #{inspect}\n"
- parts.each_with_index do |part, i|
- last = i == parts.length - 1
- part.to_tree.split(/\n/).each_with_index do |line, j|
- str << " #{last ? (j == 0 ? "\\" : ' ') : '|'}" + line + "\n"
- end
- end
- str
- else
- "- #{inspect}\n"
- end
- end
-
- def to_s opts={}
- opts = {:boundary_counter => 0}.merge opts
- if multipart?
- boundary = SimpleMime.make_boundary opts[:boundary_counter] += 1, self
- @body = [preamble, parts.map { |part| "\r\n" + part.to_s(opts) + "\r\n" }, "--\r\n" + epilogue].
- flatten.join("\r\n--" + boundary)
- content_type, attrs = SimpleMime.split_header @headers['Content-Type'][0]
- attrs['boundary'] = boundary
- @headers['Content-Type'] = [([content_type] + attrs.map { |key, val| %{#{key}="#{val}"} }).join('; ')]
- end
-
- str = ''
- @headers.each do |key, vals|
- vals.each { |val| str << "#{key}: #{val}\r\n" }
- end
- str << "\r\n" + @body
- end
-
- def self.split_header header
- # FIXME: haven't read standard. not sure what its supposed to do with " in the name, or if other
- # escapes are allowed. can't test on windows as " isn't allowed anyway. can be fixed with more
- # accurate parser later.
- # maybe move to some sort of Header class. but not all headers should be of it i suppose.
- # at least add a join_header then, taking name and {}. for use in SimpleMime#to_s (for boundary
- # rewrite), and Attachment#to_mime, among others...
- attrs = {}
- header.scan(/;\s*([^\s=]+)\s*=\s*("[^"]*"|[^\s;]*)\s*/m).each do |key, value|
- if attrs[key]; warn "ignoring duplicate header attribute #{key.inspect}"
- else attrs[key] = value[/^"/] ? value[1..-2] : value
- end
- end
-
- [header[/^[^;]+/].strip, attrs]
- end
-
- # +i+ is some value that should be unique for all multipart boundaries for a given message
- def self.make_boundary i, extra_obj = SimpleMime
- "----_=_NextPart_#{'%03d' % i}_#{'%08x' % extra_obj.object_id}.#{'%08x' % Time.now}"
- end
-end
-
-=begin
-things to consider for header work.
-encoded words:
-Subject: =?iso-8859-1?q?p=F6stal?=
-
-and other mime funkyness:
-Content-Disposition: attachment;
- filename*0*=UTF-8''09%20%D7%90%D7%A5;
- filename*1*=%20%D7%A1%D7%91-;
- filename*2*=%D7%A7%95%A5.wma
-Content-Transfer-Encoding: base64
-
-and another, doing a test with an embedded newline in an attachment name, I
-get this output from evolution. I get the feeling that this is probably a bug
-with their implementation though, they weren't expecting new lines in filenames.
-Content-Disposition: attachment; filename="asdf'b\"c
-d efgh=i: ;\\j"
-d efgh=i: ;\\j"; charset=us-ascii
-Content-Type: text/plain; name="asdf'b\"c"; charset=us-ascii
-
-=end
-
diff --git a/vendor/ruby-msg/lib/orderedhash.rb b/vendor/ruby-msg/lib/orderedhash.rb
deleted file mode 100644
index 16a4f5860..000000000
--- a/vendor/ruby-msg/lib/orderedhash.rb
+++ /dev/null
@@ -1,218 +0,0 @@
-# = OrderedHash
-#
-# == Version
-# 1.2006.07.13 (change of the first number means Big Change)
-#
-# == Description
-# Hash which preserves order of added items (like PHP array).
-#
-# == Usage
-#
-# (see examples directory under the ruby gems root directory)
-#
-# require 'rubygems'
-# require 'ordered_hash'
-#
-# hsh = OrderedHash.new
-# hsh['z'] = 1
-# hsh['a'] = 2
-# hsh['c'] = 3
-# p hsh.keys # ['z','a','c']
-#
-# == Source
-# http://simplypowerful.1984.cz/goodlibs/1.2006.07.13
-#
-# == Author
-# jan molic (/mig/at_sign/1984/dot/cz/)
-#
-# == Thanks to
-# Andrew Johnson for his suggestions and fixes of Hash[], merge, to_a, inspect and shift
-# Desmond Dsouza for == fixes
-#
-# == Licence
-# You can redistribute it and/or modify it under the same terms of Ruby's license;
-# either the dual license version in 2003, or any later version.
-#
-
-class OrderedHash < Hash
-
- attr_accessor :order
-
- class << self
-
- def [] *args
- hsh = OrderedHash.new
- if Hash === args[0]
- hsh.replace args[0]
- elsif (args.size % 2) != 0
- raise ArgumentError, "odd number of elements for Hash"
- else
- hsh[args.shift] = args.shift while args.size > 0
- end
- hsh
- end
-
- end
-
- def initialize(*a, &b)
- super
- @order = []
- end
-
- def store_only a,b
- store a,b
- end
-
- alias orig_store store
-
- def store a,b
- @order.push a unless has_key? a
- super a,b
- end
-
- alias []= store
-
- def == hsh2
- return hsh2==self if !hsh2.is_a?(OrderedHash)
- return false if @order != hsh2.order
- super hsh2
- end
-
- def clear
- @order = []
- super
- end
-
- def delete key
- @order.delete key
- super
- end
-
- def each_key
- @order.each { |k| yield k }
- self
- end
-
- def each_value
- @order.each { |k| yield self[k] }
- self
- end
-
- def each
- @order.each { |k| yield k,self[k] }
- self
- end
-
- alias each_pair each
-
- def delete_if
- @order.clone.each { |k|
- delete k if yield
- }
- self
- end
-
- def values
- ary = []
- @order.each { |k| ary.push self[k] }
- ary
- end
-
- def keys
- @order
- end
-
- def invert
- hsh2 = Hash.new
- @order.each { |k| hsh2[self[k]] = k }
- hsh2
- end
-
- def reject &block
- self.dup.delete_if( &block )
- end
-
- def reject! &block
- hsh2 = reject( &block )
- self == hsh2 ? nil : hsh2
- end
-
- def replace hsh2
- @order = hsh2.keys
- super hsh2
- end
-
- def shift
- key = @order.first
- key ? [key,delete(key)] : super
- end
-
- def unshift k,v
- unless self.include? k
- @order.unshift k
- orig_store(k,v)
- true
- else
- false
- end
- end
-
- def push k,v
- unless self.include? k
- @order.push k
- orig_store(k,v)
- true
- else
- false
- end
- end
-
- def pop
- key = @order.last
- key ? [key,delete(key)] : nil
- end
-
- def first
- self[@order.first]
- end
-
- def last
- self[@order.last]
- end
-
- def to_a
- ary = []
- each { |k,v| ary << [k,v] }
- ary
- end
-
- def to_s
- self.to_a.to_s
- end
-
- def inspect
- ary = []
- each {|k,v| ary << k.inspect + "=>" + v.inspect}
- '{' + ary.join(", ") + '}'
- end
-
- def update hsh2
- hsh2.each { |k,v| self[k] = v }
- self
- end
-
- alias :merge! update
-
- def merge hsh2
- self.dup update(hsh2)
- end
-
- def select
- ary = []
- each { |k,v| ary << [k,v] if yield k,v }
- ary
- end
-
-end
-
-#=end
diff --git a/vendor/ruby-msg/lib/rtf.rb b/vendor/ruby-msg/lib/rtf.rb
deleted file mode 100755
index 3afac68a8..000000000
--- a/vendor/ruby-msg/lib/rtf.rb
+++ /dev/null
@@ -1,109 +0,0 @@
-require 'stringio'
-
-# this file is pretty crap, its just to ensure there is always something readable if
-# there is an rtf only body, with no html encapsulation.
-
-module RTF
- class Tokenizer
- def self.process io
- while true do
- case c = io.getc
- when ?{; yield :open_group
- when ?}; yield :close_group
- when ?\\
- case c = io.getc
- when ?{, ?}, ?\\; yield :text, c.chr
- when ?'; yield :text, [io.read(2)].pack('H*')
- when ?a..?z, ?A..?Z
- # read control word
- str = c.chr
- str << c while c = io.read(1) and c =~ /[a-zA-Z]/
- neg = 1
- neg = -1 and c = io.read(1) if c == '-'
- num = if c =~ /[0-9]/
- num = c
- num << c while c = io.read(1) and c =~ /[0-9]/
- num.to_i * neg
- end
- raise "invalid rtf stream" if neg == -1 and !num # ???? \blahblah- some text
- io.seek(-1, IO::SEEK_CUR) if c != ' '
- yield :control_word, str, num
- when nil
- raise "invalid rtf stream" # \EOF
- else
- # other kind of control symbol
- yield :control_symbol, c.chr
- end
- when nil
- return
- when ?\r, ?\n
- # ignore
- else yield :text, c.chr
- end
- end
- end
- end
-
- class Converter
- # crappy
- def self.rtf2text str, format=:text
- group = 0
- text = ''
- text << "<html>\n<body>" if format == :html
- group_type = []
- group_tags = []
- RTF::Tokenizer.process(StringIO.new(str)) do |a, b, c|
- add_text = ''
- case a
- when :open_group; group += 1; group_type[group] = nil; group_tags[group] = []
- when :close_group; group_tags[group].reverse.each { |t| text << "</#{t}>" }; group -= 1;
- when :control_word; # ignore
- group_type[group] ||= b
- # maybe change this to use utf8 where possible
- add_text = if b == 'par' || b == 'line' || b == 'page'; "\n"
- elsif b == 'tab' || b == 'cell'; "\t"
- elsif b == 'endash' || b == 'emdash'; "-"
- elsif b == 'emspace' || b == 'enspace' || b == 'qmspace'; " "
- elsif b == 'ldblquote'; '"'
- else ''
- end
- if b == 'b' || b == 'i' and format == :html
- close = c == 0 ? '/' : ''
- text << "<#{close}#{b}>"
- if c == 0
- group_tags[group].delete b
- else
- group_tags[group] << b
- end
- end
- # lot of other ones belong in here.\
-=begin
-\bullet Bullet character.
-\lquote Left single quotation mark.
-\rquote Right single quotation mark.
-\ldblquote Left double quotation mark.
-\rdblquote
-=end
- when :control_symbol; # ignore
- group_type[group] ||= b
- add_text = ' ' if b == '~' # non-breakable space
- add_text = '-' if b == '_' # non-breakable hypen
- when :text
- add_text = b if group <= 1 or group_type[group] == 'rtlch' && !group_type[0...group].include?('*')
- end
- if format == :html
- text << add_text.gsub(/([<>&"'])/) do
- ent = { '<' => 'lt', '>' => 'gt', '&' => 'amp', '"' => 'quot', "'" => 'apos' }[$1]
- "&#{ent};"
- end
- text << '<br>' if add_text == "\n"
- else
- text << add_text
- end
- end
- text << "</body>\n</html>\n" if format == :html
- text
- end
- end
-end
-