ruby-msg 1.2.17
Sign up to get free protection for your applications and to get access to all the features.
- data/FIXES +34 -0
- data/README +121 -0
- data/Rakefile +66 -0
- data/bin/msgtool +63 -0
- data/bin/oletool +35 -0
- data/data/mapitags.yaml +4168 -0
- data/data/named_map.yaml +114 -0
- data/data/types.yaml +15 -0
- data/lib/blah.rb +106 -0
- data/lib/mime-new.rb +210 -0
- data/lib/mime.rb +165 -0
- data/lib/msg/properties.rb +515 -0
- data/lib/msg/rtf.rb +236 -0
- data/lib/msg.rb +505 -0
- data/lib/ole/base.rb +5 -0
- data/lib/ole/file_system.rb +181 -0
- data/lib/ole/io_helpers.rb +184 -0
- data/lib/ole/storage.rb +927 -0
- data/lib/ole/types.rb +36 -0
- data/lib/orderedhash.rb +218 -0
- data/lib/rtf.rb +118 -0
- data/lib/support.rb +51 -0
- data/test/test_mime.rb +22 -0
- data/test/test_storage.rb +139 -0
- data/test/test_word_6.doc +0 -0
- data/test/test_word_95.doc +0 -0
- data/test/test_word_97.doc +0 -0
- metadata +73 -0
data/lib/ole/types.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'iconv'
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
require 'ole/base'
|
5
|
+
|
6
|
+
module Ole # :nodoc:
|
7
|
+
# FIXME
|
8
|
+
module Types
|
9
|
+
FROM_UTF16 = Iconv.new 'utf-8', 'utf-16le'
|
10
|
+
TO_UTF16 = Iconv.new 'utf-16le', 'utf-8'
|
11
|
+
EPOCH = DateTime.parse '1601-01-01'
|
12
|
+
# Create a +DateTime+ object from a struct +FILETIME+
|
13
|
+
# (http://msdn2.microsoft.com/en-us/library/ms724284.aspx).
|
14
|
+
#
|
15
|
+
# Converts +str+ to two 32 bit time values, comprising the high and low 32 bits of
|
16
|
+
# the 100's of nanoseconds since 1st january 1601 (Epoch).
|
17
|
+
def self.load_time str
|
18
|
+
low, high = str.unpack 'L2'
|
19
|
+
# we ignore these, without even warning about it
|
20
|
+
return nil if low == 0 and high == 0
|
21
|
+
time = EPOCH + (high * (1 << 32) + low) * 1e-7 / 86400 rescue return
|
22
|
+
# extra sanity check...
|
23
|
+
unless (1800...2100) === time.year
|
24
|
+
Log.warn "ignoring unlikely time value #{time.to_s}"
|
25
|
+
return nil
|
26
|
+
end
|
27
|
+
time
|
28
|
+
end
|
29
|
+
|
30
|
+
# Convert a binary guid into a plain string (will move to proper class later).
|
31
|
+
def self.load_guid str
|
32
|
+
"{%08x-%04x-%04x-%02x%02x-#{'%02x' * 6}}" % str.unpack('L S S CC C6')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
data/lib/orderedhash.rb
ADDED
@@ -0,0 +1,218 @@
|
|
1
|
+
# = OrderedHash
|
2
|
+
#
|
3
|
+
# == Version
|
4
|
+
# 1.2006.07.13 (change of the first number means Big Change)
|
5
|
+
#
|
6
|
+
# == Description
|
7
|
+
# Hash which preserves order of added items (like PHP array).
|
8
|
+
#
|
9
|
+
# == Usage
|
10
|
+
#
|
11
|
+
# (see examples directory under the ruby gems root directory)
|
12
|
+
#
|
13
|
+
# require 'rubygems'
|
14
|
+
# require 'ordered_hash'
|
15
|
+
#
|
16
|
+
# hsh = OrderedHash.new
|
17
|
+
# hsh['z'] = 1
|
18
|
+
# hsh['a'] = 2
|
19
|
+
# hsh['c'] = 3
|
20
|
+
# p hsh.keys # ['z','a','c']
|
21
|
+
#
|
22
|
+
# == Source
|
23
|
+
# http://simplypowerful.1984.cz/goodlibs/1.2006.07.13
|
24
|
+
#
|
25
|
+
# == Author
|
26
|
+
# jan molic (/mig/at_sign/1984/dot/cz/)
|
27
|
+
#
|
28
|
+
# == Thanks to
|
29
|
+
# Andrew Johnson for his suggestions and fixes of Hash[], merge, to_a, inspect and shift
|
30
|
+
# Desmond Dsouza for == fixes
|
31
|
+
#
|
32
|
+
# == Licence
|
33
|
+
# You can redistribute it and/or modify it under the same terms of Ruby's license;
|
34
|
+
# either the dual license version in 2003, or any later version.
|
35
|
+
#
|
36
|
+
|
37
|
+
class OrderedHash < Hash
|
38
|
+
|
39
|
+
attr_accessor :order
|
40
|
+
|
41
|
+
class << self
|
42
|
+
|
43
|
+
def [] *args
|
44
|
+
hsh = OrderedHash.new
|
45
|
+
if Hash === args[0]
|
46
|
+
hsh.replace args[0]
|
47
|
+
elsif (args.size % 2) != 0
|
48
|
+
raise ArgumentError, "odd number of elements for Hash"
|
49
|
+
else
|
50
|
+
hsh[args.shift] = args.shift while args.size > 0
|
51
|
+
end
|
52
|
+
hsh
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
def initialize(*a, &b)
|
58
|
+
super
|
59
|
+
@order = []
|
60
|
+
end
|
61
|
+
|
62
|
+
def store_only a,b
|
63
|
+
store a,b
|
64
|
+
end
|
65
|
+
|
66
|
+
alias orig_store store
|
67
|
+
|
68
|
+
def store a,b
|
69
|
+
@order.push a unless has_key? a
|
70
|
+
super a,b
|
71
|
+
end
|
72
|
+
|
73
|
+
alias []= store
|
74
|
+
|
75
|
+
def == hsh2
|
76
|
+
return hsh2==self if !hsh2.is_a?(OrderedHash)
|
77
|
+
return false if @order != hsh2.order
|
78
|
+
super hsh2
|
79
|
+
end
|
80
|
+
|
81
|
+
def clear
|
82
|
+
@order = []
|
83
|
+
super
|
84
|
+
end
|
85
|
+
|
86
|
+
def delete key
|
87
|
+
@order.delete key
|
88
|
+
super
|
89
|
+
end
|
90
|
+
|
91
|
+
def each_key
|
92
|
+
@order.each { |k| yield k }
|
93
|
+
self
|
94
|
+
end
|
95
|
+
|
96
|
+
def each_value
|
97
|
+
@order.each { |k| yield self[k] }
|
98
|
+
self
|
99
|
+
end
|
100
|
+
|
101
|
+
def each
|
102
|
+
@order.each { |k| yield k,self[k] }
|
103
|
+
self
|
104
|
+
end
|
105
|
+
|
106
|
+
alias each_pair each
|
107
|
+
|
108
|
+
def delete_if
|
109
|
+
@order.clone.each { |k|
|
110
|
+
delete k if yield
|
111
|
+
}
|
112
|
+
self
|
113
|
+
end
|
114
|
+
|
115
|
+
def values
|
116
|
+
ary = []
|
117
|
+
@order.each { |k| ary.push self[k] }
|
118
|
+
ary
|
119
|
+
end
|
120
|
+
|
121
|
+
def keys
|
122
|
+
@order
|
123
|
+
end
|
124
|
+
|
125
|
+
def invert
|
126
|
+
hsh2 = Hash.new
|
127
|
+
@order.each { |k| hsh2[self[k]] = k }
|
128
|
+
hsh2
|
129
|
+
end
|
130
|
+
|
131
|
+
def reject &block
|
132
|
+
self.dup.delete_if( &block )
|
133
|
+
end
|
134
|
+
|
135
|
+
def reject! &block
|
136
|
+
hsh2 = reject( &block )
|
137
|
+
self == hsh2 ? nil : hsh2
|
138
|
+
end
|
139
|
+
|
140
|
+
def replace hsh2
|
141
|
+
@order = hsh2.keys
|
142
|
+
super hsh2
|
143
|
+
end
|
144
|
+
|
145
|
+
def shift
|
146
|
+
key = @order.first
|
147
|
+
key ? [key,delete(key)] : super
|
148
|
+
end
|
149
|
+
|
150
|
+
def unshift k,v
|
151
|
+
unless self.include? k
|
152
|
+
@order.unshift k
|
153
|
+
orig_store(k,v)
|
154
|
+
true
|
155
|
+
else
|
156
|
+
false
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def push k,v
|
161
|
+
unless self.include? k
|
162
|
+
@order.push k
|
163
|
+
orig_store(k,v)
|
164
|
+
true
|
165
|
+
else
|
166
|
+
false
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def pop
|
171
|
+
key = @order.last
|
172
|
+
key ? [key,delete(key)] : nil
|
173
|
+
end
|
174
|
+
|
175
|
+
def first
|
176
|
+
self[@order.first]
|
177
|
+
end
|
178
|
+
|
179
|
+
def last
|
180
|
+
self[@order.last]
|
181
|
+
end
|
182
|
+
|
183
|
+
def to_a
|
184
|
+
ary = []
|
185
|
+
each { |k,v| ary << [k,v] }
|
186
|
+
ary
|
187
|
+
end
|
188
|
+
|
189
|
+
def to_s
|
190
|
+
self.to_a.to_s
|
191
|
+
end
|
192
|
+
|
193
|
+
def inspect
|
194
|
+
ary = []
|
195
|
+
each {|k,v| ary << k.inspect + "=>" + v.inspect}
|
196
|
+
'{' + ary.join(", ") + '}'
|
197
|
+
end
|
198
|
+
|
199
|
+
def update hsh2
|
200
|
+
hsh2.each { |k,v| self[k] = v }
|
201
|
+
self
|
202
|
+
end
|
203
|
+
|
204
|
+
alias :merge! update
|
205
|
+
|
206
|
+
def merge hsh2
|
207
|
+
self.dup update(hsh2)
|
208
|
+
end
|
209
|
+
|
210
|
+
def select
|
211
|
+
ary = []
|
212
|
+
each { |k,v| ary << [k,v] if yield k,v }
|
213
|
+
ary
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
217
|
+
|
218
|
+
#=end
|
data/lib/rtf.rb
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
#! /usr/bin/ruby -w
|
2
|
+
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
# this file is pretty crap, its just to ensure there is always something readable if
|
6
|
+
# there is an rtf only body, with no html encapsulation.
|
7
|
+
|
8
|
+
module RTF
|
9
|
+
class Tokenizer
|
10
|
+
def self.process io
|
11
|
+
while true do
|
12
|
+
case c = io.getc
|
13
|
+
when ?{; yield :open_group
|
14
|
+
when ?}; yield :close_group
|
15
|
+
when ?\\
|
16
|
+
case c = io.getc
|
17
|
+
when ?{, ?}, ?\\; yield :text, c.chr
|
18
|
+
when ?'; yield :text, [io.read(2)].pack('H*')
|
19
|
+
when ?a..?z, ?A..?Z
|
20
|
+
# read control word
|
21
|
+
str = c.chr
|
22
|
+
str << c while c = io.read(1) and c =~ /[a-zA-Z]/
|
23
|
+
neg = 1
|
24
|
+
neg = -1 and c = io.read(1) if c == '-'
|
25
|
+
num = if c =~ /[0-9]/
|
26
|
+
num = c
|
27
|
+
num << c while c = io.read(1) and c =~ /[0-9]/
|
28
|
+
num.to_i * neg
|
29
|
+
end
|
30
|
+
raise "invalid rtf stream" if neg == -1 and !num # ???? \blahblah- some text
|
31
|
+
io.seek(-1, IO::SEEK_CUR) if c != ' '
|
32
|
+
yield :control_word, str, num
|
33
|
+
when nil
|
34
|
+
raise "invalid rtf stream" # \EOF
|
35
|
+
else
|
36
|
+
# other kind of control symbol
|
37
|
+
yield :control_symbol, c.chr
|
38
|
+
end
|
39
|
+
when nil
|
40
|
+
return
|
41
|
+
when ?\r, ?\n
|
42
|
+
# ignore
|
43
|
+
else yield :text, c.chr
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class Converter
|
50
|
+
# crappy
|
51
|
+
def self.rtf2text str, format=:text
|
52
|
+
group = 0
|
53
|
+
text = ''
|
54
|
+
text << "<html>\n<body>" if format == :html
|
55
|
+
group_type = []
|
56
|
+
group_tags = []
|
57
|
+
RTF::Tokenizer.process(StringIO.new(str)) do |a, b, c|
|
58
|
+
add_text = ''
|
59
|
+
case a
|
60
|
+
when :open_group; group += 1; group_type[group] = nil; group_tags[group] = []
|
61
|
+
when :close_group; group_tags[group].reverse.each { |t| text << "</#{t}>" }; group -= 1;
|
62
|
+
when :control_word; # ignore
|
63
|
+
group_type[group] ||= b
|
64
|
+
# maybe change this to use utf8 where possible
|
65
|
+
add_text = if b == 'par' || b == 'line' || b == 'page'; "\n"
|
66
|
+
elsif b == 'tab' || b == 'cell'; "\t"
|
67
|
+
elsif b == 'endash' || b == 'emdash'; "-"
|
68
|
+
elsif b == 'emspace' || b == 'enspace' || b == 'qmspace'; " "
|
69
|
+
elsif b == 'ldblquote'; '"'
|
70
|
+
else ''
|
71
|
+
end
|
72
|
+
if b == 'b' || b == 'i' and format == :html
|
73
|
+
close = c == 0 ? '/' : ''
|
74
|
+
text << "<#{close}#{b}>"
|
75
|
+
if c == 0
|
76
|
+
group_tags[group].delete b
|
77
|
+
else
|
78
|
+
group_tags[group] << b
|
79
|
+
end
|
80
|
+
end
|
81
|
+
# lot of other ones belong in here.\
|
82
|
+
=begin
|
83
|
+
\bullet Bullet character.
|
84
|
+
\lquote Left single quotation mark.
|
85
|
+
\rquote Right single quotation mark.
|
86
|
+
\ldblquote Left double quotation mark.
|
87
|
+
\rdblquote
|
88
|
+
=end
|
89
|
+
when :control_symbol; # ignore
|
90
|
+
group_type[group] ||= b
|
91
|
+
add_text = ' ' if b == '~' # non-breakable space
|
92
|
+
add_text = '-' if b == '_' # non-breakable hypen
|
93
|
+
when :text
|
94
|
+
add_text = b if group <= 1 or group_type[group] == 'rtlch' && !group_type[0...group].include?('*')
|
95
|
+
end
|
96
|
+
if format == :html
|
97
|
+
text << add_text.gsub(/([<>&"'])/) do
|
98
|
+
ent = { '<' => 'lt', '>' => 'gt', '&' => 'amp', '"' => 'quot', "'" => 'apos' }[$1]
|
99
|
+
"&#{ent};"
|
100
|
+
end
|
101
|
+
text << '<br>' if add_text == "\n"
|
102
|
+
else
|
103
|
+
text << add_text
|
104
|
+
end
|
105
|
+
end
|
106
|
+
text << "</body>\n</html>\n" if format == :html
|
107
|
+
text
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
if $0 == __FILE__
|
113
|
+
#str = File.read('test.rtf')
|
114
|
+
str = YAML.load(open('rtfs.yaml'))[2]
|
115
|
+
#puts str
|
116
|
+
puts text
|
117
|
+
end
|
118
|
+
|
data/lib/support.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# A file with general support functions used by most files in the project.
|
4
|
+
#
|
5
|
+
|
6
|
+
require 'logger'
|
7
|
+
|
8
|
+
class File # :nodoc:
|
9
|
+
# for consistency with StringIO and others. makes more sense than forcing
|
10
|
+
# them to provide a #stat
|
11
|
+
def size
|
12
|
+
stat.size
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Symbol # :nodoc:
|
17
|
+
def to_proc
|
18
|
+
proc { |a| a.send self }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module Enumerable # :nodoc:
|
23
|
+
# 1.9 backport
|
24
|
+
def group_by
|
25
|
+
hash = Hash.new { |hash, key| hash[key] = [] }
|
26
|
+
each { |item| hash[yield(item)] << item }
|
27
|
+
hash
|
28
|
+
end
|
29
|
+
|
30
|
+
def sum initial=0
|
31
|
+
inject(initial) { |a, b| a + b }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Logger # :nodoc:
|
36
|
+
# A helper method for creating <tt>Logger</tt>s which produce call stack
|
37
|
+
# in their output
|
38
|
+
def self.new_with_callstack logdev=STDERR
|
39
|
+
log = Logger.new logdev
|
40
|
+
log.level = WARN
|
41
|
+
log.formatter = proc do |severity, time, progname, msg|
|
42
|
+
# find where we were called from, in our code
|
43
|
+
callstack = caller.dup
|
44
|
+
callstack.shift while callstack.first =~ /\/logger\.rb:\d+:in/
|
45
|
+
from = callstack.first.sub(/:in `(.*?)'/, ":\\1")
|
46
|
+
"[%s %s]\n%-7s%s\n" % [time.strftime('%H:%M:%S'), from, severity, msg.to_s]
|
47
|
+
end
|
48
|
+
log
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
data/test/test_mime.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#! /usr/bin/ruby -w
|
2
|
+
|
3
|
+
TEST_DIR = File.dirname __FILE__
|
4
|
+
$: << "#{TEST_DIR}/../lib"
|
5
|
+
|
6
|
+
require 'test/unit'
|
7
|
+
require 'mime'
|
8
|
+
|
9
|
+
class TestMime < Test::Unit::TestCase
|
10
|
+
# test out the way it partitions a message into parts
|
11
|
+
def test_parsing_no_multipart
|
12
|
+
mime = Mime.new "Header1: Value1\r\nHeader2: Value2\r\n\r\nBody text."
|
13
|
+
assert_equal ['Value1'], mime.headers['Header1']
|
14
|
+
assert_equal 'Body text.', mime.body
|
15
|
+
assert_equal false, mime.multipart?
|
16
|
+
assert_equal nil, mime.parts
|
17
|
+
# we get round trip conversion. this is mostly fluke, as orderedhash hasn't been
|
18
|
+
# added yet
|
19
|
+
assert_equal "Header1: Value1\r\nHeader2: Value2\r\n\r\nBody text.", mime.to_s
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,139 @@
|
|
1
|
+
#! /usr/bin/ruby -w
|
2
|
+
|
3
|
+
TEST_DIR = File.dirname __FILE__
|
4
|
+
$: << "#{TEST_DIR}/../lib"
|
5
|
+
|
6
|
+
require 'test/unit'
|
7
|
+
require 'ole/storage'
|
8
|
+
require 'digest/sha1'
|
9
|
+
require 'stringio'
|
10
|
+
|
11
|
+
#
|
12
|
+
# = TODO
|
13
|
+
#
|
14
|
+
# These tests could be a lot more complete.
|
15
|
+
#
|
16
|
+
|
17
|
+
class TestRangesIO < Test::Unit::TestCase
|
18
|
+
def setup
|
19
|
+
# why not :) ?
|
20
|
+
# repeats too
|
21
|
+
ranges = [100..200, 0..10, 100..150]
|
22
|
+
@io = RangesIO.new open("#{TEST_DIR}/test_storage.rb"), ranges, :close_parent => true
|
23
|
+
end
|
24
|
+
|
25
|
+
def teardown
|
26
|
+
@io.close
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_basic
|
30
|
+
assert_equal 160, @io.size
|
31
|
+
# this will map to the start of the file:
|
32
|
+
@io.pos = 100
|
33
|
+
assert_equal '#! /usr/bi', @io.read(10)
|
34
|
+
end
|
35
|
+
|
36
|
+
# should test range_and_offset specifically
|
37
|
+
|
38
|
+
def test_reading
|
39
|
+
# test selection of initial range, offset within that range
|
40
|
+
pos = 100
|
41
|
+
@io.seek pos
|
42
|
+
# test advancing of pos properly, by...
|
43
|
+
chunked = (0...10).map { @io.read 10 }.join
|
44
|
+
# given the file is 160 long:
|
45
|
+
assert_equal 60, chunked.length
|
46
|
+
@io.seek pos
|
47
|
+
# comparing with a flat read
|
48
|
+
assert_equal chunked, @io.read(60)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# should test resizeable and migrateable IO.
|
53
|
+
|
54
|
+
class TestStorageRead < Test::Unit::TestCase
|
55
|
+
def setup
|
56
|
+
@ole = Ole::Storage.open "#{TEST_DIR}/test_word_6.doc", 'rb'
|
57
|
+
end
|
58
|
+
|
59
|
+
def teardown
|
60
|
+
@ole.close
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_header
|
64
|
+
# should have further header tests, testing the validation etc.
|
65
|
+
assert_equal 17, @ole.header.to_a.length
|
66
|
+
assert_equal 117, @ole.header.dirent_start
|
67
|
+
assert_equal 1, @ole.header.num_bat
|
68
|
+
assert_equal 1, @ole.header.num_sbat
|
69
|
+
assert_equal 0, @ole.header.num_mbat
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_fat
|
73
|
+
# the fat block has all the numbers from 5..118 bar 117
|
74
|
+
bbat_table = [112] + ((5..118).to_a - [112, 117])
|
75
|
+
assert_equal bbat_table, @ole.bbat.table.reject { |i| i >= (1 << 32) - 3 }, 'bbat'
|
76
|
+
sbat_table = (1..43).to_a - [2, 3]
|
77
|
+
assert_equal sbat_table, @ole.sbat.table.reject { |i| i >= (1 << 32) - 3 }, 'sbat'
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_directories
|
81
|
+
assert_equal 5, @ole.dirents.length, 'have all directories'
|
82
|
+
# a more complicated one would be good for this
|
83
|
+
assert_equal 4, @ole.root.children.length, 'properly nested directories'
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_utf16_conversion
|
87
|
+
assert_equal 'Root Entry', @ole.root.name
|
88
|
+
assert_equal 'WordDocument', @ole.root.children[2].name
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_data
|
92
|
+
# test the ole storage type
|
93
|
+
type = 'Microsoft Word 6.0-Dokument'
|
94
|
+
assert_equal type, @ole.root["\001CompObj"].read[/^.{32}([^\x00]+)/m, 1]
|
95
|
+
# i was actually not loading data correctly before, so carefully check everything here
|
96
|
+
hashes = [-482597081, 285782478, 134862598, -863988921]
|
97
|
+
assert_equal hashes, @ole.root.children.map { |child| child.read.hash }
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class TestStorageWrite < Test::Unit::TestCase
|
102
|
+
def sha1 str
|
103
|
+
Digest::SHA1.hexdigest str
|
104
|
+
end
|
105
|
+
|
106
|
+
# FIXME
|
107
|
+
# don't really want to lock down the actual internal api's yet. this will just
|
108
|
+
# ensure for the time being that #flush continues to work properly. need a host
|
109
|
+
# of checks involving writes that resize their file bigger/smaller, that resize
|
110
|
+
# the bats to more blocks, that resizes the sb_blocks, that has migration etc.
|
111
|
+
def test_write_hash
|
112
|
+
io = StringIO.open File.read("#{TEST_DIR}/test_word_6.doc")
|
113
|
+
assert_equal '9974e354def8471225f548f82b8d81c701221af7', sha1(io.string)
|
114
|
+
Ole::Storage.open(io) { }
|
115
|
+
assert_equal 'efa8cfaf833b30b1d1d9381771ddaafdfc95305c', sha1(io.string)
|
116
|
+
# add a repack test here
|
117
|
+
Ole::Storage.open io, &:repack
|
118
|
+
assert_equal 'c8bb9ccacf0aaad33677e1b2a661ee6e66a48b5a', sha1(io.string)
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_plain_repack
|
122
|
+
io = StringIO.open File.read("#{TEST_DIR}/test_word_6.doc")
|
123
|
+
assert_equal '9974e354def8471225f548f82b8d81c701221af7', sha1(io.string)
|
124
|
+
Ole::Storage.open io, &:repack
|
125
|
+
# note equivalence to the above flush, repack, flush
|
126
|
+
assert_equal 'c8bb9ccacf0aaad33677e1b2a661ee6e66a48b5a', sha1(io.string)
|
127
|
+
end
|
128
|
+
|
129
|
+
def test_create_from_scratch_hash
|
130
|
+
io = StringIO.new
|
131
|
+
Ole::Storage.new(io) { }
|
132
|
+
assert_equal '6bb9d6c1cdf1656375e30991948d70c5fff63d57', sha1(io.string)
|
133
|
+
# more repack test, note invariance
|
134
|
+
Ole::Storage.open io, &:repack
|
135
|
+
assert_equal '6bb9d6c1cdf1656375e30991948d70c5fff63d57', sha1(io.string)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
|
Binary file
|
Binary file
|
Binary file
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.0
|
3
|
+
specification_version: 1
|
4
|
+
name: ruby-msg
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 1.2.17
|
7
|
+
date: 2007-05-13 00:00:00 +10:00
|
8
|
+
summary: Ruby Msg library.
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: aquasync@gmail.com
|
12
|
+
homepage: http://code.google.com/p/ruby-msg
|
13
|
+
rubyforge_project:
|
14
|
+
description: A library for reading Outlook msg files, and for converting them to RFC2822 emails.
|
15
|
+
autorequire: msg
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Charles Lowe
|
31
|
+
files:
|
32
|
+
- data/named_map.yaml
|
33
|
+
- data/types.yaml
|
34
|
+
- data/mapitags.yaml
|
35
|
+
- Rakefile
|
36
|
+
- README
|
37
|
+
- FIXES
|
38
|
+
- bin/msgtool
|
39
|
+
- bin/oletool
|
40
|
+
- lib/orderedhash.rb
|
41
|
+
- lib/blah.rb
|
42
|
+
- lib/mime-new.rb
|
43
|
+
- lib/rtf.rb
|
44
|
+
- lib/support.rb
|
45
|
+
- lib/mime.rb
|
46
|
+
- lib/msg.rb
|
47
|
+
- lib/ole/types.rb
|
48
|
+
- lib/ole/file_system.rb
|
49
|
+
- lib/ole/storage.rb
|
50
|
+
- lib/ole/io_helpers.rb
|
51
|
+
- lib/ole/base.rb
|
52
|
+
- lib/msg/rtf.rb
|
53
|
+
- lib/msg/properties.rb
|
54
|
+
- test/test_mime.rb
|
55
|
+
- test/test_storage.rb
|
56
|
+
- test/test_word_6.doc
|
57
|
+
- test/test_word_95.doc
|
58
|
+
- test/test_word_97.doc
|
59
|
+
test_files: []
|
60
|
+
|
61
|
+
rdoc_options: []
|
62
|
+
|
63
|
+
extra_rdoc_files: []
|
64
|
+
|
65
|
+
executables:
|
66
|
+
- msgtool
|
67
|
+
- oletool
|
68
|
+
extensions: []
|
69
|
+
|
70
|
+
requirements: []
|
71
|
+
|
72
|
+
dependencies: []
|
73
|
+
|