ruby-msg 1.4.0 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|