3

I'm relatively inexperienced and trying to maintain a web app built with Rails 2.3.X and Ruby 1.8.7. Users who create accounts with the app are supposed to receive an automated email with an "activation" link, which is how they confirm that they actually have access to the email address they want to register.

I have found that some, but not all, of my users are getting this activation email. When they don't get the email in their inbox, the email also does not appear in their spam folders.

My suspicion is that the problem has to do with the formatting of the email; maybe some email providers don't like the format and filter it automatically.

I am not sure what information I need to provide to investigate the cause, but here are some places that I thought made sense:

Web form (user can request a new activation link)

<h3>Resend activation link?</h3>
<form action="/send_activation" method="post">
    <input name="authenticity_token" type="hidden" value="<%= form_authenticity_token %>" />
    <p class="field">
    <label for="user_email">Email Address</label>
    <input class="login" id="user_email" name="user[email]" size="45" type="text" />
    </p>
    <div class="clear"></div>  
    <input name="commit" type="submit" value="submit" />
</form>

sessions_controller.rb

def send_activation
  return unless request.post?
  @title = "Send Activation Link"
  @user = User.find_by_email(params[:user][:email])
  if @user && @user.activated_at.blank?
      UserMailer.deliver_signup_notification(@user)
      redirect_to login_url
      flash[:notice] = "Activation link sent." 
  else
      flash[:notice] = "Could not find your email address. Try another." 
  end
end

user_mailer.rb

class UserMailer < ActionMailer::Base

def activation(user)
setup_email(user)
@subject    += 'Your account has been activated!'
@body[:url]  = "http://MYWEBSITE/"
end

integral_mailer.rb

module IntegralMailer
  def perform_delivery_integral_mailer(mail)
    destinations = mail.destinations
    mail.ready_to_send

    helo = smtp_settings[:helo] || "localhost.localdomain"

    ActionMailer::Base::INTEGRAL_MAILER_SERVER.send_mail(helo, mail.from, destinations, mail.encoded)
  end
end

integral_mailer_server.rb

require 'net/smtp'
require 'logger'
require 'fileutils'
require 'mx_lookup'

class IntegralMailerServer
  include FileUtils

  def self.reloadable?; false; end

  def initialize(opts={})
    @times_to_try_busy_host = opts[:times_to_try_busy_host] || 2
    initialize_log( (opts[:age_log_file] || 'daily'), opts[:log_path] )
  end

  def send_mail(helo_domain, from_email, destination_emails, data)
    t = Thread.new do
      destination_emails.each do |dest_email|

        mx_records = MXLookup.for_email(dest_email)

        mx_records.each do |mx_record|
          begin
            do_delivery(mx_record[:host], helo_domain, data, from_email, dest_email) and break
          rescue Net::SMTPFatalError => e
            log_fatal_delivery(dest_email, mx_record[:host], e.message) and break
          rescue Net::SMTPServerBusy => e
            log_delayed_delivery(dest_email, mx_record[:host], e.message)
            # there has to be a better method to use for this
            unless (mx_records.find_all { |r| r == mx_record }).size >= @times_to_try_busy_host
              mx_records << mx_record
            else
              log_fatal_delivery(dest_email, mx_record[:host], e.message)
            end
          rescue => e
            if mx_records.last == mx_record
              log_fatal_delivery(dest_email, mx_record[:host], e.message)
            else
              log_delayed_delivery(dest_email, mx_record[:host], e.message)
            end
          end
        end # mx_records.each

      end # destination_emails.each
    end # Thread.new 
  end # send_mail

  private

  def do_delivery(host, helo, data, from_email, dest_email)
    Net::SMTP.start(host, 25, helo) do |smtp|
      smtp.send_message data, from_email, dest_email
      log_successful_delivery(dest_email, host)
    end; true
  end

  def log_delayed_delivery(email, host, reason)
    msg =  "Message NOT sent to #{email} via #{host}\n  --> #{reason.strip}\n" +
           "  --> However, I'm not giving up just yet."
    @log.warn(msg); msg
  end

  def log_fatal_delivery(email, host, reason)
    msg = "Message NOT sent to #{email} via #{host}\n  --> #{reason.strip}\n" +
          "  --> I'm giving up delivery of this message."
    @log.fatal(msg); msg
  end

  def log_successful_delivery(email, host)
    msg = "Message sent to #{email} via #{host}"
    @log.info(msg); msg
  end

  def initialize_log(age_log_file='daily', log_path=nil)
    if log_path
      @log = Logger.new(log_path, age_log_file)
    else
      if defined?(RAILS_ROOT)
        @log = Logger.new("#{RAILS_ROOT}/log/integral_mailer.log", age_log_file) rescue Logger.new(STDOUT)
      else
        @log = Logger.new(STDOUT)
      end
    end
    @log.info "Initializing"
  end
end
KDP
  • 634
  • 1
  • 11
  • 31

1 Answers1

1

Initially I would confirm the email is being sent each time. Do you have a copy of the LOG showing send success that are reported as failures by users ?

so some form of success count AFTER the send command to a variable, also CC the emails to yourself/ dummy account for inspection. To isolate receipt issues and inspect the headers retrospectivly.

break the procedure down to steps you can confirm instead of or as well as sending an email write a file to the server and inspect its content and formatting.

you need examples of success and examples of failure, testing emails is difficult at best unless you have a copy of the "lost" email.

I have had success using ActionMailer with an external delviery agent consider moving to this ? its simpler and more resilient once you've set up the agent.

Do you have record of the content of the email as well ?

Steven Moffat
  • 361
  • 4
  • 16
  • 1
    An integral_mailer.log is created automatically each day and then archived. The logs show that a few users with the issue were sent emails: `Message sent to USER@yahoo.com via mta6.am0.yahoodns.net` and `Message sent to USER@gmail.com via gmail-smtp-in.l.google.com` are examples. I can't confirm this was the "activation" email; it's possible the user requested a password reset, but the timing suggests that's not the case. I tested using guerilla mail and the activation message was never sent. I tested using tempmailaddress.com and it worked each time. – KDP Mar 21 '19 at 18:44
  • So the same email is rejected dependant on the recipient address ? Have you checked how "clean" your IP address is on sendscore or also if blacklisted anywhere ? – Steven Moffat Mar 21 '19 at 21:11
  • It looks pretty clean. SendScore gave it a 90 (infrastructure dinged slightly), but no blacklists. Confirmed with mxtoolbox.com. Also, each time user reports this issue, I have him check to see if it went to spam email folder, but it's never been the case. – KDP Mar 22 '19 at 00:33
  • Not sure if this helps, but web hosting company looked at the header for an activation email from site and said, "I didn't see any relay service in the logs at all. The headers also don't show anything hitting a relay that I could see in /var/log/postfix. It appears mail is going through [our site]." Not really sure what means. – KDP Mar 22 '19 at 00:39