module Ocp::Registry class MailClient require "net/smtp" require "thread" DEFAULT_WORKER = 1 DEFAULT_PORT = '25' DEFAULT_HELO = 'ocp.com' TEMPLATES_PATH = 'mail_template' DEFAULT_FROM = 'registry@ocp.com' DEFAULT_TLS = false DEFAULT_ADMIN_EMAIL = 'admin@ocp.com' def initialize(mail_config) validate_opinions(mail_config) @logger = Ocp::Registry.logger @worker = mail_config["worker"] || DEFAULT_WORKER @enable_tls = mail_config["enable_tls"] || DEFAULT_TLS @mail_opinions = { :address => mail_config["smtp_server"] , :port => mail_config["port"] || DEFAULT_PORT , :helo => mail_config["helo"] || DEFAULT_HELO , :user => mail_config["username"] || nil , :secret => mail_config["password"] || nil , :authtype => mail_config["authentication"] || nil } @admin_emails = mail_config["admin_emails"] || DEFAULT_ADMIN_EMAIL @mail_queue = Queue.new setup_senders end def admin_emails @admin_emails end def send_mail(mail_info) mail_info.merge!({ :from => DEFAULT_FROM }) @mail_queue << mail_info if mail_validated?(mail_info) end def validate_opinions(mail_config) unless mail_config["smtp_server"] && mail_config["admin_emails"] raise ConfigError, "Invalid Mail configuration" end end def mail_validated?(mail_info) unless mail_info[:from] && mail_info[:to] && mail_info[:template] && has_template?(mail_info[:template]) @logger.waring "Mail is ignored because less of necessary fields " return false end true end def has_template?(template) File.exists? File.expand_path("#{TEMPLATES_PATH}/#{template}.erb") end def prepare_message(mail) template = mail.delete(:template) template = File.read(File.expand_path("#{TEMPLATES_PATH}/#{template}.erb")) @logger.debug "Starting prepare message for mail - #{mail.to_s}" begin info = mail[:msg] message = ERB.new(template).result binding rescue Exception => e @logger.error "Load email template failed - #{e.message}" @logger.debug(e.backtrace.join("\n")) end return message , mail[:from] , mail[:to] end private # Here we use multi-thread to realize a provider-consumer mode with a block-queue # TODO: Realize with Fiber def setup_senders @worker.times do |index| thread = Thread.new { create_sender } thread.run @logger.info("Mail sender thread #{index} is running ") end end def create_sender loop do mail = @mail_queue.pop begin if @enable_tls require "tlsmail" Net::SMTP.enable_tls(OpenSSL::SSL::VERIFY_NONE) end @logger.info("Starting SMTP with opinions #{@mail_opinions}") Net::SMTP.start(@mail_opinions[:address] , @mail_opinions[:port] , @mail_opinions[:helo] , @mail_opinions[:user] , @mail_opinions[:secret] , @mail_opinions[:authtype]) do |smtp| until mail.nil? do msg, from, to = prepare_message(mail) smtp.send_message(msg, from, to) @logger.debug("Mail is sent with message \n#{msg}") begin mail = @mail_queue.pop(:non_block => true ) rescue ThreadError => e mail = nil end end end rescue Net::SMTPAuthenticationError => e @logger.error("Mail server authentication failed - #{e.message}", e) @logger.debug(e.backtrace.join("\n")) rescue Net::SMTPError => e @logger.error("Mail is not sent because of SMTP error - #{e.message}") @logger.debug(e.backtrace.join("\n")) rescue Exception => e @logger.error(e.message) @logger.debug(e.backtrace.join("\n")) end end end end end