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 CHANGED
@@ -1,19 +1,15 @@
1
1
  = Introduction
2
2
 
3
- Generally, the goal of the project is the conversion of .msg files
4
- into proper rfc2822 emails, independent of outlook, or any platform
5
- dependencies etc. In fact its currently pure ruby, so it should be
6
- easy to get started with.
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
- There's also work-in-progess pst support (unfortunately outlook 97
9
- only currently), based on libpst, making this project more of a general
10
- ruby mapi message store conversion library now (though some significant
11
- cleaning up has to happen first).
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 msg library, where conversion to and working
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 msg files to standard formats, like rfc2822
30
- emails, vCards, etc.
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 files
61
- to .eml, or to convert a batch to an mbox format file. See help for
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
- msgtool -c some_email.msg > some_email.eml
65
- msgtool -m *.msg > mbox
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
- * OleDetails[http://code.google.com/p/ruby-ole/wiki/OleDetails]
125
+ * PstDetails[http://code.google.com/p/ruby-msg/wiki/PstDetails]
113
126
 
114
- * msgconv[http://www.matijs.net/software/msgconv/], the original
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 files, and for converting them to RFC2822 emails.}
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.4'
67
+ s.add_dependency 'ruby-ole', '>=1.2.8'
68
68
  s.add_dependency 'vpim', '>=0.360'
69
69
  end
70
70
 
@@ -2,7 +2,7 @@ require 'mapi/types'
2
2
  require 'mapi/property_set'
3
3
 
4
4
  module Mapi
5
- VERSION = '1.4.0'
5
+ VERSION = '1.5.0'
6
6
 
7
7
  #
8
8
  # Mapi::Item is the base class used for all mapi objects, and is purely a
@@ -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
- # don't know why i bother, but if we can, we try to sort recipients by the numerical part
85
- # of the ole name, or just leave it if we can't
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?
@@ -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
- guids = [PS_PUBLIC_STRINGS] + guids_obj.read.scan(/.{16}/m).map do |str|
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
- props = props_obj.read.scan(/.{8}/m).map do |str|
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 = *str.unpack('V')
197
- len = *names_data[str_off, 4].unpack('V')
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[pad..-1].scan(/.{16}/m).each do |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
@@ -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
@@ -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.getc : flags >> 1
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.getc, io.getc
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.getc
69
+ rtf << buf[wp] = io.getbyte.chr
62
70
  wp = (wp + 1) % 4096
63
71
  end
64
72
  end
@@ -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
- class Mime
25
- Hash = begin
26
- require 'orderedhash'
27
- OrderedHash
28
- rescue LoadError
29
- Hash
30
- end
31
-
32
- attr_reader :headers, :body, :parts, :content_type, :preamble, :epilogue
33
-
34
- # Create a Mime object using +str+ as an initial serialization, which must contain headers
35
- # and a body (even if empty). Needs work.
36
- def initialize str, ignore_body=false
37
- headers, @body = $~[1..-1] if str[/(.*?\r?\n)(?:\r?\n(.*))?\Z/m]
38
-
39
- @headers = Hash.new { |hash, key| hash[key] = [] }
40
- @body ||= ''
41
- headers.to_s.scan(/^\S+:\s*.*(?:\n\t.*)*/).each do |header|
42
- @headers[header[/(\S+):/, 1]] << header[/\S+:\s*(.*)/m, 1].gsub(/\s+/m, ' ').strip # this is kind of wrong
43
- end
44
-
45
- # don't have to have content type i suppose
46
- @content_type, attrs = nil, {}
47
- if content_type = @headers['Content-Type'][0]
48
- @content_type, attrs = Mime.split_header content_type
49
- end
50
-
51
- return if ignore_body
52
-
53
- if multipart?
54
- if body.empty?
55
- @preamble = ''
56
- @epilogue = ''
57
- @parts = []
58
- else
59
- # we need to split the message at the boundary
60
- boundary = attrs['boundary'] or raise "no boundary for multipart message"
61
-
62
- # splitting the body:
63
- parts = body.split(/--#{Regexp.quote boundary}/m)
64
- unless parts[-1] =~ /^--/; warn "bad multipart boundary (missing trailing --)"
65
- else parts[-1][0..1] = ''
66
- end
67
- parts.each_with_index do |part, i|
68
- part =~ /^(\r?\n)?(.*?)(\r?\n)?\Z/m
69
- part.replace $2
70
- warn "bad multipart boundary" if (1...parts.length-1) === i and !($1 && $3)
71
- end
72
- @preamble = parts.shift
73
- @epilogue = parts.pop
74
- @parts = parts.map { |part| Mime.new part }
75
- end
76
- end
77
- end
78
-
79
- def multipart?
80
- @content_type && @content_type =~ /^multipart/ ? true : false
81
- end
82
-
83
- def inspect
84
- # add some extra here.
85
- "#<Mime content_type=#{@content_type.inspect}>"
86
- end
87
-
88
- def to_tree
89
- if multipart?
90
- str = "- #{inspect}\n"
91
- parts.each_with_index do |part, i|
92
- last = i == parts.length - 1
93
- part.to_tree.split(/\n/).each_with_index do |line, j|
94
- str << " #{last ? (j == 0 ? "\\" : ' ') : '|'}" + line + "\n"
95
- end
96
- end
97
- str
98
- else
99
- "- #{inspect}\n"
100
- end
101
- end
102
-
103
- def to_s opts={}
104
- opts = {:boundary_counter => 0}.merge opts
105
- if multipart?
106
- boundary = Mime.make_boundary opts[:boundary_counter] += 1, self
107
- @body = [preamble, parts.map { |part| "\r\n" + part.to_s(opts) + "\r\n" }, "--\r\n" + epilogue].
108
- flatten.join("\r\n--" + boundary)
109
- content_type, attrs = Mime.split_header @headers['Content-Type'][0]
110
- attrs['boundary'] = boundary
111
- @headers['Content-Type'] = [([content_type] + attrs.map { |key, val| %{#{key}="#{val}"} }).join('; ')]
112
- end
113
-
114
- str = ''
115
- @headers.each do |key, vals|
116
- vals.each { |val| str << "#{key}: #{val}\r\n" }
117
- end
118
- str << "\r\n" + @body
119
- end
120
-
121
- def self.split_header header
122
- # FIXME: haven't read standard. not sure what its supposed to do with " in the name, or if other
123
- # escapes are allowed. can't test on windows as " isn't allowed anyway. can be fixed with more
124
- # accurate parser later.
125
- # maybe move to some sort of Header class. but not all headers should be of it i suppose.
126
- # at least add a join_header then, taking name and {}. for use in Mime#to_s (for boundary
127
- # rewrite), and Attachment#to_mime, among others...
128
- attrs = {}
129
- header.scan(/;\s*([^\s=]+)\s*=\s*("[^"]*"|[^\s;]*)\s*/m).each do |key, value|
130
- if attrs[key]; warn "ignoring duplicate header attribute #{key.inspect}"
131
- else attrs[key] = value[/^"/] ? value[1..-2] : value
132
- end
133
- end
134
-
135
- [header[/^[^;]+/].strip, attrs]
136
- end
137
-
138
- # +i+ is some value that should be unique for all multipart boundaries for a given message
139
- def self.make_boundary i, extra_obj = Mime
140
- "----_=_NextPart_#{'%03d' % i}_#{'%08x' % extra_obj.object_id}.#{'%08x' % Time.now}"
141
- end
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
+
@@ -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
- version: 1.4.0
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: 2008-10-12 00:00:00 +11:00
17
+ date: 2011-05-18 00:00:00 -04:00
13
18
  default_executable:
14
19
  dependencies:
15
- - !ruby/object:Gem::Dependency
16
- name: ruby-ole
17
- type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
20
- requirements:
21
- - - ">="
22
- - !ruby/object:Gem::Version
23
- version: 1.2.4
24
- version:
25
- - !ruby/object:Gem::Dependency
26
- name: vpim
27
- type: :runtime
28
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: "0.360"
34
- version:
35
- description: A library for reading Outlook msg files, and for converting them to RFC2822 emails.
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/types.yaml
45
- - data/named_map.yaml
46
- - data/mapitags.yaml
47
- - Rakefile
48
- - README
49
- - FIXES
50
- - bin/mapitool
51
- - lib/orderedhash.rb
52
- - lib/rtf.rb
53
- - lib/mapi/rtf.rb
54
- - lib/mapi/pst.rb
55
- - lib/mapi/msg.rb
56
- - lib/mapi/convert.rb
57
- - lib/mapi/property_set.rb
58
- - lib/mapi/types.rb
59
- - lib/mapi/convert/note-tmail.rb
60
- - lib/mapi/convert/note-mime.rb
61
- - lib/mapi/convert/contact.rb
62
- - lib/mime.rb
63
- - lib/mapi.rb
64
- - test/test_types.rb
65
- - test/test_convert_note.rb
66
- - test/test_mime.rb
67
- - test/test_msg.rb
68
- - test/test_property_set.rb
69
- - test/test_convert_contact.rb
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
- - !ruby/object:Gem::Version
86
- version: "0"
87
- version:
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
- - !ruby/object:Gem::Version
92
- version: "0"
93
- version:
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.2.0
113
+ rubygems_version: 1.3.6
98
114
  signing_key:
99
- specification_version: 2
115
+ specification_version: 3
100
116
  summary: Ruby Msg library.
101
117
  test_files: []
102
118