Class Sendmail
In: lib/sendmail.rb
Parent: Object

Methods

Constants

MAIL_FILE = Pathname.new(',mail')

Public Instance methods

[Source]

# File lib/sendmail.rb, line 110
  def check_mail_options ( options )
    raise ArgumentError, 'No recipents' if options.to.empty?
    raise ArgumentError, 'No mail server' if options.server.nil?
    raise ArgumentError, 'No mail subject' if options.header[:Subject].nil?
  end

[Source]

# File lib/sendmail.rb, line 16
  def parse_mail_options ( *args )
    options = OpenStruct.new(
      :from => (defined? EMAIL)? EMAIL : ENV['EMAIL'],
      :from_name => (defined? FULLNAME)? FULLNAME : ENV['FULLNAME'],
      :to => [],
      :server => ENV['SMTPSERVER'] || 'localhost:25',
      :header => {},
      :body => STDIN,
      :charset => 'us-ascii',
      :encoding => '7bit'
    )
    string_args, hash_args = args.partition { |x| x.is_a?(String) }
    hash_args.each do |hash|
      raise ArgumentError, "Bad argument #{hash}" unless hash.is_a?(Hash)
      if hash[:header].is_a? String
        options.header.merge! YAML.load(hash[:header]).symbolize_keys
        hash.delete :header
      end
      if hash[:subject]
        options.header[:Subject] = hash[:subject]
        hash.delete :subject
      end
      hash.each do |k,v|
        x = options.send(k)
        case x
        when Hash  then v.each { |kk, vv| x[kk.to_sym] = vv }
        when Array then x.concat v
        else options.send("#{k}=", v)
        end
      end
    end
    OptionParser.new do |opts|
      opts.separator ''
      opts.on('-b', '--body FILE', 'Choose a file for the mail body') do |aFile|
        options.body = File.open(aFile)
      end
      opts.on('-f', '--mail-from NAME <EMAIL>', 'Choose the From address') do |aString|
        options.from = aString
      end
      opts.on('-t', '--mail-to NAME', 'Choose a recipient') do |aString|
        options.to << aString
      end
      opts.on('-s', '--server NAME:PORT', 'Choose a mail server') do |aString|
        options.server, options.port = aString.split(/:/)
        options.port ||= 25
      end
      opts.on('-S', '--subject NAME', 'Choose your mail subject') do |aString|
        options.header[:Subject] = aString.sub(/\.?$/, '.')
      end
      opts.on('--[no-]sign', 'Sign the message with gpg') do |signed|
        options.signed = signed
      end
      opts.on('--passphrase FILE', 'the passphrase file') do |aFile|
        options.pass = Pathname.new(aFile)
      end
      opts.on('-a', '--[no-]confirm', 'Ask a confirmation before sending') do |confirm|
        options.confirm = confirm
      end
      opts.on('--comment STRING', 'Choose a comment for GPG') do |aComment|
        options.comment = aComment
      end
      opts.on('--header STRING', 'Add some header fields (Yaml syntax)') do |s|
        options.header.merge! YAML.load(s)
      end
      opts.on('-m', '--[no-]mime', 'Choose the MIME protocol') do |mime|
        options.mime = mime
      end
      opts.on('--charset STRING',
              'Choose a charset for the encoding') do |aCharset|
        options.charset = aCharset
      end
      opts.on('--encoding STRING',
              'Choose an encoding') do |anEncoding|
        options.encoding = anEncoding
      end
      opts.on_tail('-h', '--help', 'Show this message') do
        puts opts
        exit
      end
    end.parse!(string_args)
    options.header[:To] = (options.header[:To] || options.to).join(', ')
    from = options.header[:From] || options.from
    if from.to_s =~ /<(.*)>/
      options.header[:From] = from
      options.from = $1
    else
      options.header[:From] = "#{options.from_name}  <#{from}>"
      options.from = from
    end
    check_mail_options(options)
    options
  end

Mail.

[Source]

# File lib/sendmail.rb, line 119
  def sendmail ( *args )

    body, opts = nil, nil
    if MAIL_FILE.exist?
      STDERR.puts "Using the cache file #{MAIL_FILE}"
      MAIL_FILE.open do |mail_file|
        opts = YAML::chop_header(mail_file)
        body = mail_file.read
      end
    else
      opts = parse_mail_options(*args)
      body = (opts.body.nil?)? '' : opts.body.read
    end
    opts.header.symbolize_keys!
    server, port = opts.server.split(/:/)
    port ||= 25
    to = opts.header[:To]
    to = to.join(', ') if to.is_a? Array
    STDERR.puts "To: #{to.inspect}"
    STDERR.puts "Smtp Server: #{server}"

    #
    # confirm the user
    #
    if opts.confirm
      raise 'HighLine is unavailable' unless defined? HighLine
      question = %Q[
        |Send a mail, with this subject: #{opts.header[:Subject]}
        |  to #{to}
        |  from #{opts.header[:From]}
        |  #{(opts.signed)? 'signed by ' + opts.from : 'not signed !'}
        |Are you sure? (y/n)].head_cut!
      raise 'Aborting' unless HighLine.new.agree question, true
    end

    require 'net/smtp'
    Net::SMTP.start(server, port) do |smtp|
      smtp.open_message_stream(opts.from, opts.to) do |f|
        f.print "From: #{opts.header[:From]}\n"
        f.print "To: #{to}\n"
        f.print "Subject: #{opts.header[:Subject]}\n"
        opts.header.each do |k, v|
          next if [:From, :To, :Subject].include? k
          k = $1 if k.to_s =~ /^"(.*)"$/
          f.print "#{k}: #{v}\n"
        end
        if opts.signed
          TempPath.new do |mail_body|
            mail_body.open('w') do |out|
              if opts.mime
                out.puts "
                  |Content-Type: text/plain; charset=\"#{opts.charset}\"
                  |Content-Transfer-Encoding: #{opts.encoding}
                  |
                  |".head_cut!
              end
              out.print body
            end
            f.print sign_wrap(opts, mail_body)
          end
        else
          f.print "\n" + body
        end
      end
    end
  end

[Source]

# File lib/sendmail.rb, line 193
  def sign_wrap ( opts, body )
    id = "===============Vcs#{new_id 10}==" if opts.mime

    TempPath.new do |tmp_name|
      tmp_name.open('w') do |tmp|
        if opts.mime
          tmp.print %Q[
            |MIME-Version: 1.0
            |Content-Type: multipart/signed; micalg=pgp-sha1;
            | protocol="application/pgp-signature";
            | boundary="#{id}";
            | charset="#{opts.charset}"
            |Content-Transfer-Encoding: #{opts.encoding}
            |
            |This is an OpenPGP/MIME signed message (RFC 2440 and 3156)
            |--#{id}
            |].head_cut!

          tmp.print body.read

          tmp.print %Q[
            |
            |--#{id}
            |Content-Type: application/pgp-signature; name="signature.asc"
            |Content-Description: OpenPGP digital signature
            |Content-Disposition: attachment; filename="signature.asc"
            |
            |].head_cut!
        end
    
        TempPath.new do |tmp_out|
          pid = fork do
            cmd = %w[ gpg --status-fd 2 --sign --textmode --armor
                          --digest-algo sha1 --no-use-agent -u ]
            cmd << "<#{opts.from}>"
            cmd << ((opts.mime)? '--detach-sign' : '--clearsign')
            cmd += [ '--output', tmp_out ]
            if opts.pass
              cmd += [ '--no-tty', '--batch',
                       '--passphrase-fd', opts.pass.open.to_i.to_s ]
            end
            cmd += [ '--comment', "'#{opts.comment}'" ] if opts.comment
            STDIN.reopen(body.open)
            exec(*cmd)
          end
          Process.waitpid pid

          tmp.print tmp_out.read
          tmp.puts "\n--#{id}--" if opts.mime
        end
      end

      return tmp_name.read
    end
  end

Protected Instance methods

[Source]

# File lib/sendmail.rb, line 187
  def new_id ( n )
    ([0] * n).map!{ |x| "%X" % rand(15) }.join
  end

[Validate]