From 7070b1748c5320884db85eabad60381c7db7d898 Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Sun, 23 Aug 2015 22:21:04 +0200 Subject: [PATCH] Refactoring of email detection (added also tests now). --- app/controllers/getting_started_controller.rb | 769 ++---------------- app/models/channel.rb | 6 +- app/models/channel/imap.rb | 2 +- app/models/channel/pop3.rb | 2 +- lib/email_helper.rb | 614 ++++++++++++++ lib/email_helper/probe.rb | 376 +++++++++ lib/email_helper/verify.rb | 106 +++ test/unit/email_helper_test.rb | 445 ++++++++++ 8 files changed, 1611 insertions(+), 709 deletions(-) create mode 100644 lib/email_helper.rb create mode 100644 lib/email_helper/probe.rb create mode 100644 lib/email_helper/verify.rb create mode 100644 test/unit/email_helper_test.rb diff --git a/app/controllers/getting_started_controller.rb b/app/controllers/getting_started_controller.rb index e99b07c79..3dc56d4d6 100644 --- a/app/controllers/getting_started_controller.rb +++ b/app/controllers/getting_started_controller.rb @@ -193,415 +193,11 @@ curl http://localhost/api/v1/getting_started -v -u #{login}:#{password} # check admin permissions return if deny_if_not_role(Z_ROLENAME_ADMIN) - # validation - user = nil - domain = nil - if params[:email] =~ /^(.+?)@(.+?)$/ - user = $1 - domain = $2 - end - - if !user || !domain - render json: { - result: 'invalid', - messages: { - email: 'Invalid email.' - }, - } - return - end - - # check domain based attributes - provider_map = { - google: { - domain: 'gmail.com|googlemail.com|gmail.de', - inbound: { - adapter: 'imap', - options: { - host: 'imap.gmail.com', - port: '993', - ssl: true, - user: params[:email], - password: params[:password], - }, - }, - outbound: { - adapter: 'smtp', - options: { - host: 'smtp.gmail.com', - port: '25', - start_tls: true, - user: params[:email], - password: params[:password], - } - }, - }, - microsoft: { - domain: 'outlook.com|hotmail.com', - inbound: { - adapter: 'imap', - options: { - host: 'imap-mail.outlook.com', - port: '993', - ssl: true, - user: params[:email], - password: params[:password], - }, - }, - outbound: { - adapter: 'smtp', - options: { - host: 'smtp-mail.outlook.com', - port: 25, - start_tls: true, - user: params[:email], - password: params[:password], - } - }, - }, - } - - # probe based on email domain and mx - domains = [domain] - mail_exchangers = mxers(domain) - if mail_exchangers && mail_exchangers[0] - logger.info "MX for #{domain}: #{mail_exchangers} - #{mail_exchangers[0][0]}" - end - if mail_exchangers && mail_exchangers[0] && mail_exchangers[0][0] - domains.push mail_exchangers[0][0] - end - provider_map.each {|_provider, settings| - domains.each {|domain_to_check| - - next if domain_to_check !~ /#{settings[:domain]}/i - - # probe inbound - result = email_probe_inbound( settings[:inbound] ) - if result[:result] != 'ok' - render json: result - return # rubocop:disable Lint/NonLocalExitFromIterator - end - - # probe outbound - result = email_probe_outbound( settings[:outbound], params[:email] ) - if result[:result] != 'ok' - render json: result - return # rubocop:disable Lint/NonLocalExitFromIterator - end - - render json: { - result: 'ok', - setting: settings, - } - return # rubocop:disable Lint/NonLocalExitFromIterator - } - } - - # probe inbound - inbound_map = [] - if mail_exchangers && mail_exchangers[0] && mail_exchangers[0][0] - inbound_mx = [ - { - adapter: 'imap', - options: { - host: mail_exchangers[0][0], - port: 993, - ssl: true, - user: user, - password: params[:password], - }, - }, - { - adapter: 'imap', - options: { - host: mail_exchangers[0][0], - port: 993, - ssl: true, - user: params[:email], - password: params[:password], - }, - }, - ] - inbound_map = inbound_map + inbound_mx - end - inbound_auto = [ - { - adapter: 'imap', - options: { - host: "mail.#{domain}", - port: 993, - ssl: true, - user: user, - password: params[:password], - }, - }, - { - adapter: 'imap', - options: { - host: "mail.#{domain}", - port: 993, - ssl: true, - user: params[:email], - password: params[:password], - }, - }, - { - adapter: 'imap', - options: { - host: "imap.#{domain}", - port: 993, - ssl: true, - user: user, - password: params[:password], - }, - }, - { - adapter: 'imap', - options: { - host: "imap.#{domain}", - port: 993, - ssl: true, - user: params[:email], - password: params[:password], - }, - }, - { - adapter: 'pop3', - options: { - host: "mail.#{domain}", - port: 995, - ssl: true, - user: user, - password: params[:password], - }, - }, - { - adapter: 'pop3', - options: { - host: "mail.#{domain}", - port: 995, - ssl: true, - user: params[:email], - password: params[:password], - }, - }, - { - adapter: 'pop3', - options: { - host: "pop.#{domain}", - port: 995, - ssl: true, - user: user, - password: params[:password], - }, - }, - { - adapter: 'pop3', - options: { - host: "pop.#{domain}", - port: 995, - ssl: true, - user: params[:email], - password: params[:password], - }, - }, - { - adapter: 'pop3', - options: { - host: "pop3.#{domain}", - port: 995, - ssl: true, - user: user, - password: params[:password], - }, - }, - { - adapter: 'pop3', - options: { - host: "pop3.#{domain}", - port: 995, - ssl: true, - user: params[:email], - password: params[:password], - }, - }, - ] - inbound_map = inbound_map + inbound_auto - settings = {} - success = false - inbound_map.each {|config| - logger.info "INBOUND PROBE: #{config.inspect}" - result = email_probe_inbound( config ) - logger.info "INBOUND RESULT: #{result.inspect}" - - next if result[:result] != 'ok' - - success = true - settings[:inbound] = config - break - } - - if !success - render json: { - result: 'failed', - } - return - end - - # probe outbound - outbound_map = [] - if mail_exchangers && mail_exchangers[0] && mail_exchangers[0][0] - outbound_mx = [ - { - adapter: 'smtp', - options: { - host: mail_exchangers[0][0], - port: 25, - start_tls: true, - user: user, - password: params[:password], - }, - }, - { - adapter: 'smtp', - options: { - host: mail_exchangers[0][0], - port: 25, - start_tls: true, - user: params[:email], - password: params[:password], - }, - }, - { - adapter: 'smtp', - options: { - host: mail_exchangers[0][0], - port: 465, - start_tls: true, - user: user, - password: params[:password], - }, - }, - { - adapter: 'smtp', - options: { - host: mail_exchangers[0][0], - port: 465, - start_tls: true, - user: params[:email], - password: params[:password], - }, - }, - ] - outbound_map = outbound_map + outbound_mx - end - outbound_auto = [ - { - adapter: 'smtp', - options: { - host: "mail.#{domain}", - port: 25, - start_tls: true, - user: user, - password: params[:password], - }, - }, - { - adapter: 'smtp', - options: { - host: "mail.#{domain}", - port: 25, - start_tls: true, - user: params[:email], - password: params[:password], - }, - }, - { - adapter: 'smtp', - options: { - host: "mail.#{domain}", - port: 465, - start_tls: true, - user: user, - password: params[:password], - }, - }, - { - adapter: 'smtp', - options: { - host: "mail.#{domain}", - port: 465, - start_tls: true, - user: params[:email], - password: params[:password], - }, - }, - { - adapter: 'smtp', - options: { - host: "smtp.#{domain}", - port: 25, - start_tls: true, - user: user, - password: params[:password], - }, - }, - { - adapter: 'smtp', - options: { - host: "smtp.#{domain}", - port: 25, - start_tls: true, - user: params[:email], - password: params[:password], - }, - }, - { - adapter: 'smtp', - options: { - host: "smtp.#{domain}", - port: 465, - start_tls: true, - user: user, - password: params[:password], - }, - }, - { - adapter: 'smtp', - options: { - host: "smtp.#{domain}", - port: 465, - start_tls: true, - user: params[:email], - password: params[:password], - }, - }, - ] - - success = false - outbound_map.each {|config| - logger.info "OUTBOUND PROBE: #{config.inspect}" - result = email_probe_outbound( config, params[:email] ) - logger.info "OUTBOUND RESULT: #{result.inspect}" - - next if result[:result] != 'ok' - - success = true - settings[:outbound] = config - break - } - - if !success - render json: { - result: 'failed', - } - return - end - - render json: { - result: 'ok', - setting: settings, - } + # probe settings based on email and password + render json: EmailHelper::Probe.full( + email: params[:email], + password: params[:password], + ) end def email_outbound @@ -609,18 +205,8 @@ curl http://localhost/api/v1/getting_started -v -u #{login}:#{password} # check admin permissions return if deny_if_not_role(Z_ROLENAME_ADMIN) - # validate params - if !params[:adapter] - render json: { - result: 'invalid', - } - return - end - # connection test - result = email_probe_outbound( params, params[:email] ) - - render json: result + render json: EmailHelper::Probe.outbound(params, params[:email]) end def email_inbound @@ -628,18 +214,8 @@ curl http://localhost/api/v1/getting_started -v -u #{login}:#{password} # check admin permissions return if deny_if_not_role(Z_ROLENAME_ADMIN) - # validate params - if !params[:adapter] - render json: { - result: 'invalid', - } - return - end - # connection test - result = email_probe_inbound( params ) - - render json: result + render json: EmailHelper::Probe.inbound(params) end def email_verify @@ -653,295 +229,80 @@ curl http://localhost/api/v1/getting_started -v -u #{login}:#{password} else subject = params[:subject] end - result = email_probe_outbound( params[:outbound], params[:meta][:email], subject ) - (1..5).each { - sleep 10 + result = EmailHelper::Verify.email( + outbound: params[:outbound], + inbound: params[:inbound], + sender: params[:meta][:email], + subject: subject, + ) - # fetch mailbox - found = nil + # check delivery for 30 sek. + if result[:result] != 'ok' + render json: result + return + end - begin - if params[:inbound][:adapter] =~ /^imap$/i - found = Channel::IMAP.new.fetch( { options: params[:inbound][:options] }, 'verify', subject ) - else - found = Channel::POP3.new.fetch( { options: params[:inbound][:options] }, 'verify', subject ) - end - rescue => e - render json: { - result: 'invalid', - message: e.to_s, - subject: subject, - } - return - end - - next if !found - next if found != 'verify ok' - - # remember address - address = EmailAddress.where( email: params[:meta][:email] ).first - if !address - address = EmailAddress.first - end - if address - address.update_attributes( - realname: params[:meta][:realname], - email: params[:meta][:email], - active: 1, - updated_by_id: 1, - created_by_id: 1, - ) - else - EmailAddress.create( - realname: params[:meta][:realname], - email: params[:meta][:email], - active: 1, - updated_by_id: 1, - created_by_id: 1, - ) - end - - # store mailbox - Channel.create( - area: 'Email::Inbound', - adapter: params[:inbound][:adapter], - options: params[:inbound][:options], - group_id: 1, + # remember address + address = EmailAddress.where( email: params[:meta][:email] ).first + if !address + address = EmailAddress.first + end + if address + address.update_attributes( + realname: params[:meta][:realname], + email: params[:meta][:email], active: 1, updated_by_id: 1, created_by_id: 1, ) + else + EmailAddress.create( + realname: params[:meta][:realname], + email: params[:meta][:email], + active: 1, + updated_by_id: 1, + created_by_id: 1, + ) + end - # save settings - if params[:outbound][:adapter] =~ /^smtp$/i - smtp = Channel.where( adapter: 'SMTP', area: 'Email::Outbound' ).first - smtp.options = params[:outbound][:options] - smtp.active = true - smtp.save! - sendmail = Channel.where( adapter: 'Sendmail' ).first - sendmail.active = false - sendmail.save! - else - sendmail = Channel.where( adapter: 'Sendmail', area: 'Email::Outbound' ).first - sendmail.options = {} - sendmail.active = true - sendmail.save! - smtp = Channel.where( adapter: 'SMTP' ).first - smtp.active = false - smtp.save - end + # store mailbox + Channel.create( + area: 'Email::Inbound', + adapter: params[:inbound][:adapter], + options: params[:inbound][:options], + group_id: 1, + active: 1, + updated_by_id: 1, + created_by_id: 1, + ) - render json: { - result: 'ok', - } - return - } + # save settings + if params[:outbound][:adapter] =~ /^smtp$/i + smtp = Channel.where( adapter: 'SMTP', area: 'Email::Outbound' ).first + smtp.options = params[:outbound][:options] + smtp.active = true + smtp.save! + sendmail = Channel.where( adapter: 'Sendmail' ).first + sendmail.active = false + sendmail.save! + else + sendmail = Channel.where( adapter: 'Sendmail', area: 'Email::Outbound' ).first + sendmail.options = {} + sendmail.active = true + sendmail.save! + smtp = Channel.where( adapter: 'SMTP' ).first + smtp.active = false + smtp.save + end - # check delivery for 30 sek. render json: { - result: 'invalid', - message: 'Verification Email not found in mailbox.', - subject: subject, + result: 'ok', } end private - def email_probe_outbound(params, email, subject = nil) - - # validate params - if !params[:adapter] - result = { - result: 'invalid', - message: 'Invalid, need adapter!', - } - return result - end - - if subject - mail = { - :from => email, - :to => email, - :subject => "Zammad Getting started Test Email #{subject}", - :body => "This is a Test Email of Zammad to check if sending and receiving is working correctly.\n\nYou can ignore or delete this email.", - 'x-zammad-ignore' => 'true', - } - else - mail = { - from: email, - to: 'emailtrytest@znuny.com', - subject: 'test', - body: 'test', - } - end - - # test connection - translation_map = { - 'authentication failed' => 'Authentication failed!', - 'Incorrect username' => 'Authentication failed!', - 'getaddrinfo: nodename nor servname provided, or not known' => 'Hostname not found!', - 'No route to host' => 'No route to host!', - 'Connection refused' => 'Connection refused!', - } - if params[:adapter] =~ /^smtp$/i - - # in case, fill missing params - if !params[:options].key?(:port) - params[:options][:port] = 25 - end - if !params[:options].key?(:ssl) - params[:options][:ssl] = true - end - - begin - Channel::SMTP.new.send( - mail, - { - options: params[:options] - } - ) - rescue => e - - # check if sending email was ok, but mailserver rejected - if !subject - white_map = { - 'Recipient address rejected' => true, - } - white_map.each {|key, _message| - - next if e.message !~ /#{Regexp.escape(key)}/i - - result = { - result: 'ok', - settings: params, - notice: e.message, - } - return result - } - end - message_human = '' - translation_map.each {|key, message| - if e.message =~ /#{Regexp.escape(key)}/i - message_human = message - end - } - result = { - result: 'invalid', - settings: params, - message: e.message, - message_human: message_human, - } - return result - end - result = { - result: 'ok', - } - return result - end - - begin - Channel::Sendmail.new.send( - mail, - nil - ) - rescue => e - message_human = '' - translation_map.each {|key, message| - if e.message =~ /#{Regexp.escape(key)}/i - message_human = message - end - } - result = { - result: 'invalid', - settings: params, - message: e.message, - message_human: message_human, - } - return result - end - result = { - result: 'ok', - } - result - end - - def email_probe_inbound(params) - - # validate params - if !params[:adapter] - fail 'need :adapter param' - end - - # connection test - translation_map = { - 'authentication failed' => 'Authentication failed!', - 'Incorrect username' => 'Authentication failed!', - 'getaddrinfo: nodename nor servname provided, or not known' => 'Hostname not found!', - 'No route to host' => 'No route to host!', - 'Connection refused' => 'Connection refused!', - } - if params[:adapter] =~ /^imap$/i - - begin - Channel::IMAP.new.fetch( { options: params[:options] }, 'check' ) - rescue => e - message_human = '' - translation_map.each {|key, message| - if e.message =~ /#{Regexp.escape(key)}/i - message_human = message - end - } - result = { - result: 'invalid', - settings: params, - message: e.message, - message_human: message_human, - } - return result - end - result = { - result: 'ok', - } - return result - end - - begin - Channel::POP3.new.fetch( { options: params[:options] }, 'check' ) - rescue => e - message_human = '' - translation_map.each {|key, message| - if e.message =~ /#{Regexp.escape(key)}/i - message_human = message - end - } - result = { - result: 'invalid', - settings: params, - message: e.message, - message_human: message_human, - } - return result - end - result = { - result: 'ok', - } - result - end - - def mxers(domain) - begin - mxs = Resolv::DNS.open do |dns| - ress = dns.getresources(domain, Resolv::DNS::Resource::IN::MX) - ress.map { |r| [r.exchange.to_s, IPSocket.getaddress(r.exchange.to_s), r.preference] } - end - rescue => e - logger.error e.message - logger.error e.backtrace.inspect - end - mxs - end - def auto_wizard_enabled_response return false if !AutoWizard.enabled? diff --git a/app/models/channel.rb b/app/models/channel.rb index 52aea0951..12fa1dc87 100644 --- a/app/models/channel.rb +++ b/app/models/channel.rb @@ -11,13 +11,13 @@ class Channel < ApplicationModel # 'warning: toplevel constant Twitter referenced by Channel::Twitter' error e.g. # so we have to convert the channel name to the filename via Rails String.underscore # http://stem.ps/rails/2015/01/25/ruby-gotcha-toplevel-constant-referenced-by.html - require "channel/#{channel[:adapter].underscore}" + require "channel/#{channel[:adapter].to_filename}" - channel_object = Object.const_get("Channel::#{channel[:adapter]}") + channel_object = Object.const_get("Channel::#{channel[:adapter].to_classname}") channel_instance = channel_object.new channel_instance.fetch(channel) rescue => e - logger.error "Can't use Channel::#{channel[:adapter]}" + logger.error "Can't use Channel::#{channel[:adapter].to_classname}" logger.error e.inspect logger.error e.backtrace end diff --git a/app/models/channel/imap.rb b/app/models/channel/imap.rb index ccf230016..d0a00f65a 100644 --- a/app/models/channel/imap.rb +++ b/app/models/channel/imap.rb @@ -2,7 +2,7 @@ require 'net/imap' -class Channel::IMAP < Channel::EmailParser +class Channel::Imap < Channel::EmailParser def fetch (channel, check_type = '', verify_string = '') ssl = true diff --git a/app/models/channel/pop3.rb b/app/models/channel/pop3.rb index 1a98c6502..d96d85c31 100644 --- a/app/models/channel/pop3.rb +++ b/app/models/channel/pop3.rb @@ -2,7 +2,7 @@ require 'net/pop' -class Channel::POP3 < Channel::EmailParser +class Channel::Pop3 < Channel::EmailParser def fetch (channel, check_type = '', verify_string = '') ssl = true diff --git a/lib/email_helper.rb b/lib/email_helper.rb new file mode 100644 index 000000000..2697fd1b5 --- /dev/null +++ b/lib/email_helper.rb @@ -0,0 +1,614 @@ +module EmailHelper + +=begin + +get mail parts + + user, domain = EmailHelper.parse_email('somebody@example.com') + +returns + + [user, domain] + +=end + + def self.parse_email(email) + user = nil + domain = nil + if email =~ /^(.+?)@(.+?)$/ + user = $1 + domain = $2 + end + [user, domain] + end + +=begin + +get list of providers with inbound and outbound settings + + map = EmailHelper.provider(email, password) + +returns + + { + google: { + domain: 'gmail.com|googlemail.com|gmail.de', + inbound: { + adapter: 'imap', + options: { + host: 'imap.gmail.com', + port: 993, + ssl: true, + user: email, + password: password, + }, + }, + outbound: { + adapter: 'smtp', + options: { + host: 'smtp.gmail.com', + port: 25, + start_tls: true, + user: email, + password: password, + } + }, + }, + ... + } + +=end + + def self.provider(email, password) + # check domain based attributes + provider_map = { + google: { + domain: 'gmail.com|googlemail.com|gmail.de', + inbound: { + adapter: 'imap', + options: { + host: 'imap.gmail.com', + port: 993, + ssl: true, + user: email, + password: password, + }, + }, + outbound: { + adapter: 'smtp', + options: { + host: 'smtp.gmail.com', + port: 25, + start_tls: true, + user: email, + password: password, + } + }, + }, + microsoft: { + domain: 'outlook.com|hotmail.com', + inbound: { + adapter: 'imap', + options: { + host: 'imap-mail.outlook.com', + port: 993, + ssl: true, + user: email, + password: password, + }, + }, + outbound: { + adapter: 'smtp', + options: { + host: 'smtp-mail.outlook.com', + port: 25, + start_tls: true, + user: email, + password: password, + } + }, + }, + } + provider_map + end + +=begin + +get possible inbound settings based on mx + + map = EmailHelper.provider_inbound_mx(user, email, password, mx_domains) + +returns + + { + adapter: 'imap', + options: { + host: mx_domains[0], + port: 993, + ssl: true, + user: user, + password: password, + }, + }, + { + adapter: 'imap', + options: { + host: mx_domains[0], + port: 993, + ssl: true, + user: email, + password: password, + }, + }, + +=end + + def self.provider_inbound_mx(user, email, password, mx_domains) + inbound_mxs = [] + mx_domains.each {|domain| + inbound_mx = [ + { + adapter: 'imap', + options: { + host: domain, + port: 993, + ssl: true, + user: user, + password: password, + }, + }, + { + adapter: 'imap', + options: { + host: domain, + port: 993, + ssl: true, + user: email, + password: password, + }, + }, + ] + puts "ll #{inbound_mx.inspect}" + inbound_mxs = inbound_mxs.concat(inbound_mx) + } + inbound_mxs + end + +=begin + +get possible inbound settings based on mx + + map = EmailHelper.provider_inbound_mx(user, email, password, mx_domains) + +returns + + { + adapter: 'imap', + options: { + host: mx_domains[0], + port: 993, + ssl: true, + user: user, + password: password, + }, + }, + { + adapter: 'imap', + options: { + host: mx_domains[0], + port: 993, + ssl: true, + user: email, + password: password, + }, + }, + +=end + + def self.provider_inbound_mx(user, email, password, mx_domains) + inbounds = [] + mx_domains.each {|domain| + inbound = [ + { + adapter: 'imap', + options: { + host: domain, + port: 993, + ssl: true, + user: user, + password: password, + }, + }, + { + adapter: 'imap', + options: { + host: domain, + port: 993, + ssl: true, + user: email, + password: password, + }, + }, + ] + inbounds = inbounds.concat(inbound) + } + inbounds + end + +=begin + +get possible inbound settings based on guess + + map = EmailHelper.provider_inbound_guess(user, email, password, domain) + +returns + + { + adapter: 'imap', + options: { + host: "mail.#{domain}", + port: 993, + ssl: true, + user: user, + password: password, + }, + }, + { + adapter: 'imap', + options: { + host: "mail.#{domain}", + port: 993, + ssl: true, + user: email, + password: password, + }, + }, + ... + +=end + + def self.provider_inbound_guess(user, email, password, domain) + inbound_mx = [ + { + adapter: 'imap', + options: { + host: "mail.#{domain}", + port: 993, + ssl: true, + user: user, + password: password, + }, + }, + { + adapter: 'imap', + options: { + host: "mail.#{domain}", + port: 993, + ssl: true, + user: email, + password: password, + }, + }, + { + adapter: 'imap', + options: { + host: "imap.#{domain}", + port: 993, + ssl: true, + user: user, + password: password, + }, + }, + { + adapter: 'imap', + options: { + host: "imap.#{domain}", + port: 993, + ssl: true, + user: email, + password: password, + }, + }, + { + adapter: 'pop3', + options: { + host: "mail.#{domain}", + port: 995, + ssl: true, + user: user, + password: password, + }, + }, + { + adapter: 'pop3', + options: { + host: "mail.#{domain}", + port: 995, + ssl: true, + user: email, + password: password, + }, + }, + { + adapter: 'pop3', + options: { + host: "pop.#{domain}", + port: 995, + ssl: true, + user: user, + password: password, + }, + }, + { + adapter: 'pop3', + options: { + host: "pop.#{domain}", + port: 995, + ssl: true, + user: email, + password: password, + }, + }, + { + adapter: 'pop3', + options: { + host: "pop3.#{domain}", + port: 995, + ssl: true, + user: user, + password: password, + }, + }, + { + adapter: 'pop3', + options: { + host: "pop3.#{domain}", + port: 995, + ssl: true, + user: email, + password: password, + }, + }, + ] + inbound_mx + end + +=begin + +get possible outbound settings based on mx + + map = EmailHelper.provider_outbound_mx(user, email, password, mx_domains) + +returns + + { + adapter: 'smtp', + options: { + host: domain, + port: 25, + start_tls: true, + user: user, + password: password, + }, + }, + { + adapter: 'smtp', + options: { + host: domain, + port: 25, + start_tls: true, + user: email, + password: password, + }, + }, + +=end + + def self.provider_outbound_mx(user, email, password, mx_domains) + outbound_mxs = [] + mx_domains.each {|domain| + outbound_mx = [ + { + adapter: 'smtp', + options: { + host: domain, + port: 25, + start_tls: true, + user: user, + password: password, + }, + }, + { + adapter: 'smtp', + options: { + host: domain, + port: 25, + start_tls: true, + user: email, + password: password, + }, + }, + { + adapter: 'smtp', + options: { + host: domain, + port: 465, + start_tls: true, + user: user, + password: password, + }, + }, + { + adapter: 'smtp', + options: { + host: domain, + port: 465, + start_tls: true, + user: email, + password: password, + }, + }, + ] + outbound_mxs = outbound_mxs.concat(outbound_mx) + } + outbound_mxs + end + +=begin + +get possible outbound settings based on guess + + map = EmailHelper.provider_outbound_guess(user, email, password, domain) + +returns + + { + adapter: 'imap', + options: { + host: "mail.#{domain}", + port: 993, + ssl: true, + user: user, + password: password, + }, + }, + { + adapter: 'imap', + options: { + host: "mail.#{domain}", + port: 993, + ssl: true, + user: email, + password: password, + }, + }, + ... + +=end + + def self.provider_outbound_guess(user, email, password, domain) + outbound_mx = [ + { + adapter: 'smtp', + options: { + host: "mail.#{domain}", + port: 25, + start_tls: true, + user: user, + password: password, + }, + }, + { + adapter: 'smtp', + options: { + host: "mail.#{domain}", + port: 25, + start_tls: true, + user: email, + password: password, + }, + }, + { + adapter: 'smtp', + options: { + host: "mail.#{domain}", + port: 465, + start_tls: true, + user: user, + password: password, + }, + }, + { + adapter: 'smtp', + options: { + host: "mail.#{domain}", + port: 465, + start_tls: true, + user: email, + password: password, + }, + }, + { + adapter: 'smtp', + options: { + host: "smtp.#{domain}", + port: 25, + start_tls: true, + user: user, + password: password, + }, + }, + { + adapter: 'smtp', + options: { + host: "smtp.#{domain}", + port: 25, + start_tls: true, + user: email, + password: password, + }, + }, + { + adapter: 'smtp', + options: { + host: "smtp.#{domain}", + port: 465, + start_tls: true, + user: user, + password: password, + }, + }, + { + adapter: 'smtp', + options: { + host: "smtp.#{domain}", + port: 465, + start_tls: true, + user: email, + password: password, + }, + }, + ] + outbound_mx + end + +=begin + +get dns mx records of domain + + mx_records = EmailHelper.mx_records('example.com') + +returns + + ['mx1.example.com', 'mx2.example.com'] + +=end + + def self.mx_records(domain) + mail_exchangers = mxers(domain) + if mail_exchangers && mail_exchangers[0] + Rails.logger.info "MX for #{domain}: #{mail_exchangers} - #{mail_exchangers[0][0]}" + end + mx_records = [] + if mail_exchangers && mail_exchangers[0] && mail_exchangers[0][0] + mx_records.push mail_exchangers[0][0] + end + mx_records + end + + def self.mxers(domain) + begin + mxs = Resolv::DNS.open do |dns| + ress = dns.getresources(domain, Resolv::DNS::Resource::IN::MX) + ress.map { |r| + [r.exchange.to_s, IPSocket.getaddress(r.exchange.to_s), r.preference] + } + end + rescue => e + Rails.logger.error e.message + Rails.logger.error e.backtrace.inspect + end + mxs + end + +end \ No newline at end of file diff --git a/lib/email_helper/probe.rb b/lib/email_helper/probe.rb new file mode 100644 index 000000000..4bfd14ed8 --- /dev/null +++ b/lib/email_helper/probe.rb @@ -0,0 +1,376 @@ +module EmailHelper + class Probe + +=begin + +get result of probe + + result = EmailHelper::Probe.full( + email: 'znuny@example.com', + password: 'somepassword', + ) + +returns on success + + { + result: 'ok', + inbound: { + adapter: 'imap', + options: { + host: 'imap.gmail.com', + port: 993, + ssl: true, + user: 'some@example.com', + password: 'password', + }, + }, + outbound: { + adapter: 'smtp', + options: { + host: 'smtp.gmail.com', + port: 25, + ssl: true, + user: 'some@example.com', + password: 'password', + }, + }, + } + +returns on fail + + result = { + result: 'failed', + } + +=end + + def self.full(params) + + user, domain = EmailHelper.parse_email(params[:email]) + + if !user || !domain + result = { + result: 'invalid', + messages: { + email: 'Invalid email.' + }, + } + return result + end + + # probe provider based settings + provider_map = EmailHelper.provider(params[:email], params[:password]) + domains = [domain] + + # get mx records, try to find provider based on mx records + mx_records = EmailHelper.mx_records(domain) + domains = domains.concat(mx_records) + provider_map.each {|_provider, settings| + domains.each {|domain_to_check| + + next if domain_to_check !~ /#{settings[:domain]}/i + + # probe inbound + result = EmailHelper::Probe.inbound(settings[:inbound]) + next if result[:result] != 'ok' + + # probe outbound + result = EmailHelper::Probe.outbound(settings[:outbound], params[:email]) + next if result[:result] != 'ok' + + result = { + result: 'ok', + setting: settings, + } + return result + } + } + + # probe guess settings + + # probe inbound + inbound_mx = EmailHelper.provider_inbound_mx(user, params[:email], params[:password], mx_records) + inbound_guess = EmailHelper.provider_inbound_guess(user, params[:email], params[:password], domain) + inbound_map = inbound_mx + inbound_guess + settings = {} + success = false + inbound_map.each {|config| + Rails.logger.info "INBOUND PROBE: #{config.inspect}" + result = EmailHelper::Probe.inbound( config ) + Rails.logger.info "INBOUND RESULT: #{result.inspect}" + + next if result[:result] != 'ok' + + success = true + settings[:inbound] = config + break + } + + if !success + result = { + result: 'failed', + } + return result + end + + # probe outbound + outbound_mx = EmailHelper.provider_outbound_mx(user, params[:email], params[:password], mx_records) + outbound_guess = EmailHelper.provider_outbound_guess(user, params[:email], params[:password], domain) + outbound_map = outbound_mx + outbound_guess + + success = false + outbound_map.each {|config| + Rails.logger.info "OUTBOUND PROBE: #{config.inspect}" + result = EmailHelper::Probe.outbound( config, params[:email] ) + Rails.logger.info "OUTBOUND RESULT: #{result.inspect}" + + next if result[:result] != 'ok' + + success = true + settings[:outbound] = config + break + } + + if !success + result = { + result: 'failed', + } + return result + end + + { + result: 'ok', + setting: settings, + } + end + +=begin + +get result of inbound probe + + result = EmailHelper::Probe.inbound( + adapter: 'imap', + options: { + host: 'imap.gmail.com', + port: 993, + ssl: true, + user: 'some@example.com', + password: 'password', + } + ) + +returns on success + + { + result: 'ok' + } + +returns on fail + + result = { + result: 'invalid', + settings: { + host: 'imap.gmail.com', + port: 993, + ssl: true, + user: 'some@example.com', + password: 'password', + }, + message: 'error message from used lib', + message_human: 'translated error message, readable for humans', + } + +=end + + def self.inbound(params) + + # validate params + if !params[:adapter] + result = { + result: 'invalid', + message: 'Invalid, need adapter!', + } + return result + end + + # connection test + begin + if params[:adapter] =~ /^imap$/i + Channel::Imap.new.fetch( { options: params[:options] }, 'check' ) + elsif params[:adapter] =~ /^pop3$/i + Channel::Pop3.new.fetch( { options: params[:options] }, 'check' ) + else + fail "Invalid adapter '#{params[:adapter]}'" + end + rescue => e + message_human = '' + translations.each {|key, message| + if e.message =~ /#{Regexp.escape(key)}/i + message_human = message + end + } + result = { + result: 'invalid', + settings: params, + message: e.message, + message_human: message_human, + } + return result + end + result = { + result: 'ok', + } + result + end + +=begin + +get result of outbound probe + + result = EmailHelper::Probe.outbound( + { + adapter: 'smtp', + options: { + host: 'smtp.gmail.com', + port: 25, + ssl: true, + user: 'some@example.com', + password: 'password', + } + }, + 'sender@example.com', + ) + +returns on success + + { + result: 'ok' + } + +returns on fail + + result = { + result: 'invalid', + settings: { + host: 'stmp.gmail.com', + port: 25, + ssl: true, + user: 'some@example.com', + password: 'password', + }, + message: 'error message from used lib', + message_human: 'translated error message, readable for humans', + } + +=end + + def self.outbound(params, email, subject = nil) + + # validate params + if !params[:adapter] + result = { + result: 'invalid', + message: 'Invalid, need adapter!', + } + return result + end + + if subject + mail = { + :from => email, + :to => email, + :subject => "Zammad Getting started Test Email #{subject}", + :body => "This is a Test Email of Zammad to check if sending and receiving is working correctly.\n\nYou can ignore or delete this email.", + 'x-zammad-ignore' => 'true', + } + else + mail = { + from: email, + to: 'emailtrytest@znuny.com', + subject: 'This is a Test Email', + body: "This is a Test Email of Zammad to verify if Zammad can send emails to an external address.\n\nIf you see this email, you can ignore and delete it.", + } + end + + # test connection + begin + + if params[:adapter] =~ /^smtp$/i + + # in case, fill missing params + if !params[:options].key?(:port) + params[:options][:port] = 25 + end + if !params[:options].key?(:ssl) + params[:options][:ssl] = true + end + Channel::SMTP.new.send( + mail, + { + options: params[:options] + } + ) + elsif params[:adapter] =~ /^sendmail$/i + Channel::Sendmail.new.send( + mail, + nil + ) + else + fail "Invalid adapter '#{params[:adapter]}'" + end + rescue => e + + # check if sending email was ok, but mailserver rejected + if !subject + white_map = { + 'Recipient address rejected' => true, + } + white_map.each {|key, _message| + + next if e.message !~ /#{Regexp.escape(key)}/i + + result = { + result: 'ok', + settings: params, + notice: e.message, + } + return result + } + end + message_human = '' + translations.each {|key, message| + if e.message =~ /#{Regexp.escape(key)}/i + message_human = message + end + } + result = { + result: 'invalid', + settings: params, + message: e.message, + message_human: message_human, + } + return result + end + result = { + result: 'ok', + } + result + end + + def self.translations + { + 'authentication failed' => 'Authentication failed!', + 'Username and Password not accepted' => 'Authentication failed!', + 'Incorrect username' => 'Authentication failed, username incorrect!', + 'Lookup failed' => 'Authentication failed, username incorrect!', + 'Invalid credentials' => 'Authentication failed, invalid credentials!', + 'getaddrinfo: nodename nor servname provided, or not known' => 'Hostname not found!', + 'No route to host' => 'No route to host!', + 'execution expired' => 'Host not reachable!', + 'Connection refused' => 'Connection refused!', + } + end + + end + +end diff --git a/lib/email_helper/verify.rb b/lib/email_helper/verify.rb new file mode 100644 index 000000000..edcefb013 --- /dev/null +++ b/lib/email_helper/verify.rb @@ -0,0 +1,106 @@ +module EmailHelper + class Verify + +=begin + +get result of inbound probe + + result = EmailHelper::Verify.email( + inbound: { + adapter: 'imap', + options: { + host: 'imap.gmail.com', + port: 993, + ssl: true, + user: 'some@example.com', + password: 'password', + }, + }, + outbound: { + adapter: 'smtp', + options: { + host: 'smtp.gmail.com', + port: 25, + ssl: true, + user: 'some@example.com', + password: 'password', + }, + }, + sender: 'sender@example.com', + ) + +returns on success + + { + result: 'ok' + } + +returns on fail + + { + result: 'invalid', + message: 'Verification Email not found in mailbox.', + subject: subject, + } + +or + + { + result: 'invalid', + message: 'Authentication failed!.', + subject: subject, + } + +=end + + def self.email(params) + + # send verify email + if !params[:subject] + subject = '#' + rand(99_999_999_999).to_s + else + subject = params[:subject] + end + result = EmailHelper::Probe.outbound( params[:outbound], params[:sender], subject ) + + # looking for verify email + (1..5).each { + sleep 10 + + # fetch mailbox + found = nil + + begin + if params[:inbound][:adapter] =~ /^imap$/i + found = Channel::Imap.new.fetch( { options: params[:inbound][:options] }, 'verify', subject ) + else + found = Channel::Pop3.new.fetch( { options: params[:inbound][:options] }, 'verify', subject ) + end + rescue => e + render json: { + result: 'invalid', + message: e.to_s, + subject: subject, + } + return + end + + next if !found + next if found != 'verify ok' + + return { + result: 'ok', + } + + } + + { + result: 'invalid', + message: 'Verification Email not found in mailbox.', + subject: subject, + } + end + + end + +end diff --git a/test/unit/email_helper_test.rb b/test/unit/email_helper_test.rb new file mode 100644 index 000000000..6b1f675c8 --- /dev/null +++ b/test/unit/email_helper_test.rb @@ -0,0 +1,445 @@ +# encoding: utf-8 +require 'test_helper' + +class EmailHelperTest < ActiveSupport::TestCase + + test 'a mx_records' do + + domain = 'znuny.com' + mx_domains = EmailHelper.mx_records(domain) + assert_equal('arber.znuny.com', mx_domains[0]) + + end + + test 'a email parser test' do + + user, domain = EmailHelper.parse_email('somebody@example.com') + assert_equal('somebody', user) + assert_equal('example.com', domain) + + user, domain = EmailHelper.parse_email('somebody+test@example.com') + assert_equal('somebody+test', user) + assert_equal('example.com', domain) + + user, domain = EmailHelper.parse_email('somebody+testexample.com') + assert_not(user) + assert_not(domain) + + end + + test 'provider test' do + email = 'linus@kernel.org' + password = 'some_pw' + map = EmailHelper.provider(email, password) + + assert_equal('imap', map[:google][:inbound][:adapter]) + assert_equal('imap.gmail.com', map[:google][:inbound][:options][:host]) + assert_equal(993, map[:google][:inbound][:options][:port]) + assert_equal(email, map[:google][:inbound][:options][:user]) + assert_equal(password, map[:google][:inbound][:options][:password]) + + assert_equal('smtp', map[:google][:outbound][:adapter]) + assert_equal('smtp.gmail.com', map[:google][:outbound][:options][:host]) + assert_equal(25, map[:google][:outbound][:options][:port]) + assert_equal(true, map[:google][:outbound][:options][:start_tls]) + assert_equal(email, map[:google][:outbound][:options][:user]) + assert_equal(password, map[:google][:outbound][:options][:password]) + + end + + test 'provider_inbound_mx' do + + email = 'linus@znuny.com' + password = 'some_pw' + user, domain = EmailHelper.parse_email(email) + mx_domains = EmailHelper.mx_records(domain) + map = EmailHelper.provider_inbound_mx(user, email, password, mx_domains) + + assert_equal('imap', map[0][:adapter]) + assert_equal('arber.znuny.com', map[0][:options][:host]) + assert_equal(993, map[0][:options][:port]) + assert_equal(user, map[0][:options][:user]) + assert_equal(password, map[0][:options][:password]) + + assert_equal('imap', map[1][:adapter]) + assert_equal('arber.znuny.com', map[1][:options][:host]) + assert_equal(993, map[1][:options][:port]) + assert_equal(email, map[1][:options][:user]) + assert_equal(password, map[1][:options][:password]) + + end + + test 'provider_inbound_guess' do + + email = 'linus@znuny.com' + password = 'some_pw' + user, domain = EmailHelper.parse_email(email) + map = EmailHelper.provider_inbound_guess(user, email, password, domain) + + assert_equal('imap', map[0][:adapter]) + assert_equal('mail.znuny.com', map[0][:options][:host]) + assert_equal(993, map[0][:options][:port]) + assert_equal(user, map[0][:options][:user]) + assert_equal(password, map[0][:options][:password]) + + assert_equal('imap', map[1][:adapter]) + assert_equal('mail.znuny.com', map[1][:options][:host]) + assert_equal(993, map[1][:options][:port]) + assert_equal(email, map[1][:options][:user]) + assert_equal(password, map[1][:options][:password]) + + end + + test 'provider_outbound_mx' do + + email = 'linus@znuny.com' + password = 'some_pw' + user, domain = EmailHelper.parse_email(email) + mx_domains = EmailHelper.mx_records(domain) + map = EmailHelper.provider_outbound_mx(user, email, password, mx_domains) + + assert_equal('smtp', map[0][:adapter]) + assert_equal('arber.znuny.com', map[0][:options][:host]) + assert_equal(25, map[0][:options][:port]) + assert_equal(true, map[0][:options][:start_tls]) + assert_equal(user, map[0][:options][:user]) + assert_equal(password, map[0][:options][:password]) + + assert_equal('smtp', map[1][:adapter]) + assert_equal('arber.znuny.com', map[1][:options][:host]) + assert_equal(25, map[1][:options][:port]) + assert_equal(true, map[1][:options][:start_tls]) + assert_equal(email, map[1][:options][:user]) + assert_equal(password, map[1][:options][:password]) + + end + + test 'provider_outbound_guess' do + + email = 'linus@znuny.com' + password = 'some_pw' + user, domain = EmailHelper.parse_email(email) + map = EmailHelper.provider_outbound_guess(user, email, password, domain) + + assert_equal('smtp', map[0][:adapter]) + assert_equal('mail.znuny.com', map[0][:options][:host]) + assert_equal(25, map[0][:options][:port]) + assert_equal(true, map[0][:options][:start_tls]) + assert_equal(user, map[0][:options][:user]) + assert_equal(password, map[0][:options][:password]) + + assert_equal('smtp', map[1][:adapter]) + assert_equal('mail.znuny.com', map[1][:options][:host]) + assert_equal(25, map[1][:options][:port]) + assert_equal(true, map[1][:options][:start_tls]) + assert_equal(email, map[1][:options][:user]) + assert_equal(password, map[1][:options][:password]) + + end + + test 'z probe_inbound' do + + # network issues + result = EmailHelper::Probe.inbound( + adapter: 'imap', + options: { + host: 'not_existsing_host', + port: 993, + ssl: true, + user: 'some@example.com', + password: 'password', + } + ) + + assert_equal('invalid', result[:result]) + assert_equal('Hostname not found!', result[:message_human]) + assert_equal('not_existsing_host', result[:settings][:options][:host]) + + result = EmailHelper::Probe.inbound( + adapter: 'imap', + options: { + host: 'www.zammad.com', + port: 993, + ssl: true, + user: 'some@example.com', + password: 'password', + } + ) + assert_equal('invalid', result[:result]) + assert_equal('Connection refused!', result[:message_human]) + assert_equal('www.zammad.com', result[:settings][:options][:host]) + + result = EmailHelper::Probe.inbound( + adapter: 'imap', + options: { + host: '172.42.42.42', + port: 993, + ssl: true, + user: 'some@example.com', + password: 'password', + } + ) + assert_equal('invalid', result[:result]) + assert_equal('Host not reachable!', result[:message_human]) + assert_equal('172.42.42.42', result[:settings][:options][:host]) + + # gmail + result = EmailHelper::Probe.inbound( + adapter: 'imap', + options: { + host: 'imap.gmail.com', + port: 993, + ssl: true, + user: 'some@example.com', + password: 'password', + } + ) + assert_equal('invalid', result[:result]) + assert_equal('Authentication failed, username incorrect!', result[:message_human]) + assert_equal('imap.gmail.com', result[:settings][:options][:host]) + + result = EmailHelper::Probe.inbound( + adapter: 'imap', + options: { + host: 'imap.gmail.com', + port: 993, + ssl: true, + user: 'frank.tailor05@googlemail.com', + password: 'password', + } + ) + assert_equal('invalid', result[:result]) + assert_equal('Authentication failed, invalid credentials!', result[:message_human]) + assert_equal('imap.gmail.com', result[:settings][:options][:host]) + + # dovecot + result = EmailHelper::Probe.inbound( + adapter: 'imap', + options: { + host: 'arber.znuny.com', + port: 993, + ssl: true, + user: 'some@example.com', + password: 'password', + } + ) + assert_equal('invalid', result[:result]) + assert_equal('Authentication failed!', result[:message_human]) + assert_equal('arber.znuny.com', result[:settings][:options][:host]) + + # realtest - test I + if !ENV['EMAILHELPER_MAILBOX_1'] + raise "Need EMAILHELPER_MAILBOX_1 as ENV variable like export EMAILHELPER_MAILBOX_1='unittestemailhelper01@znuny.com:somepass'" + return + end + mailbox_user = ENV['EMAILHELPER_MAILBOX_1'].split(':')[0] + mailbox_password = ENV['EMAILHELPER_MAILBOX_1'].split(':')[1] + user, domain = EmailHelper.parse_email(mailbox_user) + result = EmailHelper::Probe.inbound( + adapter: 'imap', + options: { + host: 'arber.znuny.com', + port: 993, + ssl: true, + user: user, + password: mailbox_password, + } + ) + assert_equal('ok', result[:result]) + + end + + test 'z probe_outbound' do + + # network issues + result = EmailHelper::Probe.outbound( + { + adapter: 'smtp', + options: { + host: 'not_existsing_host', + port: 25, + start_tls: true, + user: 'some@example.com', + password: 'password', + } + }, + 'some@example.com', + ) + + assert_equal('invalid', result[:result]) + assert_equal('Hostname not found!', result[:message_human]) + assert_equal('not_existsing_host', result[:settings][:options][:host]) + + result = EmailHelper::Probe.outbound( + { + adapter: 'smtp', + options: { + host: 'www.zammad.com', + port: 25, + start_tls: true, + user: 'some@example.com', + password: 'password', + } + }, + 'some@example.com', + ) + assert_equal('invalid', result[:result]) + assert_equal('Connection refused!', result[:message_human]) + assert_equal('www.zammad.com', result[:settings][:options][:host]) + + result = EmailHelper::Probe.outbound( + { + adapter: 'smtp', + options: { + host: '172.42.42.42', + port: 25, + start_tls: true, + user: 'some@example.com', + password: 'password', + } + }, + 'some@example.com', + ) + assert_equal('invalid', result[:result]) + assert_equal('Host not reachable!', result[:message_human]) + assert_equal('172.42.42.42', result[:settings][:options][:host]) + + # gmail + result = EmailHelper::Probe.outbound( + { + adapter: 'smtp', + options: { + host: 'smtp.gmail.com', + port: 25, + start_tls: true, + user: 'some@example.com', + password: 'password', + } + }, + 'some@example.com', + ) + assert_equal('invalid', result[:result]) + assert_equal('Authentication failed!', result[:message_human]) + assert_equal('smtp.gmail.com', result[:settings][:options][:host]) + + result = EmailHelper::Probe.outbound( + { + adapter: 'smtp', + options: { + host: 'smtp.gmail.com', + port: 25, + start_tls: true, + user: 'frank.tailor05@googlemail.com', + password: 'password', + } + }, + 'some@example.com', + ) + assert_equal('invalid', result[:result]) + assert_equal('Authentication failed!', result[:message_human]) + assert_equal('smtp.gmail.com', result[:settings][:options][:host]) + + # dovecot + result = EmailHelper::Probe.outbound( + { + adapter: 'smtp', + options: { + host: 'arber.znuny.com', + port: 25, + start_tls: true, + user: 'some@example.com', + password: 'password', + } + }, + 'some@example.com', + ) + assert_equal('invalid', result[:result]) + assert_equal('Authentication failed!', result[:message_human]) + assert_equal('arber.znuny.com', result[:settings][:options][:host]) + + # realtest - test I + if !ENV['EMAILHELPER_MAILBOX_1'] + raise "Need EMAILHELPER_MAILBOX_1 as ENV variable like export EMAILHELPER_MAILBOX_1='unittestemailhelper01@znuny.com:somepass'" + return + end + mailbox_user = ENV['EMAILHELPER_MAILBOX_1'].split(':')[0] + mailbox_password = ENV['EMAILHELPER_MAILBOX_1'].split(':')[1] + user, domain = EmailHelper.parse_email(mailbox_user) + result = EmailHelper::Probe.outbound( + { + adapter: 'smtp', + options: { + host: 'arber.znuny.com', + port: 25, + start_tls: true, + user: user, + password: mailbox_password, + } + }, + mailbox_user, + ) + assert_equal('ok', result[:result]) + end + + test 'zz probe' do + + result = EmailHelper::Probe.full( + email: 'invalid_format', + password: 'somepass', + ) + assert_equal('invalid', result[:result]) + assert_not(result[:setting]) + + # realtest - test I + if !ENV['EMAILHELPER_MAILBOX_1'] + raise "Need EMAILHELPER_MAILBOX_1 as ENV variable like export EMAILHELPER_MAILBOX_1='unittestemailhelper01@znuny.com:somepass'" + end + mailbox_user = ENV['EMAILHELPER_MAILBOX_1'].split(':')[0] + mailbox_password = ENV['EMAILHELPER_MAILBOX_1'].split(':')[1] + + result = EmailHelper::Probe.full( + email: mailbox_user, + password: mailbox_password, + ) + assert_equal('ok', result[:result]) + assert_equal('arber.znuny.com', result[:setting][:inbound][:options][:host]) + assert_equal('arber.znuny.com', result[:setting][:outbound][:options][:host]) + + end + + test 'zz verify' do + + # realtest - test I + if !ENV['EMAILHELPER_MAILBOX_1'] + raise "Need EMAILHELPER_MAILBOX_1 as ENV variable like export EMAILHELPER_MAILBOX_1='unittestemailhelper01@znuny.com:somepass'" + end + mailbox_user = ENV['EMAILHELPER_MAILBOX_1'].split(':')[0] + mailbox_password = ENV['EMAILHELPER_MAILBOX_1'].split(':')[1] + user, domain = EmailHelper.parse_email(mailbox_user) + result = EmailHelper::Verify.email( + inbound: { + adapter: 'imap', + options: { + host: 'arber.znuny.com', + port: 993, + ssl: true, + user: user, + password: mailbox_password, + }, + }, + outbound: { + adapter: 'smtp', + options: { + host: 'arber.znuny.com', + port: 25, + start_tls: true, + user: user, + password: mailbox_password, + }, + }, + sender: mailbox_user, + ) + assert_equal('ok', result[:result]) + end + +end