diff options
Diffstat (limited to 'lib/vendor/tmail-1.2.7/tmail/utils.rb')
-rw-r--r-- | lib/vendor/tmail-1.2.7/tmail/utils.rb | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/lib/vendor/tmail-1.2.7/tmail/utils.rb b/lib/vendor/tmail-1.2.7/tmail/utils.rb new file mode 100644 index 000000000..3590ca248 --- /dev/null +++ b/lib/vendor/tmail-1.2.7/tmail/utils.rb @@ -0,0 +1,362 @@ +# encoding: us-ascii +#-- +# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net> +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Note: Originally licensed under LGPL v2+. Using MIT license for Rails +# with permission of Minero Aoki. +#++ + +# = TMail - The EMail Swiss Army Knife for Ruby +# +# The TMail library provides you with a very complete way to handle and manipulate EMails +# from within your Ruby programs. +# +# Used as the backbone for email handling by the Ruby on Rails and Nitro web frameworks as +# well as a bunch of other Ruby apps including the Ruby-Talk mailing list to newsgroup email +# gateway, it is a proven and reliable email handler that won't let you down. +# +# Originally created by Minero Aoki, TMail has been recently picked up by Mikel Lindsaar and +# is being actively maintained. Numerous backlogged bug fixes have been applied as well as +# Ruby 1.9 compatibility and a swath of documentation to boot. +# +# TMail allows you to treat an email totally as an object and allow you to get on with your +# own programming without having to worry about crafting the perfect email address validation +# parser, or assembling an email from all it's component parts. +# +# TMail handles the most complex part of the email - the header. It generates and parses +# headers and provides you with instant access to their innards through simple and logically +# named accessor and setter methods. +# +# TMail also provides a wrapper to Net/SMTP as well as Unix Mailbox handling methods to +# directly read emails from your unix mailbox, parse them and use them. +# +# Following is the comprehensive list of methods to access TMail::Mail objects. You can also +# check out TMail::Mail, TMail::Address and TMail::Headers for other lists. +module TMail + + # Provides an exception to throw on errors in Syntax within TMail's parsers + class SyntaxError < StandardError; end + + # Provides a new email boundary to separate parts of the email. This is a random + # string based off the current time, so should be fairly unique. + # + # For Example: + # + # TMail.new_boundary + # #=> "mimepart_47bf656968207_25a8fbb80114" + # TMail.new_boundary + # #=> "mimepart_47bf66051de4_25a8fbb80240" + def TMail.new_boundary + 'mimepart_' + random_tag + end + + # Provides a new email message ID. You can use this to generate unique email message + # id's for your email so you can track them. + # + # Optionally takes a fully qualified domain name (default to the current hostname + # returned by Socket.gethostname) that will be appended to the message ID. + # + # For Example: + # + # email.message_id = TMail.new_message_id + # #=> "<47bf66845380e_25a8fbb80332@baci.local.tmail>" + # email.to_s + # #=> "Message-Id: <47bf668b633f1_25a8fbb80475@baci.local.tmail>\n\n" + # email.message_id = TMail.new_message_id("lindsaar.net") + # #=> "<47bf668b633f1_25a8fbb80475@lindsaar.net.tmail>" + # email.to_s + # #=> "Message-Id: <47bf668b633f1_25a8fbb80475@lindsaar.net.tmail>\n\n" + def TMail.new_message_id( fqdn = nil ) + fqdn ||= ::Socket.gethostname + "<#{random_tag()}@#{fqdn}.tmail>" + end + + #:stopdoc: + def TMail.random_tag #:nodoc: + @uniq += 1 + t = Time.now + sprintf('%x%x_%x%x%d%x', + t.to_i, t.tv_usec, + $$, Thread.current.object_id, @uniq, rand(255)) + end + private_class_method :random_tag + + @uniq = 0 + + #:startdoc: + + # Text Utils provides a namespace to define TOKENs, ATOMs, PHRASEs and CONTROL characters that + # are OK per RFC 2822. + # + # It also provides methods you can call to determine if a string is safe + module TextUtils + + aspecial = %Q|()<>[]:;.\\,"| + tspecial = %Q|()<>[];:\\,"/?=| + lwsp = %Q| \t\r\n| + control = %Q|\x00-\x1f\x7f-\xff| + + CONTROL_CHAR = /[#{control}]/n + ATOM_UNSAFE = /[#{Regexp.quote aspecial}#{control}#{lwsp}]/n + PHRASE_UNSAFE = /[#{Regexp.quote aspecial}#{control}]/n + TOKEN_UNSAFE = /[#{Regexp.quote tspecial}#{control}#{lwsp}]/n + + # Returns true if the string supplied is free from characters not allowed as an ATOM + def atom_safe?( str ) + not ATOM_UNSAFE === str + end + + # If the string supplied has ATOM unsafe characters in it, will return the string quoted + # in double quotes, otherwise returns the string unmodified + def quote_atom( str ) + (ATOM_UNSAFE === str) ? dquote(str) : str + end + + # If the string supplied has PHRASE unsafe characters in it, will return the string quoted + # in double quotes, otherwise returns the string unmodified + def quote_phrase( str ) + (PHRASE_UNSAFE === str) ? dquote(str) : str + end + + # Returns true if the string supplied is free from characters not allowed as a TOKEN + def token_safe?( str ) + not TOKEN_UNSAFE === str + end + + # If the string supplied has TOKEN unsafe characters in it, will return the string quoted + # in double quotes, otherwise returns the string unmodified + def quote_token( str ) + (TOKEN_UNSAFE === str) ? dquote(str) : str + end + + # Wraps supplied string in double quotes unless it is already wrapped + # Returns double quoted string + def dquote( str ) #:nodoc: + unless str =~ /^".*?"$/ + '"' + str.gsub(/["\\]/n) {|s| '\\' + s } + '"' + else + str + end + end + private :dquote + + # Unwraps supplied string from inside double quotes + # Returns unquoted string + def unquote( str ) + str =~ /^"(.*?)"$/m ? $1 : str + end + + # Provides a method to join a domain name by it's parts and also makes it + # ATOM safe by quoting it as needed + def join_domain( arr ) + arr.map {|i| + if /\A\[.*\]\z/ === i + i + else + quote_atom(i) + end + }.join('.') + end + + #:stopdoc: + ZONESTR_TABLE = { + 'jst' => 9 * 60, + 'eet' => 2 * 60, + 'bst' => 1 * 60, + 'met' => 1 * 60, + 'gmt' => 0, + 'utc' => 0, + 'ut' => 0, + 'nst' => -(3 * 60 + 30), + 'ast' => -4 * 60, + 'edt' => -4 * 60, + 'est' => -5 * 60, + 'cdt' => -5 * 60, + 'cst' => -6 * 60, + 'mdt' => -6 * 60, + 'mst' => -7 * 60, + 'pdt' => -7 * 60, + 'pst' => -8 * 60, + 'a' => -1 * 60, + 'b' => -2 * 60, + 'c' => -3 * 60, + 'd' => -4 * 60, + 'e' => -5 * 60, + 'f' => -6 * 60, + 'g' => -7 * 60, + 'h' => -8 * 60, + 'i' => -9 * 60, + # j not use + 'k' => -10 * 60, + 'l' => -11 * 60, + 'm' => -12 * 60, + 'n' => 1 * 60, + 'o' => 2 * 60, + 'p' => 3 * 60, + 'q' => 4 * 60, + 'r' => 5 * 60, + 's' => 6 * 60, + 't' => 7 * 60, + 'u' => 8 * 60, + 'v' => 9 * 60, + 'w' => 10 * 60, + 'x' => 11 * 60, + 'y' => 12 * 60, + 'z' => 0 * 60 + } + #:startdoc: + + # Takes a time zone string from an EMail and converts it to Unix Time (seconds) + def timezone_string_to_unixtime( str ) + if m = /([\+\-])(\d\d?)(\d\d)/.match(str) + sec = (m[2].to_i * 60 + m[3].to_i) * 60 + m[1] == '-' ? -sec : sec + else + min = ZONESTR_TABLE[str.downcase] or + raise SyntaxError, "wrong timezone format '#{str}'" + min * 60 + end + end + + #:stopdoc: + WDAY = %w( Sun Mon Tue Wed Thu Fri Sat TMailBUG ) + MONTH = %w( TMailBUG Jan Feb Mar Apr May Jun + Jul Aug Sep Oct Nov Dec TMailBUG ) + + def time2str( tm ) + # [ruby-list:7928] + gmt = Time.at(tm.to_i) + gmt.gmtime + offset = tm.to_i - Time.local(*gmt.to_a[0,6].reverse).to_i + + # DO NOT USE strftime: setlocale() breaks it + sprintf '%s, %s %s %d %02d:%02d:%02d %+.2d%.2d', + WDAY[tm.wday], tm.mday, MONTH[tm.month], + tm.year, tm.hour, tm.min, tm.sec, + *(offset / 60).divmod(60) + end + + + MESSAGE_ID = /<[^\@>]+\@[^>]+>/ + + def message_id?( str ) + MESSAGE_ID === str + end + + + MIME_ENCODED = /=\?[^\s?=]+\?[QB]\?[^\s?=]+\?=/i + + def mime_encoded?( str ) + MIME_ENCODED === str + end + + + def decode_params( hash ) + new = Hash.new + encoded = nil + hash.each do |key, value| + if m = /\*(?:(\d+)\*)?\z/.match(key) + ((encoded ||= {})[m.pre_match] ||= [])[(m[1] || 0).to_i] = value + else + new[key] = to_kcode(value) + end + end + if encoded + encoded.each do |key, strings| + new[key] = decode_RFC2231(strings.join('')) + end + end + + new + end + + NKF_FLAGS = { + 'EUC' => '-e -m', + 'SJIS' => '-s -m' + } + + def to_kcode( str ) + flag = NKF_FLAGS[TMail.KCODE] or return str + NKF.nkf(flag, str) + end + + RFC2231_ENCODED = /\A(?:iso-2022-jp|euc-jp|shift_jis|us-ascii)?'[a-z]*'/in + + def decode_RFC2231( str ) + m = RFC2231_ENCODED.match(str) or return str + begin + to_kcode(m.post_match.gsub(/%[\da-f]{2}/in) {|s| s[1,2].hex.chr }) + rescue + m.post_match.gsub(/%[\da-f]{2}/in, "") + end + end + + def quote_boundary + # Make sure the Content-Type boundary= parameter is quoted if it contains illegal characters + # (to ensure any special characters in the boundary text are escaped from the parser + # (such as = in MS Outlook's boundary text)) + if @body =~ /^(.*)boundary=(.*)$/m + preamble = $1 + remainder = $2 + if remainder =~ /;/ + remainder =~ /^(.*?)(;.*)$/m + boundary_text = $1 + post = $2.chomp + else + boundary_text = remainder.chomp + end + if boundary_text =~ /[\/\?\=]/ + boundary_text = "\"#{boundary_text}\"" unless boundary_text =~ /^".*?"$/ + @body = "#{preamble}boundary=#{boundary_text}#{post}" + end + end + end + + # AppleMail generates illegal character contained Content-Type parameter like: + # name==?ISO-2022-JP?B?...=?= + # so quote. (This case is only value fits in one line.) + def quote_unquoted_bencode + @body = @body.gsub(%r"(;\s+[-a-z]+=)(=\?.+?)([;\r\n ]|\z)"m) { + head, should_quoted, tail = $~.captures + # head: "; name=" + # should_quoted: "=?ISO-2022-JP?B?...=?=" + + head << quote_token(should_quoted) << tail + } + end + + # AppleMail generates name=filename attributes in the content type that + # contain spaces. Need to handle this so the TMail Parser can. + def quote_unquoted_name + @body = @body.gsub(%r|(name=)([\w\s.]+)(.*)|m) { + head, should_quoted, tail = $~.captures + # head: "; name=" + # should_quoted: "=?ISO-2022-JP?B?...=?=" + head << quote_token(should_quoted) << tail + } + end + + #:startdoc: + + end + +end |