class Hermes::Mail

Constants

LEVEL
MAILDIR
SENDMAIL
SPOOLDIR
SYSDIR

Attributes

default_format[RW]
logfile[RW]
loglevel[RW]
maildir[RW]
sendmail[RW]
spooldir[RW]
spoolfile[RW]
sysdir[RW]

Public Class Methods

new(from, headers, body) click to toggle source
Calls superclass method
# File lib/hermes/mail.rb, line 60
def initialize from, headers, body
  super headers, body
  @from = from
end

Public Instance Methods

box(path = nil, default_format = nil) click to toggle source
# File lib/hermes/transports.rb, line 33
def box path = nil, default_format = nil
  @cache ||= {}
  @cache[ path] ||= find_box path, default_format
end
create() click to toggle source
# File lib/hermes/mail.rb, line 54
def create
  new nil, nil, nil
end
expand_maildir() click to toggle source
# File lib/hermes/transports.rb, line 80
def expand_maildir
  File.expand_path @maildir||MAILDIR, "~"
end
expand_sysdir() click to toggle source
# File lib/hermes/transports.rb, line 84
def expand_sysdir
  File.expand_path @sysdir||SYSDIR, expand_maildir
end
log(type, *message) click to toggle source
# File lib/hermes/transports.rb, line 67
def log type, *message
  @logfile or return
  return if LEVEL[ type] > LEVEL[ @loglevel].to_i
  l = File.expand_path @logfile, expand_sysdir
  LockedFile.open l, "a" do |log|
    log.puts "[#{Time.new}] [#$$] [#{type}] #{message.join ' '}"
  end
  nil
rescue Errno::ENOENT
  d = File.dirname l
  Dir.mkdir! d and retry
end
parse(input) click to toggle source
# File lib/hermes/mail.rb, line 46
def parse input
  FromReader.open input do |fr|
    parse_hb fr do |h,b|
      new fr.from, h, b
    end
  end
end
pipe( cmd, *args) → status click to toggle source

Pipe into an external program. If a block is given, the programs output will be yielded there.

# File lib/hermes/transports.rb, line 116
def pipe cmd, *args
  log :INF, "Piping through:", cmd, *args
  ri, wi = IO.pipe
  ro, wo = IO.pipe
  child = fork do
    wi.close ; ro.close
    $stdout.reopen wo ; wo.close
    $stdin .reopen ri ; ri.close
    exec cmd, *args
  end
  ri.close ; wo.close
  t = Thread.new wi do |wi|
    begin
      wi.write to_s
    ensure
      wi.close
    end
  end
  begin
    r = ro.read
    yield r if block_given?
  ensure
    ro.close
  end
  t.join
  Process.wait child
  $?.success? or
    log :ERR, "Pipe failed with error code %d." % $?.exitstatus
  $?
end
receivers() click to toggle source
# File lib/hermes/mail.rb, line 75
def receivers
  addresses_of :to, :cc, :bcc
end
save( path, default_format = nil) → mb click to toggle source

Save into local mailbox.

# File lib/hermes/transports.rb, line 104
def save mailbox = nil, default_format = nil
  b = cls.box mailbox, default_format
  log :INF, "Delivering to", b.path
  b.deliver self
end
send!( smtp, *tos) → response click to toggle source

Send by SMTP.

Be aware that #send without bang is a standard Ruby method.

# File lib/hermes/transports.rb, line 176
def send! conn = nil, *tos
  if tos.empty? then
    tos = receivers.map { |t| t.plain }
  else
    tos.flatten!
  end
  f, m = true, ""
  to_s.each_line { |l|
    if f then
      f = false
      next if l =~ /^From /
    end
    m << l
  }
  open_smtp conn do |smtp|
    log :INF, "Sending to", *tos
    frs = headers.from.map { |f| f.plain }
    smtp.send_message m, frs.first, tos
  end
rescue NoMethodError
  raise "Missing field: #{$!.name}."
end
sendmail() click to toggle source
# File lib/hermes/transports.rb, line 63
def sendmail
  @sendmail||SENDMAIL
end
to_s() click to toggle source

String representation with “From ” line. Mails reside in mbox files etc. and so have to end in a newline.

Calls superclass method
# File lib/hermes/mail.rb, line 67
def to_s
  set_unix_from
  r = ""
  r << @from << $/ << super
  r.ends_with? $/ or r << $/
  r
end

Private Instance Methods

addresses_of(*args) click to toggle source
# File lib/hermes/mail.rb, line 81
def addresses_of *args
  l = args.map { |f| @headers.field f }
  AddrList.new *l
end
find_box(path, default_format) click to toggle source
# File lib/hermes/transports.rb, line 40
def find_box path, default_format
  b = case path
    when Box then
      path
    when nil then
      @spoolfile ||= getuser
      @spooldir  ||= SPOOLDIR
      m = File.expand_path @spoolfile, @spooldir
      MBox.new m
    else
      m = if path =~ /\A=/ then
        File.join expand_maildir, $'
      else
        File.expand_path path, "~"
      end
      Box.find m, default_format||@default_format
  end
  b.exists? or b.create
  b
end
getuser() click to toggle source
# File lib/hermes/transports.rb, line 90
def getuser
  e = Etc.getpwuid Process.uid
  e.name
rescue NameError
  require "etc" and retry
end
net_smpt() click to toggle source
# File lib/hermes/transports.rb, line 201
def net_smpt
  Net::SMTP
rescue NameError
  require "net/smtp" and retry
end
open_smtp(arg) { |arg| ... } click to toggle source
# File lib/hermes/transports.rb, line 207
def open_smtp arg, &block
  case arg
    when String then h, p = arg.split ":"
    when Array  then h, p = *arg
    when nil    then h, p = "localhost", nil
    else
      if arg.respond_to? :send_message then
        yield arg
        return
      else
        h, p = arg.host, arg.port
      end
  end
  net_smpt.start h, p, &block
end
set_unix_from() click to toggle source
# File lib/hermes/mail.rb, line 86
def set_unix_from
  return if @from
  # Common MTA's will issue a proper "From" line; some MDA's
  # won't.  Then, build it using the "From:" header.
  addr = nil
  l = addresses_of :from, :return_path
  # Prefer the non-local version if present.
  l.each { |a|
    if not addr or addr !~ /@/ then
      addr = a
    end
  }
  addr or raise ArgumentError, "No From: field present."
  @from = "From #{addr.plain} #{Time.now.gmtime.asctime}"
end