libis-mapi 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/COPYING +20 -0
- data/ChangeLog +108 -0
- data/Home.md +133 -0
- data/Rakefile +56 -0
- data/bin/mapitool +204 -0
- data/data/mapitags.yaml +4168 -0
- data/data/named_map.yaml +114 -0
- data/data/types.yaml +15 -0
- data/lib/mapi/base.rb +104 -0
- data/lib/mapi/convert/contact.rb +142 -0
- data/lib/mapi/convert/note-mime.rb +288 -0
- data/lib/mapi/convert/note-tmail.rb +293 -0
- data/lib/mapi/convert.rb +69 -0
- data/lib/mapi/helper.rb +46 -0
- data/lib/mapi/mime.rb +227 -0
- data/lib/mapi/msg.rb +516 -0
- data/lib/mapi/property_set.rb +329 -0
- data/lib/mapi/pst.rb +1995 -0
- data/lib/mapi/rtf.rb +297 -0
- data/lib/mapi/types.rb +51 -0
- data/lib/mapi/version.rb +3 -0
- data/lib/mapi.rb +5 -0
- data/ruby-msg.gemspec +26 -0
- metadata +102 -0
data/lib/mapi/rtf.rb
ADDED
@@ -0,0 +1,297 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'strscan'
|
3
|
+
|
4
|
+
class StringIO # :nodoc:
|
5
|
+
begin
|
6
|
+
instance_method :getbyte
|
7
|
+
rescue NameError
|
8
|
+
alias getbyte getc
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module Mapi
|
13
|
+
#
|
14
|
+
# = Introduction
|
15
|
+
#
|
16
|
+
# The +RTF+ module contains a few helper functions for dealing with rtf
|
17
|
+
# in mapi messages: +rtfdecompr+, and <tt>rtf2html</tt>.
|
18
|
+
#
|
19
|
+
# Both were ported from their original C versions for simplicity's sake.
|
20
|
+
#
|
21
|
+
module RTF
|
22
|
+
class Tokenizer
|
23
|
+
def self.process io
|
24
|
+
while true do
|
25
|
+
case c = io.getc
|
26
|
+
when ?{; yield :open_group
|
27
|
+
when ?}; yield :close_group
|
28
|
+
when ?\\
|
29
|
+
case c = io.getc
|
30
|
+
when ?{, ?}, ?\\; yield :text, c.chr
|
31
|
+
when ?'; yield :text, [io.read(2)].pack('H*')
|
32
|
+
when ?a..?z, ?A..?Z
|
33
|
+
# read control word
|
34
|
+
str = c.chr
|
35
|
+
str << c while c = io.read(1) and c =~ /[a-zA-Z]/
|
36
|
+
neg = 1
|
37
|
+
neg = -1 and c = io.read(1) if c == '-'
|
38
|
+
num = if c =~ /[0-9]/
|
39
|
+
num = c
|
40
|
+
num << c while c = io.read(1) and c =~ /[0-9]/
|
41
|
+
num.to_i * neg
|
42
|
+
end
|
43
|
+
raise "invalid rtf stream" if neg == -1 and !num # ???? \blahblah- some text
|
44
|
+
io.seek(-1, IO::SEEK_CUR) if c != ' '
|
45
|
+
yield :control_word, str, num
|
46
|
+
when nil
|
47
|
+
raise "invalid rtf stream" # \EOF
|
48
|
+
else
|
49
|
+
# other kind of control symbol
|
50
|
+
yield :control_symbol, c.chr
|
51
|
+
end
|
52
|
+
when nil
|
53
|
+
return
|
54
|
+
when ?\r, ?\n
|
55
|
+
# ignore
|
56
|
+
else yield :text, c.chr
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class Converter
|
63
|
+
# this is pretty crap, its just to ensure there is always something readable if
|
64
|
+
# there is an rtf only body, with no html encapsulation.
|
65
|
+
def self.rtf2text str, format=:text
|
66
|
+
group = 0
|
67
|
+
text = ''
|
68
|
+
text << "<html>\n<body>" if format == :html
|
69
|
+
group_type = []
|
70
|
+
group_tags = []
|
71
|
+
RTF::Tokenizer.process(StringIO.new(str)) do |a, b, c|
|
72
|
+
add_text = ''
|
73
|
+
case a
|
74
|
+
when :open_group; group += 1; group_type[group] = nil; group_tags[group] = []
|
75
|
+
when :close_group; group_tags[group].reverse.each { |t| text << "</#{t}>" }; group -= 1;
|
76
|
+
when :control_word; # ignore
|
77
|
+
group_type[group] ||= b
|
78
|
+
# maybe change this to use utf8 where possible
|
79
|
+
add_text = if b == 'par' || b == 'line' || b == 'page'; "\n"
|
80
|
+
elsif b == 'tab' || b == 'cell'; "\t"
|
81
|
+
elsif b == 'endash' || b == 'emdash'; "-"
|
82
|
+
elsif b == 'emspace' || b == 'enspace' || b == 'qmspace'; " "
|
83
|
+
elsif b == 'ldblquote'; '"'
|
84
|
+
else ''
|
85
|
+
end
|
86
|
+
if b == 'b' || b == 'i' and format == :html
|
87
|
+
close = c == 0 ? '/' : ''
|
88
|
+
text << "<#{close}#{b}>"
|
89
|
+
if c == 0
|
90
|
+
group_tags[group].delete b
|
91
|
+
else
|
92
|
+
group_tags[group] << b
|
93
|
+
end
|
94
|
+
end
|
95
|
+
# lot of other ones belong in here.\
|
96
|
+
=begin
|
97
|
+
\bullet Bullet character.
|
98
|
+
\lquote Left single quotation mark.
|
99
|
+
\rquote Right single quotation mark.
|
100
|
+
\ldblquote Left double quotation mark.
|
101
|
+
\rdblquote
|
102
|
+
=end
|
103
|
+
when :control_symbol; # ignore
|
104
|
+
group_type[group] ||= b
|
105
|
+
add_text = ' ' if b == '~' # non-breakable space
|
106
|
+
add_text = '-' if b == '_' # non-breakable hypen
|
107
|
+
when :text
|
108
|
+
add_text = b if group <= 1 or group_type[group] == 'rtlch' && !group_type[0...group].include?('*')
|
109
|
+
end
|
110
|
+
if format == :html
|
111
|
+
text << add_text.gsub(/([<>&"'])/) do
|
112
|
+
ent = { '<' => 'lt', '>' => 'gt', '&' => 'amp', '"' => 'quot', "'" => 'apos' }[$1]
|
113
|
+
"&#{ent};"
|
114
|
+
end
|
115
|
+
text << '<br>' if add_text == "\n"
|
116
|
+
else
|
117
|
+
text << add_text
|
118
|
+
end
|
119
|
+
end
|
120
|
+
text << "</body>\n</html>\n" if format == :html
|
121
|
+
text
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
RTF_PREBUF =
|
126
|
+
"{\\rtf1\\ansi\\mac\\deff0\\deftab720{\\fonttbl;}" \
|
127
|
+
"{\\f0\\fnil \\froman \\fswiss \\fmodern \\fscript " \
|
128
|
+
"\\fdecor MS Sans SerifSymbolArialTimes New RomanCourier" \
|
129
|
+
"{\\colortbl\\red0\\green0\\blue0\n\r\\par " \
|
130
|
+
"\\pard\\plain\\f0\\fs20\\b\\i\\u\\tab\\tx"
|
131
|
+
|
132
|
+
# Decompresses compressed rtf +data+, as found in the mapi property
|
133
|
+
# +PR_RTF_COMPRESSED+. Code converted from my C version, which in turn
|
134
|
+
# I wrote from a Java source, in JTNEF I believe.
|
135
|
+
#
|
136
|
+
# C version was modified to use circular buffer for back references,
|
137
|
+
# instead of the optimization of the Java version to index directly into
|
138
|
+
# output buffer. This was in preparation to support streaming in a
|
139
|
+
# read/write neutral fashion.
|
140
|
+
def rtfdecompr data
|
141
|
+
io = StringIO.new data
|
142
|
+
buf = RTF_PREBUF + "\x00" * (4096 - RTF_PREBUF.length)
|
143
|
+
wp = RTF_PREBUF.length
|
144
|
+
rtf = ''
|
145
|
+
|
146
|
+
# get header fields (as defined in RTFLIB.H)
|
147
|
+
compr_size, uncompr_size, magic, crc32 = io.read(16).unpack 'V*'
|
148
|
+
#warn "compressed-RTF data size mismatch" unless io.size == data.compr_size + 4
|
149
|
+
|
150
|
+
# process the data
|
151
|
+
case magic
|
152
|
+
when 0x414c454d # "MELA" magic number that identifies the stream as a uncompressed stream
|
153
|
+
rtf = io.read uncompr_size
|
154
|
+
when 0x75465a4c # "LZFu" magic number that identifies the stream as a compressed stream
|
155
|
+
flag_count = -1
|
156
|
+
flags = nil
|
157
|
+
while rtf.length < uncompr_size and !io.eof?
|
158
|
+
# each flag byte flags 8 literals/references, 1 per bit
|
159
|
+
flags = ((flag_count += 1) % 8 == 0) ? io.getbyte : flags >> 1
|
160
|
+
if 1 == (flags & 1) # each flag bit is 1 for reference, 0 for literal
|
161
|
+
rp, l = io.getbyte, io.getbyte
|
162
|
+
# offset is a 12 byte number. 2^12 is 4096, so thats fine
|
163
|
+
rp = (rp << 4) | (l >> 4) # the offset relative to block start
|
164
|
+
l = (l & 0xf) + 2 # the number of bytes to copy
|
165
|
+
l.times do
|
166
|
+
rtf << buf[wp] = buf[rp]
|
167
|
+
wp = (wp + 1) % 4096
|
168
|
+
rp = (rp + 1) % 4096
|
169
|
+
end
|
170
|
+
else
|
171
|
+
rtf << buf[wp] = io.getbyte.chr
|
172
|
+
wp = (wp + 1) % 4096
|
173
|
+
end
|
174
|
+
end
|
175
|
+
else # unknown magic number
|
176
|
+
raise "Unknown compression type (magic number 0x%08x)" % magic
|
177
|
+
end
|
178
|
+
|
179
|
+
# not sure if its due to a bug in the above code. doesn't seem to be
|
180
|
+
# in my tests, but sometimes there's a trailing null. we chomp it here,
|
181
|
+
# which actually makes the resultant rtf smaller than its advertised
|
182
|
+
# size (+uncompr_size+).
|
183
|
+
rtf.chomp! 0.chr
|
184
|
+
rtf
|
185
|
+
end
|
186
|
+
|
187
|
+
# Note, this is a conversion of the original C code. Not great - needs tests and
|
188
|
+
# some refactoring, and an attempt to correct some inaccuracies. Hacky but works.
|
189
|
+
#
|
190
|
+
# Returns +nil+ if it doesn't look like an rtf encapsulated rtf.
|
191
|
+
#
|
192
|
+
# Some cases that the original didn't deal with have been patched up, eg from
|
193
|
+
# this chunk, where there are tags outside of the htmlrtf ignore block.
|
194
|
+
#
|
195
|
+
# "{\\*\\htmltag116 <br />}\\htmlrtf \\line \\htmlrtf0 \\line {\\*\\htmltag84 <a href..."
|
196
|
+
#
|
197
|
+
# We take the approach of ignoring all rtf tags not explicitly handled. A proper
|
198
|
+
# parse tree would be nicer to work with. will need to look for ruby rtf library
|
199
|
+
#
|
200
|
+
# Some of the original comment to the c code is excerpted here:
|
201
|
+
#
|
202
|
+
# Sometimes in MAPI, the PR_BODY_HTML property contains the HTML of a message.
|
203
|
+
# But more usually, the HTML is encoded inside the RTF body (which you get in the
|
204
|
+
# PR_RTF_COMPRESSED property). These routines concern the decoding of the HTML
|
205
|
+
# from this RTF body.
|
206
|
+
#
|
207
|
+
# An encoded htmlrtf file is a valid RTF document, but which contains additional
|
208
|
+
# html markup information in its comments, and sometimes contains the equivalent
|
209
|
+
# rtf markup outside the comments. Therefore, when it is displayed by a plain
|
210
|
+
# simple RTF reader, the html comments are ignored and only the rtf markup has
|
211
|
+
# effect. Typically, this rtf markup is not as rich as the html markup would have been.
|
212
|
+
# But for an html-aware reader (such as the code below), we can ignore all the
|
213
|
+
# rtf markup, and extract the html markup out of the comments, and get a valid
|
214
|
+
# html document.
|
215
|
+
#
|
216
|
+
# There are actually two kinds of html markup in comments. Most of them are
|
217
|
+
# prefixed by "\*\htmltagNNN", for some number NNN. But sometimes there's one
|
218
|
+
# prefixed by "\*\mhtmltagNNN" followed by "\*\htmltagNNN". In this case,
|
219
|
+
# the two are equivalent, but the m-tag is for a MIME Multipart/Mixed Message
|
220
|
+
# and contains tags that refer to content-ids (e.g. img src="cid:072344a7")
|
221
|
+
# while the normal tag just refers to a name (e.g. img src="fred.jpg")
|
222
|
+
# The code below keeps the m-tag and discards the normal tag.
|
223
|
+
# If there are any m-tags like this, then the message also contains an
|
224
|
+
# attachment with a PR_CONTENT_ID property e.g. "072344a7". Actually,
|
225
|
+
# sometimes the m-tag is e.g. img src="http://outlook/welcome.html" and the
|
226
|
+
# attachment has a PR_CONTENT_LOCATION "http://outlook/welcome.html" instead
|
227
|
+
# of a PR_CONTENT_ID.
|
228
|
+
#
|
229
|
+
def rtf2html rtf
|
230
|
+
scan = StringScanner.new rtf
|
231
|
+
# require \fromhtml. is this worth keeping? apparently you see \\fromtext if it
|
232
|
+
# was converted from plain text.
|
233
|
+
return nil unless rtf["\\fromhtml"]
|
234
|
+
# https://github.com/Scompler/ruby-msg/commit/c9046a2448bb6f4e08a8024d2b6637e2c12c23e5
|
235
|
+
# if scan.scan_until(/\\ansicpg/)
|
236
|
+
# code_page = "cp" + scan.scan(/\d+/)
|
237
|
+
# scan.pos = 0
|
238
|
+
# else
|
239
|
+
# code_page = 'ascii'
|
240
|
+
# end
|
241
|
+
|
242
|
+
last_pos = scan.pos
|
243
|
+
if scan.scan_until(/\\ansicpg/)
|
244
|
+
code_page = "cp" + scan.scan(/\d+/)
|
245
|
+
else
|
246
|
+
code_page = 'ascii'
|
247
|
+
end
|
248
|
+
scan.pos = last_pos
|
249
|
+
|
250
|
+
html = ''
|
251
|
+
ignore_tag = nil
|
252
|
+
# skip up to the first htmltag. return nil if we don't ever find one
|
253
|
+
return nil unless scan.scan_until /(?=\{\\\*\\htmltag)/
|
254
|
+
until scan.empty?
|
255
|
+
if scan.scan /\{/
|
256
|
+
elsif scan.scan /\}/
|
257
|
+
elsif scan.scan /\\\*\\htmltag(\d+) ?/
|
258
|
+
#p scan[1]
|
259
|
+
if ignore_tag == scan[1]
|
260
|
+
scan.scan_until /\}/
|
261
|
+
ignore_tag = nil
|
262
|
+
end
|
263
|
+
elsif scan.scan /\\\*\\mhtmltag(\d+) ?/
|
264
|
+
ignore_tag = scan[1]
|
265
|
+
elsif scan.scan /\\par ?/
|
266
|
+
html << "\r\n"
|
267
|
+
elsif scan.scan /\\tab ?/
|
268
|
+
html << "\t"
|
269
|
+
elsif scan.scan /\\'([0-9A-Za-z]{2})/
|
270
|
+
html << scan[1].hex.chr
|
271
|
+
elsif scan.scan /\\pntext/
|
272
|
+
scan.scan_until /\}/
|
273
|
+
elsif scan.scan /\\htmlrtf/
|
274
|
+
scan.scan_until /\\htmlrtf0 ?/
|
275
|
+
# a generic throw away unknown tags thing.
|
276
|
+
# the above 2 however, are handled specially
|
277
|
+
elsif scan.scan /\\[a-z-]+(\d+)? ?/
|
278
|
+
#elsif scan.scan /\\li(\d+) ?/
|
279
|
+
#elsif scan.scan /\\fi-(\d+) ?/
|
280
|
+
elsif scan.scan /[\r\n]/
|
281
|
+
elsif scan.scan /\\([{}\\])/
|
282
|
+
html << scan[1]
|
283
|
+
elsif scan.scan /(.)/
|
284
|
+
html << scan[1]
|
285
|
+
else
|
286
|
+
p :wtf
|
287
|
+
end
|
288
|
+
end
|
289
|
+
html.strip.empty? ? nil : html.force_encoding(code_page)
|
290
|
+
# https://github.com/Scompler/ruby-msg/commit/c9046a2448bb6f4e08a8024d2b6637e2c12c23e5
|
291
|
+
# html.strip.empty? ? nil : html.enode('utf-8', code_page)
|
292
|
+
end
|
293
|
+
|
294
|
+
module_function :rtf2html, :rtfdecompr
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
data/lib/mapi/types.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'ole/types'
|
3
|
+
|
4
|
+
module Mapi
|
5
|
+
Log = Logger.new_with_callstack
|
6
|
+
|
7
|
+
module Types
|
8
|
+
#
|
9
|
+
# Mapi property types, taken from http://msdn2.microsoft.com/en-us/library/bb147591.aspx.
|
10
|
+
#
|
11
|
+
# The fields are [mapi name, variant name, description]. Maybe I should just make it a
|
12
|
+
# struct.
|
13
|
+
#
|
14
|
+
# seen some synonyms here, like PT_I8 vs PT_LONG. seen stuff like PT_SRESTRICTION, not
|
15
|
+
# sure what that is. look at `grep ' PT_' data/mapitags.yaml | sort -u`
|
16
|
+
# also, it has stuff like PT_MV_BINARY, where _MV_ probably means multi value, and is
|
17
|
+
# likely just defined to | in 0x1000.
|
18
|
+
#
|
19
|
+
# Note that the last 2 are the only ones where the Mapi value differs from the Variant value
|
20
|
+
# for the corresponding variant type. Odd. Also, the last 2 are currently commented out here
|
21
|
+
# because of the clash.
|
22
|
+
#
|
23
|
+
# Note 2 - the strings here say VT_BSTR, but I don't have that defined in Ole::Types. Should
|
24
|
+
# maybe change them to match. I've also seen reference to PT_TSTRING, which is defined as some
|
25
|
+
# sort of get unicode first, and fallback to ansii or something.
|
26
|
+
#
|
27
|
+
DATA = {
|
28
|
+
0x0001 => ['PT_NULL', 'VT_NULL', 'Null (no valid data)'],
|
29
|
+
0x0002 => ['PT_SHORT', 'VT_I2', '2-byte integer (signed)'],
|
30
|
+
0x0003 => ['PT_LONG', 'VT_I4', '4-byte integer (signed)'],
|
31
|
+
0x0004 => ['PT_FLOAT', 'VT_R4', '4-byte real (floating point)'],
|
32
|
+
0x0005 => ['PT_DOUBLE', 'VT_R8', '8-byte real (floating point)'],
|
33
|
+
0x0006 => ['PT_CURRENCY', 'VT_CY', '8-byte integer (scaled by 10,000)'],
|
34
|
+
0x000a => ['PT_ERROR', 'VT_ERROR', 'SCODE value; 32-bit unsigned integer'],
|
35
|
+
0x000b => ['PT_BOOLEAN', 'VT_BOOL', 'Boolean'],
|
36
|
+
0x000d => ['PT_OBJECT', 'VT_UNKNOWN', 'Data object'],
|
37
|
+
0x001e => ['PT_STRING8', 'VT_BSTR', 'String'],
|
38
|
+
0x001f => ['PT_UNICODE', 'VT_BSTR', 'String'],
|
39
|
+
0x0040 => ['PT_SYSTIME', 'VT_DATE', '8-byte real (date in integer, time in fraction)'],
|
40
|
+
#0x0102 => ['PT_BINARY', 'VT_BLOB', 'Binary (unknown format)'],
|
41
|
+
#0x0102 => ['PT_CLSID', 'VT_CLSID', 'OLE GUID']
|
42
|
+
}
|
43
|
+
|
44
|
+
module Constants
|
45
|
+
DATA.each { |num, (mapi_name, variant_name, desc)| const_set mapi_name, num }
|
46
|
+
end
|
47
|
+
|
48
|
+
include Constants
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
data/lib/mapi/version.rb
ADDED
data/lib/mapi.rb
ADDED
data/ruby-msg.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__) + '/lib'
|
2
|
+
require 'mapi/version'
|
3
|
+
|
4
|
+
PKG_NAME = 'libis-mapi'
|
5
|
+
PKG_VERSION = Mapi::VERSION
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = PKG_NAME
|
9
|
+
s.version = PKG_VERSION
|
10
|
+
s.licenses = ['MIT']
|
11
|
+
s.summary = %q{Ruby Msg library.}
|
12
|
+
s.description = %q{A library for reading and converting Outlook msg and pst files (mapi message stores).}
|
13
|
+
s.authors = ['Charles Lowe', 'kenjiuno']
|
14
|
+
s.email = %q{ku@digitaldolphins.jp}
|
15
|
+
s.homepage = %q{https://github.com/HiraokaHyperTools/ruby-msg}
|
16
|
+
|
17
|
+
s.executables = ['mapitool']
|
18
|
+
s.files = ['Home.md', 'COPYING', 'Rakefile', 'ChangeLog', 'ruby-msg.gemspec']
|
19
|
+
s.files += Dir.glob('data/*.yaml')
|
20
|
+
s.files += Dir.glob('lib/**/*.rb')
|
21
|
+
s.files += Dir.glob('bin/*')
|
22
|
+
|
23
|
+
s.add_runtime_dependency 'ruby-ole', '~> 1.2', '>=1.2.8'
|
24
|
+
s.add_runtime_dependency 'vpim', '~> 13.0'
|
25
|
+
end
|
26
|
+
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: libis-mapi
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Charles Lowe
|
8
|
+
- kenjiuno
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2023-08-30 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: ruby-ole
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '1.2'
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.2.8
|
24
|
+
type: :runtime
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: !ruby/object:Gem::Requirement
|
27
|
+
requirements:
|
28
|
+
- - "~>"
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '1.2'
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.2.8
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: vpim
|
36
|
+
requirement: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '13.0'
|
41
|
+
type: :runtime
|
42
|
+
prerelease: false
|
43
|
+
version_requirements: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '13.0'
|
48
|
+
description: A library for reading and converting Outlook msg and pst files (mapi
|
49
|
+
message stores).
|
50
|
+
email: ku@digitaldolphins.jp
|
51
|
+
executables:
|
52
|
+
- mapitool
|
53
|
+
extensions: []
|
54
|
+
extra_rdoc_files: []
|
55
|
+
files:
|
56
|
+
- COPYING
|
57
|
+
- ChangeLog
|
58
|
+
- Home.md
|
59
|
+
- Rakefile
|
60
|
+
- bin/mapitool
|
61
|
+
- data/mapitags.yaml
|
62
|
+
- data/named_map.yaml
|
63
|
+
- data/types.yaml
|
64
|
+
- lib/mapi.rb
|
65
|
+
- lib/mapi/base.rb
|
66
|
+
- lib/mapi/convert.rb
|
67
|
+
- lib/mapi/convert/contact.rb
|
68
|
+
- lib/mapi/convert/note-mime.rb
|
69
|
+
- lib/mapi/convert/note-tmail.rb
|
70
|
+
- lib/mapi/helper.rb
|
71
|
+
- lib/mapi/mime.rb
|
72
|
+
- lib/mapi/msg.rb
|
73
|
+
- lib/mapi/property_set.rb
|
74
|
+
- lib/mapi/pst.rb
|
75
|
+
- lib/mapi/rtf.rb
|
76
|
+
- lib/mapi/types.rb
|
77
|
+
- lib/mapi/version.rb
|
78
|
+
- ruby-msg.gemspec
|
79
|
+
homepage: https://github.com/HiraokaHyperTools/ruby-msg
|
80
|
+
licenses:
|
81
|
+
- MIT
|
82
|
+
metadata: {}
|
83
|
+
post_install_message:
|
84
|
+
rdoc_options: []
|
85
|
+
require_paths:
|
86
|
+
- lib
|
87
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
requirements: []
|
98
|
+
rubygems_version: 3.3.7
|
99
|
+
signing_key:
|
100
|
+
specification_version: 4
|
101
|
+
summary: Ruby Msg library.
|
102
|
+
test_files: []
|