Отправка электронной почты средствами RMail и Net::SMTP

Процесс отправки электронной почты из скрипта можно разложить на 2 составляющие:

  1. Данный модуль производит автоматическое quoted-printable-кодирование в таблице символов ISO-2022-JP для заголовков Content-Type и Content-Disposition, что, например, препятствует правильной передаче имен вложений.
  2. Невозможно полностью переназначить значение заголовка Message-Id.

Далее читателю предлагается модуль SendMail, который по средствам метода send_mail позволяет отправлять почту с вложениями и с расширенными возможностями.

require 'rubygems'
require 'rmail'
require 'net/smtp'
require 'md5'

module SendMail
  # Default settings. You may change all of them on each call of send_mail method.
  @defaults = {
    :smtp => {
      :address => 'smtp.example.com',
      :port => 25,
      :domain => 'example.com', # This field sends on HELO request
      :user_name => 'user_name',
      :password => 'password',
      :authentication => :plain # Supported 3 types: :plain, :login, :cram_md5
    },   
    :from => {
      :name => 'Exampler',
      :email => 'example@example.com'
    },   
    :options => {
      :charset => 'utf-8',
      :priority => 3, # The priority header used in many mail programs. You can see priority hash below
      :quoted_printable => true, # Using quoted-printable transfer encoding in E-Mail. If this field missed
                                 # using default transfer encoding (7bit) - not all servers support it.
      :attachments_transfer_encoding => :base64 # Supported 2 types: :base64, :quoted_printable.
    }
  }
 
  @priorities = {
    2 => 'High',
    3 => 'Normal',
    4 => 'Low'
  }
 
  def self.quoted_printable(text)
    if @options[:quoted_printable]
      "=?#{@options[:charset]}?Q?#{[text].pack('M*').chop}?="
    else
      text
    end
  end
 
  def self.send_mail(params)
    params[:smtp] ||= {}
    params[:to] = [params[:to]] if params[:to].class != Array
    params[:from] ||= {}
    params[:options] ||= {}
    params[:attachments] ||= []
    params[:attachments] = [params[:attachments]] if params[:attachments].class != Array
    @options = {}
   
   
    email = RMail::Message.new
   
    @options[:quoted_printable] = params[:options][:quoted_printable].nil? ? @defaults[:options][:quoted_printable] : params[:options][:quoted_printable]
    @options[:charset] = params[:options][:charset].nil? ? @defaults[:options][:charset] : params[:options][:charset]
   
   
    to_list = RMail::Address::List.new
    for to in params[:to]
      to = {:email => to} if to.class != Hash
     
      address_string = to[:name].nil? ? to[:email] : "#{self.quoted_printable(to[:name])} <#{to[:email]}>"
      to_list << RMail::Address.new(address_string)
    end
    email.header.to = to_list

    from_name = params[:from][:name].nil? ? @defaults[:from][:name] : params[:from][:name]
    from_name = self.quoted_printable(from_name)
    from_email = params[:from][:email].nil? ? @defaults[:from][:email] : params[:from][:email]
    email.header.from = from_name.nil? ? from_email : "#{from_name} <#{from_email}>"
   
    if !params[:reply_to].nil?
      params[:reply_to] = {:email => params[:reply_to]} if params[:reply_to].class != Hash
     
      email.header.reply_to  = params[:reply_to][:name].nil? ? params[:reply_to][:email] : "#{self.quoted_printable(params[:reply_to][:name])} <#{params[:reply_to][:email]}>"
    end
   
    email.header.subject = self.quoted_printable(params[:subject])
   
    priority = params[:options][:priority].nil? ? @defaults[:options][:priority] : params[:options][:priority]
    email.header['X-Priority'] = "#{priority} (#{@priorities[priority]})"
   
    main = RMail::Message.new
    main.header['Content-Transfer-Encoding'] = @options[:quoted_printable] ? 'quoted-printable' : '7bit'
    main.header.add 'Content-Type', 'text/plain', nil, 'charset' => @options[:charset]
    main.body = params[:body]
   
    email.add_part(main)
   
   
    for attachment in params[:attachments]
      part = RMail::Message.new
     
      transfer_encoding = attachment[:transfer_encoding].nil? ? params[:options][:attachments_transfer_encoding] : attachment[:transfer_encoding]
      transfer_encoding = @defaults[:options][:attachments_transfer_encoding] if transfer_encoding.nil?
     
      case transfer_encoding
        when :base64
          part.header['Content-Transfer-Encoding'] = 'base64'
          part.body = [attachment[:content]].pack('m')
        when :quoted_printable
          part.header['Content-Transfer-Encoding'] = 'quoted-printable'
          part.body = [attachment[:content]].pack('M*')
      end
     
      part.header.add 'Content-Type', attachment[:mime_type], nil, 'name' => quoted_printable(attachment[:filename])
      part.header.add 'Content-Disposition', 'attachment', nil, 'filename' => quoted_printable(attachment[:filename])
     
      email.add_part(part)
    end
   
   
    server = params[:smtp][:address].nil? ? @defaults[:smtp][:address] : params[:smtp][:address]
    port = params[:smtp][:port].nil? ? @defaults[:smtp][:port] : params[:smtp][:port]
    domain = params[:smtp][:domain].nil? ? @defaults[:smtp][:domain] : params[:smtp][:domain]
    login = params[:smtp][:user_name].nil? ? @defaults[:smtp][:user_name] : params[:smtp][:user_name]
    password = params[:smtp][:password].nil? ? @defaults[:smtp][:password] : params[:smtp][:password]
    authentication = params[:smtp][:authentication].nil? ? @defaults[:smtp][:authentication] : params[:smtp][:authentication]
   
    email.header['Message-Id'] = "#{MD5.new(Time.now.to_s + 'send_mail_noise').hexdigest}@#{domain}"
    email.header.date = Time.now

    Net::SMTP.start(server, port, domain, login, password, authentication) do |smtp|
      smtp.send_mail RMail::Serialize.write('', email), from_email, email.header.to.addresses
    end
  end
end

Пример использования:

require 'send_mail'

SendMail::send_mail(
  :to => [
    'example1@example.com',
    {:name => 'Exampler2', :email => 'example2@example.com'}
  ],
  :subject => 'Subject',
  :reply_to => 'nixon@nixon-site.ru',
  :body => 'Some text',
  :options => {
    :priority => 2,
    :attachments_transfer_encoding => :quoted_printable
  },
  :attachments => [
    {:filename => 'first.jpeg',
    :mime_type => 'image/jpeg',
    :content => File.open('/path/to/file1') {|file| file.sysread(File.size(file))},
    :transfer_encoding => :base64},
    {:filename => 'second.text',
    :mime_type => 'text/plain',
    :content => File.open('/path/to/file2') {|file| file.sysread(File.size(file))}}
  ]
)

Ключ :to хэша параметров метода send_mail может принимать как массив адресов, так и один адрес. Формат адреса: либо строка с адресом электронной почты, либо хэш из ключей :name (имя адресата) и :email (адрес электронной почты). Ключу :attachments также можно передавать как массив из хэшей, так и один хэш.

Теперь рассмотрим непосредственно описание и допустимые значения хэша параметров метода:

  1. :to — адреса получателей. Допустимые значения для этого поля указаны выше.
  2. :subject — тема сообщения. Строка.
  3. :from — адрес отправителя. Формат такой же как и у элемента массива параметра :to.
  4. :reply_to — адрес для обратной связи. Формат такой же как и у параметра :from.
  5. :body — текст сообщения. Строка.
  6. :options — опции сообщения. Хэш.
    1. :charset — таблица символов, в которой написано сообщение. Строка. Значение должно быть стандартным сокращениям существующих таблиц сиволов.
    2. :priority — приоритет сообщения. Число. Используется в большинстве почтовых программ. Соответствие номера приоритета и его значения можно найти в хэше @priorities метода send_mail.
    3. :quoted_printablequoted-printable-кодирование сообщения. Логический тип.
    4. :attachments_transfer_encoding — метод кодирования содержимого вложений. Символ. Допустимые значения: :base64 (base64-кодирование), :quoted_printable (quoted-printable-кодирование).
  7. :attachments — массив вложений. Хэш.
    1. :filename — имя файла. Строка.
    2. :mime_type — MIME-тип вложения. Стока.
    3. :content — содержимое вложения. Строка.
    4. :transfer_encoding — метод кодирования содержимого вложения. Необязательный параметр. Если не установлен, то используется значение параметра :options[:attachment_transfer_encoding].
  8. :smtp — данные о SMTP-сервере. Хэш.
    1. :address — адрес сервера. Строка.
    2. :port — порт. Число.
    3. :domain — домен. Строка. Используется в HELO-запросе к серверу.
    4. :user_name — имя пользователя. Строка.
    5. :password — пароль. Строка.
    6. :authentication — типа авторизации. Символ. Допустимые значения: :plain, :login, :cram_md5.

Для параметров :smtp, :from и :options можно задать значения по-умолчанию внутри модуля SendMail (хэш @defaults).

Также, следует помнить, что для использования RMail, необходимо установить гем rmail.

Вход для пользователей