rmail.rb - Kommandozeilen Mailer für Windows

Winfried Mueller, www.reintechnisch.de, Start: 13.05.05, Stand 15.05.05

Ein Kommandozeilenwerkzeug zu haben, mit dem man eben mal schnell eine Mail versenden kann, ist praktisch. Unter Linux gibt es ja das Programm mail dafür. Unter Windows gibt es nichts in der Standard-Ausrüstung. Auch hat man meist keinen Mailserver unter Windows installiert, der einem die Mail per smtp abnehmen könnte.

Also sollte eine kleines Kommandozeilen-Tool entstehen, mit dem ich über den Webserver meines Mailproviders per smtp-auth eine Mail verschicken kann. Mit diesem Werkzeug kann ich auf jedem Windows Rechner eine Mail über meinen Provider verschicken, insofern Ruby installiert und eine Online-Verbindung aufgebaut ist.

 
# rmail - send a Mail from Command-Line, only for Windows platform
#
# Winfried Mueller, www.reintechnisch.de, Version: 2005/05/14
#
# State: experimental

require "net/smtp"
require "Win32API"

module Config
  FROM     = "win.mueller@web.de"
  A_LOGIN  = "win.mueller" 
  A_PASS   = nil
  SMTP_SRV = "smtp.web.de"
end

def get_passwd( char=nil, prompt=nil, char_range=0x20..0x7F )
  getch = Win32API.new("msvcrt", "_getch", [], 'L')
  prompt ||= "Password: "
  print prompt
  passwd = ""

  loop do
    c = getch.Call
    case c
      when 0x0d
        break
      when char_range
        passwd << c.chr
        if char
          print char
        end
      else
        STDERR.print "\x7" # BEL if not allowed char
    end
  end
  print "\n"

  passwd
end



def t_input( prompt=nil )
  if prompt
    print prompt
  end
  gets.chomp
end

def tm_input( prompt=nil )
  if prompt
    puts prompt
  end
  msg = ""
  loop do
    s = gets.chomp
    if s =~ /^\.$/
      break
    end
    msg << s + "\n"
  end
  msg
end


class Mail
  def initialize( from, to, subject, body )
    raise unless from.is_a?(String)
    raise unless to.is_a?(String) || to.is_a?(Array)
    raise unless body.is_a?(String)
    raise unless subject.is_a?(String)
    @from = from
    @to = []
    if to.is_a?( String )
      @to << to
    elsif to.is_a?( Array )
      @to = to.clone
    end
    @subject = subject
    @body = body
  end
  attr_reader :from, :to, :subject, :body
end

class Mailer
  DBG = false
  def initialize( smtp_srv, port=25, helo='localhost.localdomain' )
    @server = smtp_srv
    @port = port
    @helo = helo
    @auth_account = nil
    @auth_pass = nil
    @auth_type = nil
  end

  def set_auth( account, pass, type=:plain )
    raise unless (type && account && pass)
    raise unless [:plain, :login, :cram_md5].include?(type)
    @auth_type = type
    @auth_pass = pass
    @auth_account = account
  end

  def send( mail )
    mailmsg  = "From: #{mail.from}\n"
    mailmsg += "To: #{mail.to.join(",\n        ")}\n"
    mailmsg +=  "Subject: #{mail.subject}\n\n"
    mailmsg += mail.body.chomp 
    mailmsg + "\n"

    if DBG
      print mailmsg
    else
      Net::SMTP.start(@server, @port, @helo, 
                      @auth_account, @auth_pass, @auth_type) do |smtp|
        smtp.sendmail( mailmsg, mail.from, mail.to )      
      end
    end
  end

end

# App ---------------------------------------------------------------------
from    = Config::FROM    || t_input( "From: " )
srv     = Config::SMTP_SRV || t_input( "SMTP-Server: " )
a_login = Config::A_LOGIN || t_input( "Auth_Login: " )
a_pass  = Config::A_PASS  || get_passwd( "*", "Auth_Pass: " )
to      = t_input( "To: " )
subject = t_input( "Subject: " )

msg = tm_input( "Message (end with .):" )

puts "Sending Mail, wait..."
mail = Mail.new( from, to, subject, msg )
mailer = Mailer.new( srv )

mailer.set_auth( a_login, a_pass )

mailer.send( mail )

Nach Aufruf von rmail gibt man alles ein, was abgefragt wird. Dabei kann man zu Anfang im Modul Config schon einiges vorkonfigurieren, um es nicht jedesmal wieder eingeben zu müssen. Auth_Login ist bei Web.de z.B. die Mailadresse vor dem @-Zeichen, bei user@web.de wäre das also user. Das Auth_Passwort ist das selbe, wie das pop3-Passwort. Bei gmx wäre der Auth_Login die Benutzernummer, Passwort ebenso das von pop3.

Die Message kann mehrere Zeilen beinhalten. Sobald man fertig ist, muss man wie unter Linux, einen Punkt auf die letzte Zeile setzen. Eine Zeile, die nur einen Punkt enthält, ist also das Ende-Kriterium. Jetzt wird die Mail über den Provider versandt.

Das Programm ist objektorientiert aufgebaut, die Klassen Mail und Mailer können also auch in eigenen Projekten verwendet werden, um z.B. automatisch generierte Mails zu versenden. Auch get_passwd und t_input sind wiederverwendbare Komponenten. Im Grunde besteht das Anwendungsprogramm nur aus den letzten Zeilen nach # App ---. Damit das Programm jedoch ohne Abhängigkeiten zu Bibliotheken funktioniert, wurde hier alles in ein Programmfile zusammenkopiert.