Class: SesProxy::SmtpServer

Inherits:
EM::P::SmtpServer
  • Object
show all
Defined in:
lib/ses_proxy/smtp_server.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.running?Boolean

Returns:

  • (Boolean)


188
189
190
# File 'lib/ses_proxy/smtp_server.rb', line 188

def self.running?
  !!@server
end

.start(host, port) ⇒ Object



175
176
177
178
179
# File 'lib/ses_proxy/smtp_server.rb', line 175

def self.start(host, port)
  trap(:QUIT) { stop }
  @host = host
  @server = EM.start_server host, port, self
end

.stopObject



181
182
183
184
185
186
# File 'lib/ses_proxy/smtp_server.rb', line 181

def self.stop
  if @server
   EM.stop_server @server
   @server = nil
  end
end

Instance Method Details

#get_server_domainObject



21
22
23
# File 'lib/ses_proxy/smtp_server.rb', line 21

def get_server_domain
  @host || "mock.smtp.server.local"
end

#get_server_greetingObject



25
26
27
# File 'lib/ses_proxy/smtp_server.rb', line 25

def get_server_greeting
  "smtp ses greetings"
end

#messageObject



66
67
68
# File 'lib/ses_proxy/smtp_server.rb', line 66

def message
  @message || ""
end

#process_data_line(ln) ⇒ Object

Allow EM::Protocols::SmtpServer#receive_message to return an Array so that cases other than succeed/fail can be handled. If an Array is returned it is expected to have two elements, the response code and the message (e.g. [451, “Temp Failure”]). Does not break backward compatibility.



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/ses_proxy/smtp_server.rb', line 195

def process_data_line ln
  if ln == "."
    if @databuffer.length > 0
      receive_data_chunk @databuffer
      @databuffer.clear
    end


    succeeded = proc {
      send_data "250 Message accepted\r\n"
      reset_protocol_state
    }
    failed = proc {
      send_data "550 Message rejected\r\n"
      reset_protocol_state
    }

    d = receive_message

    if d.respond_to?(:set_deferred_status)
      d.callback(&succeeded)
      d.errback(&failed)
    elsif d.kind_of?(Array)
      send_data d.join(' ') + "\r\n"
      reset_protocol_state
    else
      (d ? succeeded : failed).call
    end

    @state.delete :data
  else
    # slice off leading . if any
    ln.slice!(0...1) if ln[0] == ?.
    @databuffer << ln
    if @databuffer.length > @@parms[:chunksize]
      receive_data_chunk @databuffer
      @databuffer.clear
    end
  end
end

#receive_data_chunk(data) ⇒ Object



53
54
55
56
# File 'lib/ses_proxy/smtp_server.rb', line 53

def receive_data_chunk(data)
  @message = "#{@message}\n#{data.join("\n")}"
  true
end

#receive_ehlo_domain(domain) ⇒ Object



170
171
172
173
# File 'lib/ses_proxy/smtp_server.rb', line 170

def receive_ehlo_domain(domain)
  @ehlo_domain = domain
  true
end

#receive_messageObject



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/ses_proxy/smtp_server.rb', line 78

def receive_message
  return false unless verified
  mail = Mail.read_from_string(message + "\n") #https://github.com/mikel/mail/issues/612#issuecomment-35564404
  bounced = Bounce.where({:email=>{"$in"=>recipients}, :count=>{"$gte"=>2}, :retry_at=>{"$gte"=>Time.now}}).map(&:email)

  #Remove bounced addresses
  actual_recipients = mail.to_addrs - bounced
  actual_cc_addrs = mail.cc_addrs - bounced
  actual_bcc_addrs = recipients - (mail.to_addrs + mail.cc_addrs) - bounced

  #Remove blacklisted domains
  if SesProxy::Conf.get[:blacklisted_domains] and SesProxy::Conf.get[:blacklisted_domains].any?
    bld = SesProxy::Conf.get[:blacklisted_domains]
    _proc = proc {|address| address unless bld.include?(address.split('@').last)}
    actual_recipients.collect!(&_proc).compact!
    actual_cc_addrs.collect!(&_proc).compact!
    actual_bcc_addrs.collect!(&_proc).compact!
  end

  #Remove blacklisted regexp
  if SesProxy::Conf.get[:blacklisted_regexp] and SesProxy::Conf.get[:blacklisted_regexp].any?
    blr = SesProxy::Conf.get[:blacklisted_regexp]
    _proc = proc {|address| address unless blr.map{|regexp| Regexp.new(regexp).match(address)}.compact.any? }
    actual_recipients.collect!(&_proc).compact!
    actual_cc_addrs.collect!(&_proc).compact!
    actual_bcc_addrs.collect!(&_proc).compact!
  end

  original_number = recipients.size
  filtered_number = actual_recipients.size + actual_cc_addrs.size + actual_bcc_addrs.size
  record = RecipientsNumber.new({
    :original=>original_number,
    :filtered=>filtered_number,
    :created_at => Time.now,
    :updated_at => Time.now
  })
  record.save!
  if actual_recipients.any?
    mail.to = actual_recipients.uniq.join(",")
    mail.cc = actual_cc_addrs.uniq.join(",")
    mail.bcc = actual_bcc_addrs.uniq.join(",")
    mail['X-SES-Proxy'] = 'True'
    unless SesProxy::Conf.get[:collect_sent_mails].eql? false
      record = Email.new({
        :sender => sender,
        :recipients => actual_recipients.uniq.join(","),
        :subject => mail.subject,
        :body => mail.decode_body,
        :system => mail['X-Sender-System']||"Unknown",
        :created_at => Time.now,
        :updated_at => Time.now
      })
      record.save!
    end
    begin
      if SesProxy::Conf.get[:aws][:ses] and SesProxy::Conf.get[:aws][:ses][:username] and SesProxy::Conf.get[:aws][:ses][:password]
        mail.deliver!
      else
        ses.send_raw_email(mail.to_s)
      end
    rescue Exception => e
      print "Error! "
      puts e.message
      return false
    end
  else
    puts "No valid recipients! #{mail.to_addrs}"
  end
  if not original_number.eql? filtered_number
    unless SesProxy::Conf.get[:collect_bounced_mails].eql? false
      mail.to = (recipients&bounced).uniq.join(",")
      mail.cc = (mail.cc_addrs&bounced).uniq.join(",")
      mail.bcc = (mail.bcc_addrs&bounced).uniq.join(",")
      record = BouncedEmail.new({
        :sender => sender,
        :recipients => (recipients&bounced).uniq.join(","),
        :subject => mail.subject,
        :body => mail.decode_body,
        :system => mail['X-Sender-System']||"Unknown",
        :created_at => Time.now,
        :updated_at => Time.now
      })
      record.save!
    end
  end
  if actual_recipients.empty? and SesProxy::Conf.get[:raise_error_if_no_recipients].eql?(true)
    return [550, "Message rejected because of all recipients are bounced!"]
  else
    return true
  end
end

#receive_plain_auth(user, password) ⇒ Object



29
30
31
# File 'lib/ses_proxy/smtp_server.rb', line 29

def receive_plain_auth(user, password)
  @verified = SesProxy::Conf.get[:smtp_auth][:user].eql?(user) and SesProxy::Conf.get[:smtp_auth][:password].eql?(password)
end

#receive_recipient(recipient) ⇒ Object



48
49
50
51
# File 'lib/ses_proxy/smtp_server.rb', line 48

def receive_recipient(recipient)
  recipients << recipient.gsub(/[<>]/,'')
  true
end

#receive_sender(sender) ⇒ Object



33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/ses_proxy/smtp_server.rb', line 33

def receive_sender(sender)
  sender = sender.gsub(/[<>]/,'')
  @sender = sender
  domains = VerifiedSender.where({:type=>'domain'}).map(&:ses_identity)
  email_addresses = VerifiedSender.where({:type=>'email'}).map(&:ses_identity)
  if domains.empty? or email_addresses.empty?
    resp = VerifiedSender.update_identities ses.client
    domains = resp[:domains].map(&:ses_identity)
    email_addresses = resp[:emails].map(&:ses_identity)
  end
  ok = (email_addresses.include?(sender) or domains.include?(sender.split('@').last))
  puts "Invalid sender!" unless ok
  return ok
end

#recipientsObject



58
59
60
# File 'lib/ses_proxy/smtp_server.rb', line 58

def recipients
  @recipients ||= []
end

#senderObject



62
63
64
# File 'lib/ses_proxy/smtp_server.rb', line 62

def sender
  @sender || ""
end

#sesObject



74
75
76
# File 'lib/ses_proxy/smtp_server.rb', line 74

def ses
  @ses ||= AWS::SimpleEmailService.new(SesProxy::Conf.get[:aws])
end

#verifiedObject



70
71
72
# File 'lib/ses_proxy/smtp_server.rb', line 70

def verified
  @verified || false
end