aboutsummaryrefslogtreecommitdiffstats
path: root/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/header.rb
diff options
context:
space:
mode:
authorJeremy Kemper <jeremy@bitsweat.net>2008-05-19 19:04:28 -0700
committerJeremy Kemper <jeremy@bitsweat.net>2008-05-19 19:04:28 -0700
commit762295e0ae9d67450403596412bb8489893378d9 (patch)
treede5a6344505601fcb3dabbadc708018f0ed3fef2 /actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/header.rb
parentb43309328a4a4c1f6fda6db40a4a86a3c8d643fc (diff)
downloadrails-762295e0ae9d67450403596412bb8489893378d9.tar.gz
rails-762295e0ae9d67450403596412bb8489893378d9.tar.bz2
rails-762295e0ae9d67450403596412bb8489893378d9.zip
Bundle tmail 1.2.3
Diffstat (limited to 'actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/header.rb')
-rw-r--r--actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/header.rb960
1 files changed, 960 insertions, 0 deletions
diff --git a/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/header.rb b/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/header.rb
new file mode 100644
index 0000000000..9153dcd7c6
--- /dev/null
+++ b/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/header.rb
@@ -0,0 +1,960 @@
+#--
+# 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.
+#++
+
+require 'tmail/encode'
+require 'tmail/address'
+require 'tmail/parser'
+require 'tmail/config'
+require 'tmail/utils'
+
+#:startdoc:
+module TMail
+
+ # Provides methods to handle and manipulate headers in the email
+ class HeaderField
+
+ include TextUtils
+
+ class << self
+
+ alias newobj new
+
+ def new( name, body, conf = DEFAULT_CONFIG )
+ klass = FNAME_TO_CLASS[name.downcase] || UnstructuredHeader
+ klass.newobj body, conf
+ end
+
+ # Returns a HeaderField object matching the header you specify in the "name" param.
+ # Requires an initialized TMail::Port to be passed in.
+ #
+ # The method searches the header of the Port you pass into it to find a match on
+ # the header line you pass. Once a match is found, it will unwrap the matching line
+ # as needed to return an initialized HeaderField object.
+ #
+ # If you want to get the Envelope sender of the email object, pass in "EnvelopeSender",
+ # if you want the From address of the email itself, pass in 'From'.
+ #
+ # This is because a mailbox doesn't have the : after the From that designates the
+ # beginning of the envelope sender (which can be different to the from address of
+ # the emial)
+ #
+ # Other fields can be passed as normal, "Reply-To", "Received" etc.
+ #
+ # Note: Change of behaviour in 1.2.1 => returns nil if it does not find the specified
+ # header field, otherwise returns an instantiated object of the correct header class
+ #
+ # For example:
+ # port = TMail::FilePort.new("/test/fixtures/raw_email_simple")
+ # h = TMail::HeaderField.new_from_port(port, "From")
+ # h.addrs.to_s #=> "Mikel Lindsaar <mikel@nowhere.com>"
+ # h = TMail::HeaderField.new_from_port(port, "EvelopeSender")
+ # h.addrs.to_s #=> "mike@anotherplace.com.au"
+ # h = TMail::HeaderField.new_from_port(port, "SomeWeirdHeaderField")
+ # h #=> nil
+ def new_from_port( port, name, conf = DEFAULT_CONFIG )
+ if name == "EnvelopeSender"
+ name = "From"
+ re = Regexp.new('\A(From) ', 'i')
+ else
+ re = Regexp.new('\A(' + Regexp.quote(name) + '):', 'i')
+ end
+ str = nil
+ port.ropen {|f|
+ f.each do |line|
+ if m = re.match(line) then str = m.post_match.strip
+ elsif str and /\A[\t ]/ === line then str << ' ' << line.strip
+ elsif /\A-*\s*\z/ === line then break
+ elsif str then break
+ end
+ end
+ }
+ new(name, str, Config.to_config(conf)) if str
+ end
+
+ def internal_new( name, conf )
+ FNAME_TO_CLASS[name].newobj('', conf, true)
+ end
+
+ end # class << self
+
+ def initialize( body, conf, intern = false )
+ @body = body
+ @config = conf
+
+ @illegal = false
+ @parsed = false
+
+ if intern
+ @parsed = true
+ parse_init
+ end
+ end
+
+ def inspect
+ "#<#{self.class} #{@body.inspect}>"
+ end
+
+ def illegal?
+ @illegal
+ end
+
+ def empty?
+ ensure_parsed
+ return true if @illegal
+ isempty?
+ end
+
+ private
+
+ def ensure_parsed
+ return if @parsed
+ @parsed = true
+ parse
+ end
+
+ # defabstract parse
+ # end
+
+ def clear_parse_status
+ @parsed = false
+ @illegal = false
+ end
+
+ public
+
+ def body
+ ensure_parsed
+ v = Decoder.new(s = '')
+ do_accept v
+ v.terminate
+ s
+ end
+
+ def body=( str )
+ @body = str
+ clear_parse_status
+ end
+
+ include StrategyInterface
+
+ def accept( strategy )
+ ensure_parsed
+ do_accept strategy
+ strategy.terminate
+ end
+
+ # abstract do_accept
+
+ end
+
+
+ class UnstructuredHeader < HeaderField
+
+ def body
+ ensure_parsed
+ @body
+ end
+
+ def body=( arg )
+ ensure_parsed
+ @body = arg
+ end
+
+ private
+
+ def parse_init
+ end
+
+ def parse
+ @body = Decoder.decode(@body.gsub(/\n|\r\n|\r/, ''))
+ end
+
+ def isempty?
+ not @body
+ end
+
+ def do_accept( strategy )
+ strategy.text @body
+ end
+
+ end
+
+
+ class StructuredHeader < HeaderField
+
+ def comments
+ ensure_parsed
+ if @comments[0]
+ [Decoder.decode(@comments[0])]
+ else
+ @comments
+ end
+ end
+
+ private
+
+ def parse
+ save = nil
+
+ begin
+ parse_init
+ do_parse
+ rescue SyntaxError
+ if not save and mime_encoded? @body
+ save = @body
+ @body = Decoder.decode(save)
+ retry
+ elsif save
+ @body = save
+ end
+
+ @illegal = true
+ raise if @config.strict_parse?
+ end
+ end
+
+ def parse_init
+ @comments = []
+ init
+ end
+
+ def do_parse
+ quote_boundary
+ obj = Parser.parse(self.class::PARSE_TYPE, @body, @comments)
+ set obj if obj
+ end
+
+ end
+
+
+ class DateTimeHeader < StructuredHeader
+
+ PARSE_TYPE = :DATETIME
+
+ def date
+ ensure_parsed
+ @date
+ end
+
+ def date=( arg )
+ ensure_parsed
+ @date = arg
+ end
+
+ private
+
+ def init
+ @date = nil
+ end
+
+ def set( t )
+ @date = t
+ end
+
+ def isempty?
+ not @date
+ end
+
+ def do_accept( strategy )
+ strategy.meta time2str(@date)
+ end
+
+ end
+
+
+ class AddressHeader < StructuredHeader
+
+ PARSE_TYPE = :MADDRESS
+
+ def addrs
+ ensure_parsed
+ @addrs
+ end
+
+ private
+
+ def init
+ @addrs = []
+ end
+
+ def set( a )
+ @addrs = a
+ end
+
+ def isempty?
+ @addrs.empty?
+ end
+
+ def do_accept( strategy )
+ first = true
+ @addrs.each do |a|
+ if first
+ first = false
+ else
+ strategy.meta ','
+ strategy.space
+ end
+ a.accept strategy
+ end
+
+ @comments.each do |c|
+ strategy.space
+ strategy.meta '('
+ strategy.text c
+ strategy.meta ')'
+ end
+ end
+
+ end
+
+
+ class ReturnPathHeader < AddressHeader
+
+ PARSE_TYPE = :RETPATH
+
+ def addr
+ addrs()[0]
+ end
+
+ def spec
+ a = addr() or return nil
+ a.spec
+ end
+
+ def routes
+ a = addr() or return nil
+ a.routes
+ end
+
+ private
+
+ def do_accept( strategy )
+ a = addr()
+
+ strategy.meta '<'
+ unless a.routes.empty?
+ strategy.meta a.routes.map {|i| '@' + i }.join(',')
+ strategy.meta ':'
+ end
+ spec = a.spec
+ strategy.meta spec if spec
+ strategy.meta '>'
+ end
+
+ end
+
+
+ class SingleAddressHeader < AddressHeader
+
+ def addr
+ addrs()[0]
+ end
+
+ private
+
+ def do_accept( strategy )
+ a = addr()
+ a.accept strategy
+ @comments.each do |c|
+ strategy.space
+ strategy.meta '('
+ strategy.text c
+ strategy.meta ')'
+ end
+ end
+
+ end
+
+
+ class MessageIdHeader < StructuredHeader
+
+ def id
+ ensure_parsed
+ @id
+ end
+
+ def id=( arg )
+ ensure_parsed
+ @id = arg
+ end
+
+ private
+
+ def init
+ @id = nil
+ end
+
+ def isempty?
+ not @id
+ end
+
+ def do_parse
+ @id = @body.slice(MESSAGE_ID) or
+ raise SyntaxError, "wrong Message-ID format: #{@body}"
+ end
+
+ def do_accept( strategy )
+ strategy.meta @id
+ end
+
+ end
+
+
+ class ReferencesHeader < StructuredHeader
+
+ def refs
+ ensure_parsed
+ @refs
+ end
+
+ def each_id
+ self.refs.each do |i|
+ yield i if MESSAGE_ID === i
+ end
+ end
+
+ def ids
+ ensure_parsed
+ @ids
+ end
+
+ def each_phrase
+ self.refs.each do |i|
+ yield i unless MESSAGE_ID === i
+ end
+ end
+
+ def phrases
+ ret = []
+ each_phrase {|i| ret.push i }
+ ret
+ end
+
+ private
+
+ def init
+ @refs = []
+ @ids = []
+ end
+
+ def isempty?
+ @ids.empty?
+ end
+
+ def do_parse
+ str = @body
+ while m = MESSAGE_ID.match(str)
+ pre = m.pre_match.strip
+ @refs.push pre unless pre.empty?
+ @refs.push s = m[0]
+ @ids.push s
+ str = m.post_match
+ end
+ str = str.strip
+ @refs.push str unless str.empty?
+ end
+
+ def do_accept( strategy )
+ first = true
+ @ids.each do |i|
+ if first
+ first = false
+ else
+ strategy.space
+ end
+ strategy.meta i
+ end
+ end
+
+ end
+
+
+ class ReceivedHeader < StructuredHeader
+
+ PARSE_TYPE = :RECEIVED
+
+ def from
+ ensure_parsed
+ @from
+ end
+
+ def from=( arg )
+ ensure_parsed
+ @from = arg
+ end
+
+ def by
+ ensure_parsed
+ @by
+ end
+
+ def by=( arg )
+ ensure_parsed
+ @by = arg
+ end
+
+ def via
+ ensure_parsed
+ @via
+ end
+
+ def via=( arg )
+ ensure_parsed
+ @via = arg
+ end
+
+ def with
+ ensure_parsed
+ @with
+ end
+
+ def id
+ ensure_parsed
+ @id
+ end
+
+ def id=( arg )
+ ensure_parsed
+ @id = arg
+ end
+
+ def _for
+ ensure_parsed
+ @_for
+ end
+
+ def _for=( arg )
+ ensure_parsed
+ @_for = arg
+ end
+
+ def date
+ ensure_parsed
+ @date
+ end
+
+ def date=( arg )
+ ensure_parsed
+ @date = arg
+ end
+
+ private
+
+ def init
+ @from = @by = @via = @with = @id = @_for = nil
+ @with = []
+ @date = nil
+ end
+
+ def set( args )
+ @from, @by, @via, @with, @id, @_for, @date = *args
+ end
+
+ def isempty?
+ @with.empty? and not (@from or @by or @via or @id or @_for or @date)
+ end
+
+ def do_accept( strategy )
+ list = []
+ list.push 'from ' + @from if @from
+ list.push 'by ' + @by if @by
+ list.push 'via ' + @via if @via
+ @with.each do |i|
+ list.push 'with ' + i
+ end
+ list.push 'id ' + @id if @id
+ list.push 'for <' + @_for + '>' if @_for
+
+ first = true
+ list.each do |i|
+ strategy.space unless first
+ strategy.meta i
+ first = false
+ end
+ if @date
+ strategy.meta ';'
+ strategy.space
+ strategy.meta time2str(@date)
+ end
+ end
+
+ end
+
+
+ class KeywordsHeader < StructuredHeader
+
+ PARSE_TYPE = :KEYWORDS
+
+ def keys
+ ensure_parsed
+ @keys
+ end
+
+ private
+
+ def init
+ @keys = []
+ end
+
+ def set( a )
+ @keys = a
+ end
+
+ def isempty?
+ @keys.empty?
+ end
+
+ def do_accept( strategy )
+ first = true
+ @keys.each do |i|
+ if first
+ first = false
+ else
+ strategy.meta ','
+ end
+ strategy.meta i
+ end
+ end
+
+ end
+
+
+ class EncryptedHeader < StructuredHeader
+
+ PARSE_TYPE = :ENCRYPTED
+
+ def encrypter
+ ensure_parsed
+ @encrypter
+ end
+
+ def encrypter=( arg )
+ ensure_parsed
+ @encrypter = arg
+ end
+
+ def keyword
+ ensure_parsed
+ @keyword
+ end
+
+ def keyword=( arg )
+ ensure_parsed
+ @keyword = arg
+ end
+
+ private
+
+ def init
+ @encrypter = nil
+ @keyword = nil
+ end
+
+ def set( args )
+ @encrypter, @keyword = args
+ end
+
+ def isempty?
+ not (@encrypter or @keyword)
+ end
+
+ def do_accept( strategy )
+ if @key
+ strategy.meta @encrypter + ','
+ strategy.space
+ strategy.meta @keyword
+ else
+ strategy.meta @encrypter
+ end
+ end
+
+ end
+
+
+ class MimeVersionHeader < StructuredHeader
+
+ PARSE_TYPE = :MIMEVERSION
+
+ def major
+ ensure_parsed
+ @major
+ end
+
+ def major=( arg )
+ ensure_parsed
+ @major = arg
+ end
+
+ def minor
+ ensure_parsed
+ @minor
+ end
+
+ def minor=( arg )
+ ensure_parsed
+ @minor = arg
+ end
+
+ def version
+ sprintf('%d.%d', major, minor)
+ end
+
+ private
+
+ def init
+ @major = nil
+ @minor = nil
+ end
+
+ def set( args )
+ @major, @minor = *args
+ end
+
+ def isempty?
+ not (@major or @minor)
+ end
+
+ def do_accept( strategy )
+ strategy.meta sprintf('%d.%d', @major, @minor)
+ end
+
+ end
+
+
+ class ContentTypeHeader < StructuredHeader
+
+ PARSE_TYPE = :CTYPE
+
+ def main_type
+ ensure_parsed
+ @main
+ end
+
+ def main_type=( arg )
+ ensure_parsed
+ @main = arg.downcase
+ end
+
+ def sub_type
+ ensure_parsed
+ @sub
+ end
+
+ def sub_type=( arg )
+ ensure_parsed
+ @sub = arg.downcase
+ end
+
+ def content_type
+ ensure_parsed
+ @sub ? sprintf('%s/%s', @main, @sub) : @main
+ end
+
+ def params
+ ensure_parsed
+ unless @params.blank?
+ @params.each do |k, v|
+ @params[k] = unquote(v)
+ end
+ end
+ @params
+ end
+
+ def []( key )
+ ensure_parsed
+ @params and unquote(@params[key])
+ end
+
+ def []=( key, val )
+ ensure_parsed
+ (@params ||= {})[key] = val
+ end
+
+ private
+
+ def init
+ @main = @sub = @params = nil
+ end
+
+ def set( args )
+ @main, @sub, @params = *args
+ end
+
+ def isempty?
+ not (@main or @sub)
+ end
+
+ def do_accept( strategy )
+ if @sub
+ strategy.meta sprintf('%s/%s', @main, @sub)
+ else
+ strategy.meta @main
+ end
+ @params.each do |k,v|
+ if v
+ strategy.meta ';'
+ strategy.space
+ strategy.kv_pair k, v
+ end
+ end
+ end
+
+ end
+
+
+ class ContentTransferEncodingHeader < StructuredHeader
+
+ PARSE_TYPE = :CENCODING
+
+ def encoding
+ ensure_parsed
+ @encoding
+ end
+
+ def encoding=( arg )
+ ensure_parsed
+ @encoding = arg
+ end
+
+ private
+
+ def init
+ @encoding = nil
+ end
+
+ def set( s )
+ @encoding = s
+ end
+
+ def isempty?
+ not @encoding
+ end
+
+ def do_accept( strategy )
+ strategy.meta @encoding.capitalize
+ end
+
+ end
+
+
+ class ContentDispositionHeader < StructuredHeader
+
+ PARSE_TYPE = :CDISPOSITION
+
+ def disposition
+ ensure_parsed
+ @disposition
+ end
+
+ def disposition=( str )
+ ensure_parsed
+ @disposition = str.downcase
+ end
+
+ def params
+ ensure_parsed
+ unless @params.blank?
+ @params.each do |k, v|
+ @params[k] = unquote(v)
+ end
+ end
+ @params
+ end
+
+ def []( key )
+ ensure_parsed
+ @params and unquote(@params[key])
+ end
+
+ def []=( key, val )
+ ensure_parsed
+ (@params ||= {})[key] = val
+ end
+
+ private
+
+ def init
+ @disposition = @params = nil
+ end
+
+ def set( args )
+ @disposition, @params = *args
+ end
+
+ def isempty?
+ not @disposition and (not @params or @params.empty?)
+ end
+
+ def do_accept( strategy )
+ strategy.meta @disposition
+ @params.each do |k,v|
+ strategy.meta ';'
+ strategy.space
+ strategy.kv_pair k, unquote(v)
+ end
+ end
+
+ end
+
+
+ class HeaderField # redefine
+
+ FNAME_TO_CLASS = {
+ 'date' => DateTimeHeader,
+ 'resent-date' => DateTimeHeader,
+ 'to' => AddressHeader,
+ 'cc' => AddressHeader,
+ 'bcc' => AddressHeader,
+ 'from' => AddressHeader,
+ 'reply-to' => AddressHeader,
+ 'resent-to' => AddressHeader,
+ 'resent-cc' => AddressHeader,
+ 'resent-bcc' => AddressHeader,
+ 'resent-from' => AddressHeader,
+ 'resent-reply-to' => AddressHeader,
+ 'sender' => SingleAddressHeader,
+ 'resent-sender' => SingleAddressHeader,
+ 'return-path' => ReturnPathHeader,
+ 'message-id' => MessageIdHeader,
+ 'resent-message-id' => MessageIdHeader,
+ 'in-reply-to' => ReferencesHeader,
+ 'received' => ReceivedHeader,
+ 'references' => ReferencesHeader,
+ 'keywords' => KeywordsHeader,
+ 'encrypted' => EncryptedHeader,
+ 'mime-version' => MimeVersionHeader,
+ 'content-type' => ContentTypeHeader,
+ 'content-transfer-encoding' => ContentTransferEncodingHeader,
+ 'content-disposition' => ContentDispositionHeader,
+ 'content-id' => MessageIdHeader,
+ 'subject' => UnstructuredHeader,
+ 'comments' => UnstructuredHeader,
+ 'content-description' => UnstructuredHeader
+ }
+
+ end
+
+end # module TMail