123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 |
- require 'mail/fields'
- # encoding: utf-8
- module Mail
- # Provides a single class to call to create a new structured or unstructured
- # field. Works out per RFC what field of field it is being given and returns
- # the correct field of class back on new.
- #
- # ===Per RFC 2822
- #
- # 2.2. Header Fields
- #
- # Header fields are lines composed of a field name, followed by a colon
- # (":"), followed by a field body, and terminated by CRLF. A field
- # name MUST be composed of printable US-ASCII characters (i.e.,
- # characters that have values between 33 and 126, inclusive), except
- # colon. A field body may be composed of any US-ASCII characters,
- # except for CR and LF. However, a field body may contain CRLF when
- # used in header "folding" and "unfolding" as described in section
- # 2.2.3. All field bodies MUST conform to the syntax described in
- # sections 3 and 4 of this standard.
- #
- class Field
-
- include Patterns
- include Comparable
-
- STRUCTURED_FIELDS = %w[ bcc cc content-description content-disposition
- content-id content-location content-transfer-encoding
- content-type date from in-reply-to keywords message-id
- mime-version received references reply-to
- resent-bcc resent-cc resent-date resent-from
- resent-message-id resent-sender resent-to
- return-path sender to ]
- KNOWN_FIELDS = STRUCTURED_FIELDS + ['comments', 'subject']
-
- # Generic Field Exception
- class FieldError < StandardError
- end
- # Raised when a parsing error has occurred (ie, a StructuredField has tried
- # to parse a field that is invalid or improperly written)
- class ParseError < FieldError #:nodoc:
- attr_accessor :element, :value, :reason
- def initialize(element, value, reason)
- @element = element
- @value = value
- @reason = reason
- super("#{element} can not parse |#{value}|\nReason was: #{reason}")
- end
- end
- # Raised when attempting to set a structured field's contents to an invalid syntax
- class SyntaxError < FieldError #:nodoc:
- end
-
- # Accepts a string:
- #
- # Field.new("field-name: field data")
- #
- # Or name, value pair:
- #
- # Field.new("field-name", "value")
- #
- # Or a name by itself:
- #
- # Field.new("field-name")
- #
- # Note, does not want a terminating carriage return. Returns
- # self appropriately parsed. If value is not a string, then
- # it will be passed through as is, for example, content-type
- # field can accept an array with the type and a hash of
- # parameters:
- #
- # Field.new('content-type', ['text', 'plain', {:charset => 'UTF-8'}])
- def initialize(name, value = nil, charset = 'utf-8')
- case
- when name =~ /:/ # Field.new("field-name: field data")
- charset = value unless value.blank?
- name, value = split(name)
- create_field(name, value, charset)
- when name !~ /:/ && value.blank? # Field.new("field-name")
- create_field(name, nil, charset)
- else # Field.new("field-name", "value")
- create_field(name, value, charset)
- end
- return self
- end
- def field=(value)
- @field = value
- end
-
- def field
- @field
- end
-
- def name
- field.name
- end
-
- def value
- field.value
- end
-
- def value=(val)
- create_field(name, val, charset)
- end
-
- def to_s
- field.to_s
- end
-
- def update(name, value)
- create_field(name, value, charset)
- end
-
- def same( other )
- match_to_s(other.name, field.name)
- end
-
- alias_method :==, :same
-
- def <=>( other )
- self_order = FIELD_ORDER.rindex(self.name.to_s.downcase) || 100
- other_order = FIELD_ORDER.rindex(other.name.to_s.downcase) || 100
- self_order <=> other_order
- end
-
- def method_missing(name, *args, &block)
- field.send(name, *args, &block)
- end
-
- FIELD_ORDER = %w[ return-path received
- resent-date resent-from resent-sender resent-to
- resent-cc resent-bcc resent-message-id
- date from sender reply-to to cc bcc
- message-id in-reply-to references
- subject comments keywords
- mime-version content-type content-transfer-encoding
- content-location content-disposition content-description ]
-
- private
-
- def split(raw_field)
- match_data = raw_field.mb_chars.match(/^(#{FIELD_NAME})\s*:\s*(#{FIELD_BODY})?$/)
- [match_data[1].to_s.mb_chars.strip, match_data[2].to_s.mb_chars.strip]
- rescue
- STDERR.puts "WARNING: Could not parse (and so ignorning) '#{raw_field}'"
- end
-
- def create_field(name, value, charset)
- begin
- self.field = new_field(name, value, charset)
- rescue Mail::Field::ParseError => e
- self.field = Mail::UnstructuredField.new(name, value)
- self.field.errors << [name, value, e]
- self.field
- end
- end
- def new_field(name, value, charset)
- # Could do this with constantize and make it "as DRY as", but a simple case
- # statement is, well, simpler...
- case name.to_s.downcase
- when /^to$/i
- ToField.new(value, charset)
- when /^cc$/i
- CcField.new(value, charset)
- when /^bcc$/i
- BccField.new(value, charset)
- when /^message-id$/i
- MessageIdField.new(value, charset)
- when /^in-reply-to$/i
- InReplyToField.new(value, charset)
- when /^references$/i
- ReferencesField.new(value, charset)
- when /^subject$/i
- SubjectField.new(value, charset)
- when /^comments$/i
- CommentsField.new(value, charset)
- when /^keywords$/i
- KeywordsField.new(value, charset)
- when /^date$/i
- DateField.new(value, charset)
- when /^from$/i
- FromField.new(value, charset)
- when /^sender$/i
- SenderField.new(value, charset)
- when /^reply-to$/i
- ReplyToField.new(value, charset)
- when /^resent-date$/i
- ResentDateField.new(value, charset)
- when /^resent-from$/i
- ResentFromField.new(value, charset)
- when /^resent-sender$/i
- ResentSenderField.new(value, charset)
- when /^resent-to$/i
- ResentToField.new(value, charset)
- when /^resent-cc$/i
- ResentCcField.new(value, charset)
- when /^resent-bcc$/i
- ResentBccField.new(value, charset)
- when /^resent-message-id$/i
- ResentMessageIdField.new(value, charset)
- when /^return-path$/i
- ReturnPathField.new(value, charset)
- when /^received$/i
- ReceivedField.new(value, charset)
- when /^mime-version$/i
- MimeVersionField.new(value, charset)
- when /^content-transfer-encoding$/i
- ContentTransferEncodingField.new(value, charset)
- when /^content-description$/i
- ContentDescriptionField.new(value, charset)
- when /^content-disposition$/i
- ContentDispositionField.new(value, charset)
- when /^content-type$/i
- ContentTypeField.new(value, charset)
- when /^content-id$/i
- ContentIdField.new(value, charset)
- when /^content-location$/i
- ContentLocationField.new(value, charset)
- else
- OptionalField.new(name, value, charset)
- end
-
- end
- end
-
- end
|