diff options
Diffstat (limited to 'fake-service/lib/smail/mail.rb')
-rw-r--r-- | fake-service/lib/smail/mail.rb | 299 |
1 files changed, 299 insertions, 0 deletions
diff --git a/fake-service/lib/smail/mail.rb b/fake-service/lib/smail/mail.rb new file mode 100644 index 00000000..8d1c8806 --- /dev/null +++ b/fake-service/lib/smail/mail.rb @@ -0,0 +1,299 @@ +module Smail + class Mail + attr_reader :to, :cc, :bcc, :from, :subject, :body, :headers, :status, :draft_reply_for + attr_accessor :ident, :security_casing + + def initialize(data = {}) + @ident = data[:ident] + @to = data[:to] + @cc = data[:cc] + @bcc = data[:bcc] + @from = data[:from] + @subject = data[:subject] + @body = data[:body] + @headers = data[:headers] || {} + @status = data[:status] || [] + @draft_reply_for = data[:draft_reply_for] || [] + self.tags = data[:tags] || Tags.new + end + + def hash + @ident.hash + end + + def eql?(object) + self == object + end + + def tags=(t) + @tags.mail = nil if @tags + @tags = t + @tags.mail = self if @tags + t + end + + def ==(object) + @ident == object.ident + end + + def tag_names + @tags.names + end + + def is_tagged?(t) + @tags.is_tagged?(t) + end + + def tag_names=(vs) + to_remove = self.tag_names - vs + to_add = vs - self.tag_names + + to_remove.each do |tn| + self.remove_tag(tn) + end + + to_add.each do |v| + self.add_tag(v) + end + end + + def add_tag(nm) + @tags.add_tag(nm) + end + + def remove_tag(nm) + @tags.remove(nm) + end + + def has_trash_tag? + tag_names.include? "trash" + end + + def starred=(v); v ? add_status(:starred) : remove_status(:starred); end + def starred?; status?(:starred); end + def read=(v); v ? add_status(:read) : remove_status(:read); end + def read?; status?(:read); end + def replied=(v); v ? add_status(:replied) : remove_status(:replied); end + def replied?; status?(:replied); end + + def add_status(n) + unless self.status?(n) + @status = @status + [n] + @tags.added_status(n) + Smail.mail_service.stats_status_added(n, self) + end + end + + def remove_status(n) + if self.status?(n) + @status = @status - [n] + @tags.removed_status(n) + Smail.mail_service.stats_status_removed(n, self) + end + end + + def status?(n) + @status.include?(n) + end + + def to_json(*args) + { + header: { + to: Array(@to), + from: @from, + subject: @subject, + }.merge(@headers).merge({date: @headers[:date].iso8601}), + ident: ident, + tags: @tags.names, + status: @status, + security_casing: @security_casing, + draft_reply_for: @draft_reply_for, + body: @body + }.to_json(*args) + end + + + def self.from_json(obj, new_ident = nil) + ident = obj['ident'] + draft_reply_for = obj['draft_reply_for'] + hdrs = obj['header'] + to = hdrs['to'] + cc = hdrs['cc'] + bcc = hdrs['bcc'] + from = hdrs['from'] + subject = hdrs['subject'] + new_hdrs = {} + hdrs.each do |k,v| + new_hdrs[k.to_sym] = v unless %w(from to subject).include?(k) + end + tag_names = obj['tags'] + st = obj['status'] + bd = obj['body'] + + mail = new(:subject => subject, + :from => from, + :to => Array(to), + :cc => Array(cc), + :bcc => Array(bcc), + :headers => new_hdrs, + :status => st, + :draft_reply_for => draft_reply_for, + :ident => (ident.to_s.empty? ? new_ident : ident), + :body => bd) + + tag_names.each do |tag_name| + mail.add_tag tag_name + end + + mail + + end + + def to_s + ([ + ("#{INTERNAL_TO_EXTERNAL_HEADER[:to]}: #{format_header_value_out(:to, @to)}" if @to), + ("#{INTERNAL_TO_EXTERNAL_HEADER[:from]}: #{format_header_value_out(:from, @from)}" if @from), + ("#{INTERNAL_TO_EXTERNAL_HEADER[:subject]}: #{format_header_value_out(:subject, @subject)}" if @subject), + ("#{INTERNAL_TO_EXTERNAL_HEADER[:x_tw_smail_tags]}: #{format_header_value_out(:x_tw_smail_tags, @tags.names)}" if !@tags.names.empty?), + ("#{INTERNAL_TO_EXTERNAL_HEADER[:x_tw_smail_status]}: #{format_header_value_out(:x_tw_smail_status, @status)}" if !@status.empty?), + ("#{INTERNAL_TO_EXTERNAL_HEADER[:x_tw_smail_ident]}: #{format_header_value_out(:x_tw_smail_ident, @ident)}"), + ].compact + @headers.map { |k,v| "#{INTERNAL_TO_EXTERNAL_HEADER[k]}: #{format_header_value_out(k, v)}"}).sort.join("\n") + "\n\n#{@body}" + end + + SPECIAL_HEADERS = [:subject, :from, :to, :x_tw_smail_tags, :x_tw_smail_status, :x_tw_smail_ident] + INTERNAL_TO_EXTERNAL_HEADER = { + :subject => "Subject", + :date => "Date", + :from => "From", + :to => "To", + :cc => "CC", + :bcc => "BCC", + :message_id => "Message-ID", + :mime_version => "Mime-Version", + :content_type => "Content-Type", + :content_transfer_encoding => "Content-Transfer-Encoding", + :x_tw_smail_tags => "X-TW-SMail-Tags", + :x_tw_smail_status => "X-TW-SMail-Status", + :x_tw_smail_ident => "X-TW-SMail-Ident", + } + + def format_header_value_out(k,v) + case k + when :date + v.strftime("%a, %d %b %Y %H:%M:%S %z") + when :to, :cc, :bcc + Array(v).join(", ") + when :x_tw_smail_tags, :x_tw_smail_status + v.join(", ") + else + v + end + end + + class << self + def formatted_header(k, ls) + format_header_value(k, ls[k] && ls[k][1]) + end + + def has_header(hdr_name, ls, val, otherwise) + if ls[hdr_name] + val + else + otherwise + end + end + + def time_rand from = (Time.now - 300000000), to = Time.now + Time.at(from + rand * (to.to_f - from.to_f)) + end + + # io is String or IO + def read(io, ident = nil) + io = StringIO.new(io) if io.is_a? String + headers = {} + body = "" + reading_headers = true + previous_header = nil + first = true + io.each do |ln| + if first && ln =~ /^From / + # Ignore line delimiter things + else + if reading_headers + if ln.chomp == "" + reading_headers = false + else + if previous_header && ln =~ /^\s+/ + previous_header[1] << " #{ln.strip}" + else + key, value = ln.chomp.split(/: /, 2) + previous_header = [key, value] + headers[internal_header_name(key)] = previous_header + end + end + else + body << ln + end + end + if first + first = false + end + end + + header_data = {} + headers.each do |k, (_, v)| + unless special_header?(k) + header_data[k] = format_header_value(k, v) + end + end + + unless header_data[:date] + header_data[:date] = time_rand + end + + + new(:subject => formatted_header(:subject, headers), + :from => formatted_header(:from, headers), + :to => formatted_header(:to, headers), + :headers => header_data, + :tags => formatted_header(:x_tw_smail_tags, headers), + :status => formatted_header(:x_tw_smail_status, headers), + :ident => has_header(:x_tw_smail_ident, headers, formatted_header(:x_tw_smail_ident, headers), ident), + :body => body + ) + end + + + private + def internal_header_name(k) + k.downcase.gsub(/-/, '_').to_sym + end + + def special_header?(k) + SPECIAL_HEADERS.include?(k) + end + + def format_header_value(k, v) + case k + when :date + DateTime.parse(v) + when :to, :cc, :bcc + vs = (v || "").split(/, /) + if vs.length == 1 + vs[0] + else + vs + end + when :x_tw_smail_tags + Tags.new *(v || "").split(/, /) + when :x_tw_smail_status + (v || "").split(/, /).map { |ss| ss.to_sym } + when :x_tw_smail_ident + v.to_i + else + v + end + end + end + end +end |