Upgraded Rails and RSpec
[monkeycharger.git] / vendor / rails / actionmailer / lib / action_mailer / vendor / tmail-1.1.0 / tmail / utils.rb
blob016330ffd582e3c4be44487f5c170d43e27e1331
1 =begin rdoc
3 = General Purpose TMail Utilities
5 =end
6 #--
7 # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
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:
17 # The above copyright notice and this permission notice shall be
18 # included in all copies or substantial portions of the Software.
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.
28 # Note: Originally licensed under LGPL v2+. Using MIT license for Rails
29 # with permission of Minero Aoki.
30 #++
32 module TMail
34   class SyntaxError < StandardError; end
37   def TMail.new_boundary
38     'mimepart_' + random_tag
39   end
41   def TMail.new_message_id( fqdn = nil )
42     fqdn ||= ::Socket.gethostname
43     "<#{random_tag()}@#{fqdn}.tmail>"
44   end
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
55   @uniq = 0
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'
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
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
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
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
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
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
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
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
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     }
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
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 )
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
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
203     MESSAGE_ID = /<[^\@>]+\@[^>\@]+>/
205     def message_id?( str )
206       MESSAGE_ID === str
207     end
210     MIME_ENCODED = /=\?[^\s?=]+\?[QB]\?[^\s?=]+\?=/i
212     def mime_encoded?( str )
213       MIME_ENCODED === str
214     end
215   
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
233       new
234     end
236     NKF_FLAGS = {
237       'EUC'  => '-e -m',
238       'SJIS' => '-s -m'
239     }
241     def to_kcode( str )
242       flag = NKF_FLAGS[$KCODE] or return str
243       NKF.nkf(flag, str)
244     end
246     RFC2231_ENCODED = /\A(?:iso-2022-jp|euc-jp|shift_jis|us-ascii)?'[a-z]*'/in
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
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
279   end