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 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