tmail 1.1.1

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.
Files changed (102) hide show
  1. data/LICENSE +21 -0
  2. data/README +157 -0
  3. data/bat/changelog +19 -0
  4. data/bat/clobber/package +10 -0
  5. data/bat/compile +42 -0
  6. data/bat/config.yaml +8 -0
  7. data/bat/prepare +8 -0
  8. data/bat/publish +51 -0
  9. data/bat/rdoc +42 -0
  10. data/bat/release +12 -0
  11. data/bat/setup +1616 -0
  12. data/bat/stats +138 -0
  13. data/bat/tag +25 -0
  14. data/bat/test +25 -0
  15. data/ext/tmail/Makefile +25 -0
  16. data/ext/tmail/base64/MANIFEST +4 -0
  17. data/ext/tmail/base64/base64.c +264 -0
  18. data/ext/tmail/base64/depend +1 -0
  19. data/ext/tmail/base64/extconf.rb +38 -0
  20. data/ext/tmail/scanner_c/MANIFEST +4 -0
  21. data/ext/tmail/scanner_c/depend +1 -0
  22. data/ext/tmail/scanner_c/extconf.rb +38 -0
  23. data/ext/tmail/scanner_c/scanner_c.c +582 -0
  24. data/lib/tmail.rb +4 -0
  25. data/lib/tmail/Makefile +19 -0
  26. data/lib/tmail/address.rb +245 -0
  27. data/lib/tmail/attachments.rb +47 -0
  28. data/lib/tmail/base64.rb +75 -0
  29. data/lib/tmail/compat.rb +39 -0
  30. data/lib/tmail/config.rb +71 -0
  31. data/lib/tmail/core_extensions.rb +67 -0
  32. data/lib/tmail/encode.rb +524 -0
  33. data/lib/tmail/header.rb +931 -0
  34. data/lib/tmail/index.rb +8 -0
  35. data/lib/tmail/interface.rb +540 -0
  36. data/lib/tmail/loader.rb +1 -0
  37. data/lib/tmail/mail.rb +507 -0
  38. data/lib/tmail/mailbox.rb +435 -0
  39. data/lib/tmail/mbox.rb +1 -0
  40. data/lib/tmail/net.rb +282 -0
  41. data/lib/tmail/obsolete.rb +137 -0
  42. data/lib/tmail/parser.rb +1475 -0
  43. data/lib/tmail/parser.y +381 -0
  44. data/lib/tmail/port.rb +379 -0
  45. data/lib/tmail/quoting.rb +142 -0
  46. data/lib/tmail/require_arch.rb +56 -0
  47. data/lib/tmail/scanner.rb +44 -0
  48. data/lib/tmail/scanner_r.rb +263 -0
  49. data/lib/tmail/stringio.rb +279 -0
  50. data/lib/tmail/tmail.rb +1 -0
  51. data/lib/tmail/utils.rb +281 -0
  52. data/lib/tmail/version.rb +38 -0
  53. data/meta/icli.yaml +16 -0
  54. data/meta/tmail-1.1.1.roll +24 -0
  55. data/sample/data/multipart +23 -0
  56. data/sample/data/normal +29 -0
  57. data/sample/data/sendtest +5 -0
  58. data/sample/data/simple +14 -0
  59. data/sample/data/test +27 -0
  60. data/sample/extract-attachements.rb +33 -0
  61. data/sample/from-check.rb +26 -0
  62. data/sample/multipart.rb +26 -0
  63. data/sample/parse-bench.rb +68 -0
  64. data/sample/parse-test.rb +19 -0
  65. data/sample/sendmail.rb +94 -0
  66. data/test/extctrl.rb +6 -0
  67. data/test/fixtures/raw_base64_decoded_string +0 -0
  68. data/test/fixtures/raw_base64_email +83 -0
  69. data/test/fixtures/raw_base64_encoded_string +1 -0
  70. data/test/fixtures/raw_email +14 -0
  71. data/test/fixtures/raw_email10 +20 -0
  72. data/test/fixtures/raw_email11 +34 -0
  73. data/test/fixtures/raw_email12 +32 -0
  74. data/test/fixtures/raw_email13 +29 -0
  75. data/test/fixtures/raw_email2 +114 -0
  76. data/test/fixtures/raw_email3 +70 -0
  77. data/test/fixtures/raw_email4 +59 -0
  78. data/test/fixtures/raw_email5 +19 -0
  79. data/test/fixtures/raw_email6 +20 -0
  80. data/test/fixtures/raw_email7 +66 -0
  81. data/test/fixtures/raw_email8 +47 -0
  82. data/test/fixtures/raw_email9 +28 -0
  83. data/test/fixtures/raw_email_quoted_with_0d0a +14 -0
  84. data/test/fixtures/raw_email_simple +11 -0
  85. data/test/fixtures/raw_email_with_illegal_boundary +58 -0
  86. data/test/fixtures/raw_email_with_multipart_mixed_quoted_boundary +50 -0
  87. data/test/fixtures/raw_email_with_nested_attachment +100 -0
  88. data/test/fixtures/raw_email_with_partially_quoted_subject +14 -0
  89. data/test/fixtures/raw_email_with_quoted_illegal_boundary +58 -0
  90. data/test/kcode.rb +14 -0
  91. data/test/test_address.rb +1128 -0
  92. data/test/test_attachments.rb +35 -0
  93. data/test/test_base64.rb +63 -0
  94. data/test/test_encode.rb +77 -0
  95. data/test/test_header.rb +885 -0
  96. data/test/test_helper.rb +2 -0
  97. data/test/test_mail.rb +623 -0
  98. data/test/test_mbox.rb +126 -0
  99. data/test/test_port.rb +430 -0
  100. data/test/test_scanner.rb +209 -0
  101. data/test/test_utils.rb +37 -0
  102. metadata +205 -0
@@ -0,0 +1,279 @@
1
+ =begin rdoc
2
+
3
+ = String handling class
4
+
5
+ =end
6
+ #--
7
+ # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
8
+ #
9
+ # Permission is hereby granted, free of charge, to any person obtaining
10
+ # a copy of this software and associated documentation files (the
11
+ # "Software"), to deal in the Software without restriction, including
12
+ # without limitation the rights to use, copy, modify, merge, publish,
13
+ # distribute, sublicense, and/or sell copies of the Software, and to
14
+ # permit persons to whom the Software is furnished to do so, subject to
15
+ # the following conditions:
16
+ #
17
+ # The above copyright notice and this permission notice shall be
18
+ # included in all copies or substantial portions of the Software.
19
+ #
20
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
+ #
28
+ # Note: Originally licensed under LGPL v2+. Using MIT license for Rails
29
+ # with permission of Minero Aoki.
30
+ #++
31
+
32
+ class StringInput#:nodoc:
33
+
34
+ include Enumerable
35
+
36
+ class << self
37
+
38
+ def new( str )
39
+ if block_given?
40
+ begin
41
+ f = super
42
+ yield f
43
+ ensure
44
+ f.close if f
45
+ end
46
+ else
47
+ super
48
+ end
49
+ end
50
+
51
+ alias open new
52
+
53
+ end
54
+
55
+ def initialize( str )
56
+ @src = str
57
+ @pos = 0
58
+ @closed = false
59
+ @lineno = 0
60
+ end
61
+
62
+ attr_reader :lineno
63
+
64
+ def string
65
+ @src
66
+ end
67
+
68
+ def inspect
69
+ "#<#{self.class}:#{@closed ? 'closed' : 'open'},src=#{@src[0,30].inspect}>"
70
+ end
71
+
72
+ def close
73
+ stream_check!
74
+ @pos = nil
75
+ @closed = true
76
+ end
77
+
78
+ def closed?
79
+ @closed
80
+ end
81
+
82
+ def pos
83
+ stream_check!
84
+ [@pos, @src.size].min
85
+ end
86
+
87
+ alias tell pos
88
+
89
+ def seek( offset, whence = IO::SEEK_SET )
90
+ stream_check!
91
+ case whence
92
+ when IO::SEEK_SET
93
+ @pos = offset
94
+ when IO::SEEK_CUR
95
+ @pos += offset
96
+ when IO::SEEK_END
97
+ @pos = @src.size - offset
98
+ else
99
+ raise ArgumentError, "unknown seek flag: #{whence}"
100
+ end
101
+ @pos = 0 if @pos < 0
102
+ @pos = [@pos, @src.size + 1].min
103
+ offset
104
+ end
105
+
106
+ def rewind
107
+ stream_check!
108
+ @pos = 0
109
+ end
110
+
111
+ def eof?
112
+ stream_check!
113
+ @pos > @src.size
114
+ end
115
+
116
+ def each( &block )
117
+ stream_check!
118
+ begin
119
+ @src.each(&block)
120
+ ensure
121
+ @pos = 0
122
+ end
123
+ end
124
+
125
+ def gets
126
+ stream_check!
127
+ if idx = @src.index(?\n, @pos)
128
+ idx += 1 # "\n".size
129
+ line = @src[ @pos ... idx ]
130
+ @pos = idx
131
+ @pos += 1 if @pos == @src.size
132
+ else
133
+ line = @src[ @pos .. -1 ]
134
+ @pos = @src.size + 1
135
+ end
136
+ @lineno += 1
137
+
138
+ line
139
+ end
140
+
141
+ def getc
142
+ stream_check!
143
+ ch = @src[@pos]
144
+ @pos += 1
145
+ @pos += 1 if @pos == @src.size
146
+ ch
147
+ end
148
+
149
+ def read( len = nil )
150
+ stream_check!
151
+ return read_all unless len
152
+ str = @src[@pos, len]
153
+ @pos += len
154
+ @pos += 1 if @pos == @src.size
155
+ str
156
+ end
157
+
158
+ alias sysread read
159
+
160
+ def read_all
161
+ stream_check!
162
+ return nil if eof?
163
+ rest = @src[@pos ... @src.size]
164
+ @pos = @src.size + 1
165
+ rest
166
+ end
167
+
168
+ def stream_check!
169
+ @closed and raise IOError, 'closed stream'
170
+ end
171
+
172
+ end
173
+
174
+
175
+ class StringOutput#:nodoc:
176
+
177
+ class << self
178
+
179
+ def new( str = '' )
180
+ if block_given?
181
+ begin
182
+ f = super
183
+ yield f
184
+ ensure
185
+ f.close if f
186
+ end
187
+ else
188
+ super
189
+ end
190
+ end
191
+
192
+ alias open new
193
+
194
+ end
195
+
196
+ def initialize( str = '' )
197
+ @dest = str
198
+ @closed = false
199
+ end
200
+
201
+ def close
202
+ @closed = true
203
+ end
204
+
205
+ def closed?
206
+ @closed
207
+ end
208
+
209
+ def string
210
+ @dest
211
+ end
212
+
213
+ alias value string
214
+ alias to_str string
215
+
216
+ def size
217
+ @dest.size
218
+ end
219
+
220
+ alias pos size
221
+
222
+ def inspect
223
+ "#<#{self.class}:#{@dest ? 'open' : 'closed'},#{object_id}>"
224
+ end
225
+
226
+ def print( *args )
227
+ stream_check!
228
+ raise ArgumentError, 'wrong # of argument (0 for >1)' if args.empty?
229
+ args.each do |s|
230
+ raise ArgumentError, 'nil not allowed' if s.nil?
231
+ @dest << s.to_s
232
+ end
233
+ nil
234
+ end
235
+
236
+ def puts( *args )
237
+ stream_check!
238
+ args.each do |str|
239
+ @dest << (s = str.to_s)
240
+ @dest << "\n" unless s[-1] == ?\n
241
+ end
242
+ @dest << "\n" if args.empty?
243
+ nil
244
+ end
245
+
246
+ def putc( ch )
247
+ stream_check!
248
+ @dest << ch.chr
249
+ nil
250
+ end
251
+
252
+ def printf( *args )
253
+ stream_check!
254
+ @dest << sprintf(*args)
255
+ nil
256
+ end
257
+
258
+ def write( str )
259
+ stream_check!
260
+ s = str.to_s
261
+ @dest << s
262
+ s.size
263
+ end
264
+
265
+ alias syswrite write
266
+
267
+ def <<( str )
268
+ stream_check!
269
+ @dest << str.to_s
270
+ self
271
+ end
272
+
273
+ private
274
+
275
+ def stream_check!
276
+ @closed and raise IOError, 'closed stream'
277
+ end
278
+
279
+ end
@@ -0,0 +1 @@
1
+ require 'tmail'
@@ -0,0 +1,281 @@
1
+ =begin rdoc
2
+
3
+ = General Purpose TMail Utilities
4
+
5
+ =end
6
+ #--
7
+ # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
8
+ #
9
+ # Permission is hereby granted, free of charge, to any person obtaining
10
+ # a copy of this software and associated documentation files (the
11
+ # "Software"), to deal in the Software without restriction, including
12
+ # without limitation the rights to use, copy, modify, merge, publish,
13
+ # distribute, sublicense, and/or sell copies of the Software, and to
14
+ # permit persons to whom the Software is furnished to do so, subject to
15
+ # the following conditions:
16
+ #
17
+ # The above copyright notice and this permission notice shall be
18
+ # included in all copies or substantial portions of the Software.
19
+ #
20
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
+ #
28
+ # Note: Originally licensed under LGPL v2+. Using MIT license for Rails
29
+ # with permission of Minero Aoki.
30
+ #++
31
+
32
+ module TMail
33
+
34
+ class SyntaxError < StandardError; end
35
+
36
+
37
+ def TMail.new_boundary
38
+ 'mimepart_' + random_tag
39
+ end
40
+
41
+ def TMail.new_message_id( fqdn = nil )
42
+ fqdn ||= ::Socket.gethostname
43
+ "<#{random_tag()}@#{fqdn}.tmail>"
44
+ end
45
+
46
+ def TMail.random_tag
47
+ @uniq += 1
48
+ t = Time.now
49
+ sprintf('%x%x_%x%x%d%x',
50
+ t.to_i, t.tv_usec,
51
+ $$, Thread.current.object_id, @uniq, rand(255))
52
+ end
53
+ private_class_method :random_tag
54
+
55
+ @uniq = 0
56
+
57
+ module TextUtils
58
+ # Defines characters per RFC that are OK for TOKENs, ATOMs, PHRASEs and CONTROL characters.
59
+
60
+ aspecial = '()<>[]:;.\\,"'
61
+ tspecial = '()<>[];:\\,"/?='
62
+ lwsp = " \t\r\n"
63
+ control = '\x00-\x1f\x7f-\xff'
64
+
65
+ ATOM_UNSAFE = /[#{Regexp.quote aspecial}#{control}#{lwsp}]/n
66
+ PHRASE_UNSAFE = /[#{Regexp.quote aspecial}#{control}]/n
67
+ TOKEN_UNSAFE = /[#{Regexp.quote tspecial}#{control}#{lwsp}]/n
68
+ CONTROL_CHAR = /[#{control}]/n
69
+
70
+ def atom_safe?( str )
71
+ # Returns true if the string supplied is free from characters not allowed as an ATOM
72
+ not ATOM_UNSAFE === str
73
+ end
74
+
75
+ def quote_atom( str )
76
+ # If the string supplied has ATOM unsafe characters in it, will return the string quoted
77
+ # in double quotes, otherwise returns the string unmodified
78
+ (ATOM_UNSAFE === str) ? dquote(str) : str
79
+ end
80
+
81
+ def quote_phrase( str )
82
+ # If the string supplied has PHRASE unsafe characters in it, will return the string quoted
83
+ # in double quotes, otherwise returns the string unmodified
84
+ (PHRASE_UNSAFE === str) ? dquote(str) : str
85
+ end
86
+
87
+ def token_safe?( str )
88
+ # Returns true if the string supplied is free from characters not allowed as a TOKEN
89
+ not TOKEN_UNSAFE === str
90
+ end
91
+
92
+ def quote_token( str )
93
+ # If the string supplied has TOKEN unsafe characters in it, will return the string quoted
94
+ # in double quotes, otherwise returns the string unmodified
95
+ (TOKEN_UNSAFE === str) ? dquote(str) : str
96
+ end
97
+
98
+ def dquote( str )
99
+ # Wraps supplied string in double quotes unless it is already wrapped
100
+ # Returns double quoted string
101
+ unless str =~ /^".*?"$/
102
+ '"' + str.gsub(/["\\]/n) {|s| '\\' + s } + '"'
103
+ else
104
+ str
105
+ end
106
+ end
107
+ private :dquote
108
+
109
+ def unquote( str )
110
+ # Unwraps supplied string from inside double quotes
111
+ # Returns unquoted string
112
+ str =~ /^"(.*?)"$/ ? $1 : str
113
+ end
114
+
115
+ def join_domain( arr )
116
+ arr.map {|i|
117
+ if /\A\[.*\]\z/ === i
118
+ i
119
+ else
120
+ quote_atom(i)
121
+ end
122
+ }.join('.')
123
+ end
124
+
125
+
126
+ ZONESTR_TABLE = {
127
+ 'jst' => 9 * 60,
128
+ 'eet' => 2 * 60,
129
+ 'bst' => 1 * 60,
130
+ 'met' => 1 * 60,
131
+ 'gmt' => 0,
132
+ 'utc' => 0,
133
+ 'ut' => 0,
134
+ 'nst' => -(3 * 60 + 30),
135
+ 'ast' => -4 * 60,
136
+ 'edt' => -4 * 60,
137
+ 'est' => -5 * 60,
138
+ 'cdt' => -5 * 60,
139
+ 'cst' => -6 * 60,
140
+ 'mdt' => -6 * 60,
141
+ 'mst' => -7 * 60,
142
+ 'pdt' => -7 * 60,
143
+ 'pst' => -8 * 60,
144
+ 'a' => -1 * 60,
145
+ 'b' => -2 * 60,
146
+ 'c' => -3 * 60,
147
+ 'd' => -4 * 60,
148
+ 'e' => -5 * 60,
149
+ 'f' => -6 * 60,
150
+ 'g' => -7 * 60,
151
+ 'h' => -8 * 60,
152
+ 'i' => -9 * 60,
153
+ # j not use
154
+ 'k' => -10 * 60,
155
+ 'l' => -11 * 60,
156
+ 'm' => -12 * 60,
157
+ 'n' => 1 * 60,
158
+ 'o' => 2 * 60,
159
+ 'p' => 3 * 60,
160
+ 'q' => 4 * 60,
161
+ 'r' => 5 * 60,
162
+ 's' => 6 * 60,
163
+ 't' => 7 * 60,
164
+ 'u' => 8 * 60,
165
+ 'v' => 9 * 60,
166
+ 'w' => 10 * 60,
167
+ 'x' => 11 * 60,
168
+ 'y' => 12 * 60,
169
+ 'z' => 0 * 60
170
+ }
171
+
172
+ def timezone_string_to_unixtime( str )
173
+ # Takes a time zone string from an EMail and converts it to Unix Time (seconds)
174
+ if m = /([\+\-])(\d\d?)(\d\d)/.match(str)
175
+ sec = (m[2].to_i * 60 + m[3].to_i) * 60
176
+ m[1] == '-' ? -sec : sec
177
+ else
178
+ min = ZONESTR_TABLE[str.downcase] or
179
+ raise SyntaxError, "wrong timezone format '#{str}'"
180
+ min * 60
181
+ end
182
+ end
183
+
184
+
185
+ WDAY = %w( Sun Mon Tue Wed Thu Fri Sat TMailBUG )
186
+ MONTH = %w( TMailBUG Jan Feb Mar Apr May Jun
187
+ Jul Aug Sep Oct Nov Dec TMailBUG )
188
+
189
+ def time2str( tm )
190
+ # [ruby-list:7928]
191
+ gmt = Time.at(tm.to_i)
192
+ gmt.gmtime
193
+ offset = tm.to_i - Time.local(*gmt.to_a[0,6].reverse).to_i
194
+
195
+ # DO NOT USE strftime: setlocale() breaks it
196
+ sprintf '%s, %s %s %d %02d:%02d:%02d %+.2d%.2d',
197
+ WDAY[tm.wday], tm.mday, MONTH[tm.month],
198
+ tm.year, tm.hour, tm.min, tm.sec,
199
+ *(offset / 60).divmod(60)
200
+ end
201
+
202
+
203
+ MESSAGE_ID = /<[^\@>]+\@[^>\@]+>/
204
+
205
+ def message_id?( str )
206
+ MESSAGE_ID === str
207
+ end
208
+
209
+
210
+ MIME_ENCODED = /=\?[^\s?=]+\?[QB]\?[^\s?=]+\?=/i
211
+
212
+ def mime_encoded?( str )
213
+ MIME_ENCODED === str
214
+ end
215
+
216
+
217
+ def decode_params( hash )
218
+ new = Hash.new
219
+ encoded = nil
220
+ hash.each do |key, value|
221
+ if m = /\*(?:(\d+)\*)?\z/.match(key)
222
+ ((encoded ||= {})[m.pre_match] ||= [])[(m[1] || 0).to_i] = value
223
+ else
224
+ new[key] = to_kcode(value)
225
+ end
226
+ end
227
+ if encoded
228
+ encoded.each do |key, strings|
229
+ new[key] = decode_RFC2231(strings.join(''))
230
+ end
231
+ end
232
+
233
+ new
234
+ end
235
+
236
+ NKF_FLAGS = {
237
+ 'EUC' => '-e -m',
238
+ 'SJIS' => '-s -m'
239
+ }
240
+
241
+ def to_kcode( str )
242
+ flag = NKF_FLAGS[$KCODE] or return str
243
+ NKF.nkf(flag, str)
244
+ end
245
+
246
+ RFC2231_ENCODED = /\A(?:iso-2022-jp|euc-jp|shift_jis|us-ascii)?'[a-z]*'/in
247
+
248
+ def decode_RFC2231( str )
249
+ m = RFC2231_ENCODED.match(str) or return str
250
+ begin
251
+ NKF.nkf(NKF_FLAGS[$KCODE],
252
+ m.post_match.gsub(/%[\da-f]{2}/in) {|s| s[1,2].hex.chr })
253
+ rescue
254
+ m.post_match.gsub(/%[\da-f]{2}/in, "")
255
+ end
256
+ end
257
+
258
+ def quote_boundary
259
+ # Make sure the Content-Type boundary= parameter is quoted if it contains illegal characters
260
+ # (to ensure any special characters in the boundary text are escaped from the parser
261
+ # (such as = in MS Outlook's boundary text))
262
+ if @body =~ /^(.*)boundary=(.*)$/m
263
+ preamble = $1
264
+ remainder = $2
265
+ if remainder =~ /;/
266
+ remainder =~ /^(.*)(;.*)$/m
267
+ boundary_text = $1
268
+ post = $2.chomp
269
+ else
270
+ boundary_text = remainder.chomp
271
+ end
272
+ if boundary_text =~ /[\/\?\=]/
273
+ boundary_text = "\"#{boundary_text}\"" unless boundary_text =~ /^".*?"$/
274
+ @body = "#{preamble}boundary=#{boundary_text}#{post}"
275
+ end
276
+ end
277
+ end
278
+
279
+ end
280
+
281
+ end