ruby-msg 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +50 -38
- data/Rakefile +2 -2
- data/lib/mapi.rb +1 -1
- data/lib/mapi/convert/note-mime.rb +3 -3
- data/lib/mapi/msg.rb +12 -5
- data/lib/mapi/property_set.rb +1 -1
- data/lib/mapi/rtf.rb +11 -3
- data/lib/mime.rb +121 -118
- data/test/test_mime.rb +2 -2
- metadata +85 -69
data/README
CHANGED
@@ -1,19 +1,15 @@
|
|
1
1
|
= Introduction
|
2
2
|
|
3
|
-
Generally, the goal of the project is the conversion of
|
4
|
-
|
5
|
-
dependencies
|
6
|
-
easy to get
|
3
|
+
Generally, the goal of the project is to enable the conversion of
|
4
|
+
msg and pst files into standards based formats, without reliance on
|
5
|
+
outlook, or any platform dependencies. In fact its currently <em>pure
|
6
|
+
ruby</em>, so it should be easy to get running.
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
It draws on <tt>msgconvert.pl</tt>, but tries to take a cleaner and
|
14
|
-
more complete approach. Neither are complete yet, however, but I think
|
15
|
-
that this project provides a clean foundation upon which to work on
|
16
|
-
a good converter for msg files for use in outlook migrations etc.
|
8
|
+
It is targeted at people who want to migrate their PIM data from outlook,
|
9
|
+
converting msg and pst files into rfc2822 emails, vCard contacts,
|
10
|
+
iCalendar appointments etc. However, it also aims to be a fairly complete
|
11
|
+
mapi message store manipulation library, providing a sane model for
|
12
|
+
(currently read-only) access to msg and pst files (message stores).
|
17
13
|
|
18
14
|
I am happy to accept patches, give commit bits etc.
|
19
15
|
|
@@ -23,53 +19,60 @@ Please let me know how it works for you, any feedback would be welcomed.
|
|
23
19
|
|
24
20
|
Broad features of the project:
|
25
21
|
|
26
|
-
* Can be used as a general
|
22
|
+
* Can be used as a general mapi library, where conversion to and working
|
27
23
|
on a standard format doesn't make sense.
|
28
24
|
|
29
|
-
* Supports conversion of
|
30
|
-
emails,
|
25
|
+
* Supports conversion of messages to standard formats, like rfc2822
|
26
|
+
emails, vCard, etc.
|
31
27
|
|
32
28
|
* Well commented, and easily extended.
|
33
29
|
|
30
|
+
* Basic RTF converter, for providing a readable body when only RTF
|
31
|
+
exists (needs work)
|
32
|
+
|
33
|
+
* RTF decompression support included, as well as HTML extraction from
|
34
|
+
RTF where appropriate (both in pure ruby, see <tt>lib/mapi/rtf.rb</tt>)
|
35
|
+
|
36
|
+
* Support for mapping property codes to symbolic names, with many
|
37
|
+
included.
|
38
|
+
|
39
|
+
Features of the msg format message store:
|
40
|
+
|
34
41
|
* Most key .msg structures are understood, and the only the parsing
|
35
42
|
code should require minor tweaks. Most of remaining work is in achieving
|
36
43
|
high-fidelity conversion to standards formats (see [TODO]).
|
37
44
|
|
38
|
-
Features of the lower-level msg handling:
|
39
|
-
|
40
45
|
* Supports both types of property storage (large ones in +substg+
|
41
|
-
files, and small ones in the +properties+ file
|
46
|
+
files, and small ones in the +properties+ file.
|
42
47
|
|
43
48
|
* Complete support for named properties in different GUID namespaces.
|
44
49
|
|
45
|
-
* Support for mapping property codes to symbolic names, with many
|
46
|
-
included.
|
47
|
-
|
48
|
-
* RTF decompression support included, as well as HTML extraction from
|
49
|
-
RTF where appropriate (both in pure ruby, see <tt>lib/msg/rtf.rb</tt>)
|
50
|
-
|
51
|
-
* Initial RTF converter, for providing a readable body when only RTF
|
52
|
-
exists (needs work)
|
53
|
-
|
54
50
|
* Initial support for handling embedded ole files, converting nested
|
55
51
|
.msg files to message/rfc822 attachments, and serializing others
|
56
52
|
as ole file attachments (allows you to view embedded excel for example).
|
57
53
|
|
54
|
+
Features of the pst format message store:
|
55
|
+
|
56
|
+
* Handles both Outlook 1997 & 2003 format pst files, both with no-
|
57
|
+
and "compressible-" encryption.
|
58
|
+
|
59
|
+
* Understanding of the file format is still very superficial.
|
60
|
+
|
58
61
|
= Usage
|
59
62
|
|
60
|
-
At the command line, it is simple to convert individual msg
|
61
|
-
to .eml, or to convert a batch to an mbox format file. See
|
62
|
-
details:
|
63
|
+
At the command line, it is simple to convert individual msg or pst
|
64
|
+
files to .eml, or to convert a batch to an mbox format file. See mapitool
|
65
|
+
help for details:
|
63
66
|
|
64
|
-
|
65
|
-
|
67
|
+
mapitool -si some_email.msg > some_email.eml
|
68
|
+
mapitool -s *.msg > mbox
|
66
69
|
|
67
70
|
There is also a fairly complete and easy to use high level library
|
68
71
|
access:
|
69
72
|
|
70
|
-
require 'msg'
|
73
|
+
require 'mapi/msg'
|
71
74
|
|
72
|
-
msg = Msg.open filename
|
75
|
+
msg = Mapi::Msg.open filename
|
73
76
|
|
74
77
|
# access to the 3 main data stores, if you want to poke with the msg
|
75
78
|
# internals
|
@@ -101,6 +104,16 @@ support conversion to mime objects.
|
|
101
104
|
# inclusive of attachments etc. (not ideal in memory, but its wip).
|
102
105
|
puts mime.to_s
|
103
106
|
|
107
|
+
= Thanks
|
108
|
+
|
109
|
+
* The initial implementation of parsing msg files was based primarily
|
110
|
+
on msgconvert.pl[http://www.matijs.net/software/msgconv/].
|
111
|
+
|
112
|
+
* The basis for the outlook 97 pst file was the source to +libpst+.
|
113
|
+
|
114
|
+
* The code for rtf decompression was implemented by inspecting the
|
115
|
+
algorithm used in the +JTNEF+ project.
|
116
|
+
|
104
117
|
= Other
|
105
118
|
|
106
119
|
For more information, see
|
@@ -109,8 +122,7 @@ For more information, see
|
|
109
122
|
|
110
123
|
* MsgDetails[http://code.google.com/p/ruby-msg/wiki/MsgDetails]
|
111
124
|
|
112
|
-
*
|
125
|
+
* PstDetails[http://code.google.com/p/ruby-msg/wiki/PstDetails]
|
113
126
|
|
114
|
-
*
|
115
|
-
perl converter.
|
127
|
+
* OleDetails[http://code.google.com/p/ruby-ole/wiki/OleDetails]
|
116
128
|
|
data/Rakefile
CHANGED
@@ -48,7 +48,7 @@ spec = Gem::Specification.new do |s|
|
|
48
48
|
s.name = PKG_NAME
|
49
49
|
s.version = PKG_VERSION
|
50
50
|
s.summary = %q{Ruby Msg library.}
|
51
|
-
s.description = %q{A library for reading Outlook msg
|
51
|
+
s.description = %q{A library for reading and converting Outlook msg and pst files (mapi message stores).}
|
52
52
|
s.authors = ["Charles Lowe"]
|
53
53
|
s.email = %q{aquasync@gmail.com}
|
54
54
|
s.homepage = %q{http://code.google.com/p/ruby-msg}
|
@@ -64,7 +64,7 @@ spec = Gem::Specification.new do |s|
|
|
64
64
|
'--title', "#{PKG_NAME} documentation",
|
65
65
|
'--tab-width', '2']
|
66
66
|
|
67
|
-
s.add_dependency 'ruby-ole', '>=1.2.
|
67
|
+
s.add_dependency 'ruby-ole', '>=1.2.8'
|
68
68
|
s.add_dependency 'vpim', '>=0.360'
|
69
69
|
end
|
70
70
|
|
data/lib/mapi.rb
CHANGED
@@ -81,9 +81,9 @@ module Mapi
|
|
81
81
|
recips_by_type = recipients.group_by { |r| r.type }
|
82
82
|
# i want to the the types in a specific order.
|
83
83
|
[:to, :cc, :bcc].each do |type|
|
84
|
-
#
|
85
|
-
# of the ole name
|
86
|
-
recips = recips_by_type[type]
|
84
|
+
# for maximal (probably pointless) fidelity, we try to sort recipients by the
|
85
|
+
# numerical part of the ole name
|
86
|
+
recips = recips_by_type[type] || []
|
87
87
|
recips = (recips.sort_by { |r| r.obj.name[/\d{8}$/].hex } rescue recips)
|
88
88
|
# switched to using , for separation, not ;. see issue #4
|
89
89
|
# recips.empty? is strange. i wouldn't have thought it possible, but it was right?
|
data/lib/mapi/msg.rb
CHANGED
@@ -173,7 +173,9 @@ module Mapi
|
|
173
173
|
# parse guids
|
174
174
|
# this is the guids for named properities (other than builtin ones)
|
175
175
|
# i think PS_PUBLIC_STRINGS, and PS_MAPI are builtin.
|
176
|
-
|
176
|
+
# Scan using an ascii pattern - it's binary data we're looking
|
177
|
+
# at, so we don't want to look for unicode characters
|
178
|
+
guids = [PS_PUBLIC_STRINGS] + guids_obj.read.scan(/.{16}/mn).map do |str|
|
177
179
|
Ole::Types.load_guid str
|
178
180
|
end
|
179
181
|
|
@@ -187,14 +189,16 @@ module Mapi
|
|
187
189
|
# parse actual props.
|
188
190
|
# not sure about any of this stuff really.
|
189
191
|
# should flip a few bits in the real msg, to get a better understanding of how this works.
|
190
|
-
|
192
|
+
# Scan using an ascii pattern - it's binary data we're looking
|
193
|
+
# at, so we don't want to look for unicode characters
|
194
|
+
props = props_obj.read.scan(/.{8}/mn).map do |str|
|
191
195
|
flags, offset = str[4..-1].unpack 'v2'
|
192
196
|
# the property will be serialised as this pseudo property, mapping it to this named property
|
193
197
|
pseudo_prop = 0x8000 + offset
|
194
198
|
named = flags & 1 == 1
|
195
199
|
prop = if named
|
196
|
-
str_off =
|
197
|
-
len =
|
200
|
+
str_off = str.unpack('V').first
|
201
|
+
len = names_data[str_off, 4].unpack('V').first
|
198
202
|
Ole::Types::FROM_UTF16.iconv names_data[str_off + 4, len]
|
199
203
|
else
|
200
204
|
a, b = str.unpack('v2')
|
@@ -249,11 +253,14 @@ module Mapi
|
|
249
253
|
def parse_properties obj
|
250
254
|
data = obj.read
|
251
255
|
# don't really understand this that well...
|
256
|
+
|
252
257
|
pad = data.length % 16
|
253
258
|
unless (pad == 0 || pad == 8) and data[0...pad] == "\000" * pad
|
254
259
|
Log.warn "padding was not as expected #{pad} (#{data.length}) -> #{data[0...pad].inspect}"
|
255
260
|
end
|
256
|
-
data
|
261
|
+
# Scan using an ascii pattern - it's binary data we're looking
|
262
|
+
# at, so we don't want to look for unicode characters
|
263
|
+
data[pad..-1].scan(/.{16}/mn).each do |data|
|
257
264
|
property, encoding = ('%08x' % data.unpack('V')).scan /.{4}/
|
258
265
|
key = property.hex
|
259
266
|
# doesn't make any sense to me. probably because its a serialization of some internal
|
data/lib/mapi/property_set.rb
CHANGED
@@ -249,7 +249,7 @@ module Mapi
|
|
249
249
|
# for providing rtf decompression
|
250
250
|
def body_rtf
|
251
251
|
return @body_rtf if defined?(@body_rtf)
|
252
|
-
@body_rtf = (RTF.rtfdecompr rtf_compressed.read rescue nil)
|
252
|
+
@body_rtf = (RTF.rtfdecompr rtf_compressed.read)# rescue nil)
|
253
253
|
end
|
254
254
|
|
255
255
|
# for providing rtf to html conversion
|
data/lib/mapi/rtf.rb
CHANGED
@@ -2,6 +2,14 @@ require 'stringio'
|
|
2
2
|
require 'strscan'
|
3
3
|
require 'rtf'
|
4
4
|
|
5
|
+
class StringIO # :nodoc:
|
6
|
+
begin
|
7
|
+
instance_method :getbyte
|
8
|
+
rescue NameError
|
9
|
+
alias getbyte getc
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
5
13
|
module Mapi
|
6
14
|
#
|
7
15
|
# = Introduction
|
@@ -46,9 +54,9 @@ module Mapi
|
|
46
54
|
flags = nil
|
47
55
|
while rtf.length < uncompr_size and !io.eof?
|
48
56
|
# each flag byte flags 8 literals/references, 1 per bit
|
49
|
-
flags = ((flag_count += 1) % 8 == 0) ? io.
|
57
|
+
flags = ((flag_count += 1) % 8 == 0) ? io.getbyte : flags >> 1
|
50
58
|
if 1 == (flags & 1) # each flag bit is 1 for reference, 0 for literal
|
51
|
-
rp, l = io.
|
59
|
+
rp, l = io.getbyte, io.getbyte
|
52
60
|
# offset is a 12 byte number. 2^12 is 4096, so thats fine
|
53
61
|
rp = (rp << 4) | (l >> 4) # the offset relative to block start
|
54
62
|
l = (l & 0xf) + 2 # the number of bytes to copy
|
@@ -58,7 +66,7 @@ module Mapi
|
|
58
66
|
rp = (rp + 1) % 4096
|
59
67
|
end
|
60
68
|
else
|
61
|
-
rtf << buf[wp] = io.
|
69
|
+
rtf << buf[wp] = io.getbyte.chr
|
62
70
|
wp = (wp + 1) % 4096
|
63
71
|
end
|
64
72
|
end
|
data/lib/mime.rb
CHANGED
@@ -21,124 +21,126 @@
|
|
21
21
|
# I don't want to lower case things, just for starters.
|
22
22
|
# * Mime was the original place I wrote #to_tree, intended as a quick debug hack.
|
23
23
|
#
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
24
|
+
module Mapi
|
25
|
+
class Mime
|
26
|
+
Hash = begin
|
27
|
+
require 'orderedhash'
|
28
|
+
OrderedHash
|
29
|
+
rescue LoadError
|
30
|
+
Hash
|
31
|
+
end
|
32
|
+
|
33
|
+
attr_reader :headers, :body, :parts, :content_type, :preamble, :epilogue
|
34
|
+
|
35
|
+
# Create a Mime object using +str+ as an initial serialization, which must contain headers
|
36
|
+
# and a body (even if empty). Needs work.
|
37
|
+
def initialize str, ignore_body=false
|
38
|
+
headers, @body = $~[1..-1] if str[/(.*?\r?\n)(?:\r?\n(.*))?\Z/m]
|
39
|
+
|
40
|
+
@headers = Hash.new { |hash, key| hash[key] = [] }
|
41
|
+
@body ||= ''
|
42
|
+
headers.to_s.scan(/^\S+:\s*.*(?:\n\t.*)*/).each do |header|
|
43
|
+
@headers[header[/(\S+):/, 1]] << header[/\S+:\s*(.*)/m, 1].gsub(/\s+/m, ' ').strip # this is kind of wrong
|
44
|
+
end
|
45
|
+
|
46
|
+
# don't have to have content type i suppose
|
47
|
+
@content_type, attrs = nil, {}
|
48
|
+
if content_type = @headers['Content-Type'][0]
|
49
|
+
@content_type, attrs = Mime.split_header content_type
|
50
|
+
end
|
51
|
+
|
52
|
+
return if ignore_body
|
53
|
+
|
54
|
+
if multipart?
|
55
|
+
if body.empty?
|
56
|
+
@preamble = ''
|
57
|
+
@epilogue = ''
|
58
|
+
@parts = []
|
59
|
+
else
|
60
|
+
# we need to split the message at the boundary
|
61
|
+
boundary = attrs['boundary'] or raise "no boundary for multipart message"
|
62
|
+
|
63
|
+
# splitting the body:
|
64
|
+
parts = body.split(/--#{Regexp.quote boundary}/m)
|
65
|
+
unless parts[-1] =~ /^--/; warn "bad multipart boundary (missing trailing --)"
|
66
|
+
else parts[-1][0..1] = ''
|
67
|
+
end
|
68
|
+
parts.each_with_index do |part, i|
|
69
|
+
part =~ /^(\r?\n)?(.*?)(\r?\n)?\Z/m
|
70
|
+
part.replace $2
|
71
|
+
warn "bad multipart boundary" if (1...parts.length-1) === i and !($1 && $3)
|
72
|
+
end
|
73
|
+
@preamble = parts.shift
|
74
|
+
@epilogue = parts.pop
|
75
|
+
@parts = parts.map { |part| Mime.new part }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def multipart?
|
81
|
+
@content_type && @content_type =~ /^multipart/ ? true : false
|
82
|
+
end
|
83
|
+
|
84
|
+
def inspect
|
85
|
+
# add some extra here.
|
86
|
+
"#<Mime content_type=#{@content_type.inspect}>"
|
87
|
+
end
|
88
|
+
|
89
|
+
def to_tree
|
90
|
+
if multipart?
|
91
|
+
str = "- #{inspect}\n"
|
92
|
+
parts.each_with_index do |part, i|
|
93
|
+
last = i == parts.length - 1
|
94
|
+
part.to_tree.split(/\n/).each_with_index do |line, j|
|
95
|
+
str << " #{last ? (j == 0 ? "\\" : ' ') : '|'}" + line + "\n"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
str
|
99
|
+
else
|
100
|
+
"- #{inspect}\n"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def to_s opts={}
|
105
|
+
opts = {:boundary_counter => 0}.merge opts
|
106
|
+
if multipart?
|
107
|
+
boundary = Mime.make_boundary opts[:boundary_counter] += 1, self
|
108
|
+
@body = [preamble, parts.map { |part| "\r\n" + part.to_s(opts) + "\r\n" }, "--\r\n" + epilogue].
|
109
|
+
flatten.join("\r\n--" + boundary)
|
110
|
+
content_type, attrs = Mime.split_header @headers['Content-Type'][0]
|
111
|
+
attrs['boundary'] = boundary
|
112
|
+
@headers['Content-Type'] = [([content_type] + attrs.map { |key, val| %{#{key}="#{val}"} }).join('; ')]
|
113
|
+
end
|
114
|
+
|
115
|
+
str = ''
|
116
|
+
@headers.each do |key, vals|
|
117
|
+
vals.each { |val| str << "#{key}: #{val}\r\n" }
|
118
|
+
end
|
119
|
+
str << "\r\n" + @body
|
120
|
+
end
|
121
|
+
|
122
|
+
def self.split_header header
|
123
|
+
# FIXME: haven't read standard. not sure what its supposed to do with " in the name, or if other
|
124
|
+
# escapes are allowed. can't test on windows as " isn't allowed anyway. can be fixed with more
|
125
|
+
# accurate parser later.
|
126
|
+
# maybe move to some sort of Header class. but not all headers should be of it i suppose.
|
127
|
+
# at least add a join_header then, taking name and {}. for use in Mime#to_s (for boundary
|
128
|
+
# rewrite), and Attachment#to_mime, among others...
|
129
|
+
attrs = {}
|
130
|
+
header.scan(/;\s*([^\s=]+)\s*=\s*("[^"]*"|[^\s;]*)\s*/m).each do |key, value|
|
131
|
+
if attrs[key]; warn "ignoring duplicate header attribute #{key.inspect}"
|
132
|
+
else attrs[key] = value[/^"/] ? value[1..-2] : value
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
[header[/^[^;]+/].strip, attrs]
|
137
|
+
end
|
138
|
+
|
139
|
+
# +i+ is some value that should be unique for all multipart boundaries for a given message
|
140
|
+
def self.make_boundary i, extra_obj = Mime
|
141
|
+
"----_=_NextPart_#{'%03d' % i}_#{'%08x' % extra_obj.object_id}.#{'%08x' % Time.now}"
|
142
|
+
end
|
143
|
+
end
|
142
144
|
end
|
143
145
|
|
144
146
|
=begin
|
@@ -163,3 +165,4 @@ Content-Type: text/plain; name="asdf'b\"c"; charset=us-ascii
|
|
163
165
|
|
164
166
|
=end
|
165
167
|
|
168
|
+
|
data/test/test_mime.rb
CHANGED
@@ -9,7 +9,7 @@ require 'mime'
|
|
9
9
|
class TestMime < Test::Unit::TestCase
|
10
10
|
# test out the way it partitions a message into parts
|
11
11
|
def test_parsing_no_multipart
|
12
|
-
mime = Mime.new "Header1: Value1\r\nHeader2: Value2\r\n\r\nBody text."
|
12
|
+
mime = Mapi::Mime.new "Header1: Value1\r\nHeader2: Value2\r\n\r\nBody text."
|
13
13
|
assert_equal ['Value1'], mime.headers['Header1']
|
14
14
|
assert_equal 'Body text.', mime.body
|
15
15
|
assert_equal false, mime.multipart?
|
@@ -18,7 +18,7 @@ class TestMime < Test::Unit::TestCase
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def test_boundaries
|
21
|
-
assert_match(/^----_=_NextPart_001_/, Mime.make_boundary(1))
|
21
|
+
assert_match(/^----_=_NextPart_001_/, Mapi::Mime.make_boundary(1))
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
metadata
CHANGED
@@ -1,102 +1,118 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-msg
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 5
|
8
|
+
- 0
|
9
|
+
version: 1.5.0
|
5
10
|
platform: ruby
|
6
11
|
authors:
|
7
|
-
- Charles Lowe
|
12
|
+
- Charles Lowe
|
8
13
|
autorequire:
|
9
14
|
bindir: bin
|
10
15
|
cert_chain: []
|
11
16
|
|
12
|
-
date:
|
17
|
+
date: 2011-05-18 00:00:00 -04:00
|
13
18
|
default_executable:
|
14
19
|
dependencies:
|
15
|
-
- !ruby/object:Gem::Dependency
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: ruby-ole
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 1
|
29
|
+
- 2
|
30
|
+
- 8
|
31
|
+
version: 1.2.8
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: vpim
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 0
|
43
|
+
- 360
|
44
|
+
version: "0.360"
|
45
|
+
type: :runtime
|
46
|
+
version_requirements: *id002
|
47
|
+
description: A library for reading and converting Outlook msg and pst files (mapi message stores).
|
36
48
|
email: aquasync@gmail.com
|
37
49
|
executables:
|
38
|
-
- mapitool
|
50
|
+
- mapitool
|
39
51
|
extensions: []
|
40
52
|
|
41
53
|
extra_rdoc_files:
|
42
|
-
- README
|
54
|
+
- README
|
43
55
|
files:
|
44
|
-
- data/
|
45
|
-
- data/
|
46
|
-
- data/
|
47
|
-
- Rakefile
|
48
|
-
- README
|
49
|
-
- FIXES
|
50
|
-
- bin/mapitool
|
51
|
-
- lib/
|
52
|
-
- lib/
|
53
|
-
- lib/
|
54
|
-
- lib/
|
55
|
-
- lib/mapi/
|
56
|
-
- lib/mapi/
|
57
|
-
- lib/mapi/
|
58
|
-
- lib/mapi/
|
59
|
-
- lib/mapi/
|
60
|
-
- lib/mapi/
|
61
|
-
- lib/mapi/convert/contact.rb
|
62
|
-
- lib/mime.rb
|
63
|
-
- lib/mapi.rb
|
64
|
-
- test/
|
65
|
-
- test/test_convert_note.rb
|
66
|
-
- test/test_mime.rb
|
67
|
-
- test/
|
68
|
-
- test/
|
69
|
-
- test/
|
56
|
+
- data/mapitags.yaml
|
57
|
+
- data/types.yaml
|
58
|
+
- data/named_map.yaml
|
59
|
+
- Rakefile
|
60
|
+
- README
|
61
|
+
- FIXES
|
62
|
+
- bin/mapitool
|
63
|
+
- lib/rtf.rb
|
64
|
+
- lib/mapi.rb
|
65
|
+
- lib/mime.rb
|
66
|
+
- lib/orderedhash.rb
|
67
|
+
- lib/mapi/rtf.rb
|
68
|
+
- lib/mapi/property_set.rb
|
69
|
+
- lib/mapi/pst.rb
|
70
|
+
- lib/mapi/convert.rb
|
71
|
+
- lib/mapi/types.rb
|
72
|
+
- lib/mapi/msg.rb
|
73
|
+
- lib/mapi/convert/contact.rb
|
74
|
+
- lib/mapi/convert/note-mime.rb
|
75
|
+
- lib/mapi/convert/note-tmail.rb
|
76
|
+
- test/test_property_set.rb
|
77
|
+
- test/test_convert_note.rb
|
78
|
+
- test/test_mime.rb
|
79
|
+
- test/test_convert_contact.rb
|
80
|
+
- test/test_types.rb
|
81
|
+
- test/test_msg.rb
|
70
82
|
has_rdoc: true
|
71
83
|
homepage: http://code.google.com/p/ruby-msg
|
84
|
+
licenses: []
|
85
|
+
|
72
86
|
post_install_message:
|
73
87
|
rdoc_options:
|
74
|
-
- --main
|
75
|
-
- README
|
76
|
-
- --title
|
77
|
-
- ruby-msg documentation
|
78
|
-
- --tab-width
|
79
|
-
- "2"
|
88
|
+
- --main
|
89
|
+
- README
|
90
|
+
- --title
|
91
|
+
- ruby-msg documentation
|
92
|
+
- --tab-width
|
93
|
+
- "2"
|
80
94
|
require_paths:
|
81
|
-
- lib
|
95
|
+
- lib
|
82
96
|
required_ruby_version: !ruby/object:Gem::Requirement
|
83
97
|
requirements:
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
segments:
|
101
|
+
- 0
|
102
|
+
version: "0"
|
88
103
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
104
|
requirements:
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
segments:
|
108
|
+
- 0
|
109
|
+
version: "0"
|
94
110
|
requirements: []
|
95
111
|
|
96
112
|
rubyforge_project: ruby-msg
|
97
|
-
rubygems_version: 1.
|
113
|
+
rubygems_version: 1.3.6
|
98
114
|
signing_key:
|
99
|
-
specification_version:
|
115
|
+
specification_version: 3
|
100
116
|
summary: Ruby Msg library.
|
101
117
|
test_files: []
|
102
118
|
|