kns_email_endpoint 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +71 -0
- data/Guardfile +10 -0
- data/LICENCE +21 -0
- data/README.md +1 -0
- data/Rakefile +2 -0
- data/kns_email_endpoint.gemspec +39 -0
- data/lib/kns_email_endpoint/configuration.rb +87 -0
- data/lib/kns_email_endpoint/connection.rb +97 -0
- data/lib/kns_email_endpoint/core_extensions/class.rb +44 -0
- data/lib/kns_email_endpoint/core_extensions/hash.rb +14 -0
- data/lib/kns_email_endpoint/core_extensions/imap.rb +36 -0
- data/lib/kns_email_endpoint/core_extensions/message.rb +26 -0
- data/lib/kns_email_endpoint/core_extensions/pop3.rb +0 -0
- data/lib/kns_email_endpoint/core_extensions/test_retriever.rb +42 -0
- data/lib/kns_email_endpoint/email_endpoint.rb +88 -0
- data/lib/kns_email_endpoint/message_state.rb +89 -0
- data/lib/kns_email_endpoint/process_email.rb +123 -0
- data/lib/kns_email_endpoint/storage/abstract_storage.rb +93 -0
- data/lib/kns_email_endpoint/storage/file_storage.rb +74 -0
- data/lib/kns_email_endpoint/storage/memcache_storage.rb +72 -0
- data/lib/kns_email_endpoint/storage/storage.rb +15 -0
- data/lib/kns_email_endpoint/version.rb +3 -0
- data/lib/kns_email_endpoint.rb +19 -0
- data/spec/a18x34/.app +4 -0
- data/spec/a18x34/a18x34.krl +91 -0
- data/spec/lib/kns_email_endpoint/configuration_spec.rb +44 -0
- data/spec/lib/kns_email_endpoint/connection_spec.rb +133 -0
- data/spec/lib/kns_email_endpoint/email_endpoint_spec.rb +97 -0
- data/spec/lib/kns_email_endpoint/message_state_spec.rb +69 -0
- data/spec/lib/kns_email_endpoint/process_email_spec.rb +67 -0
- data/spec/lib/kns_email_endpoint/storage/file_storage_spec.rb +16 -0
- data/spec/lib/kns_email_endpoint/storage/memcache_storage_spec.rb +17 -0
- data/spec/lib/kns_email_endpoint/storage/shared_storage.rb +65 -0
- data/spec/lib/kns_email_endpoint/storage/storage_spec.rb +14 -0
- data/spec/test_config_file.yml +65 -0
- data/spec/test_email.eml +43 -0
- metadata +181 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in kns_email_endpoint.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
|
7
|
+
group :development do
|
8
|
+
|
9
|
+
gem "rspec", ">= 2.0.0"
|
10
|
+
gem "guard-rspec"
|
11
|
+
gem "growl"
|
12
|
+
gem "rb-fsevent"
|
13
|
+
gem "awesome_print"
|
14
|
+
|
15
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
kns_email_endpoint (0.1.0)
|
5
|
+
dalli (~> 1.0.0)
|
6
|
+
kns_endpoint (>= 0.1.9)
|
7
|
+
mail (>= 2.2.6.1)
|
8
|
+
work_queue (~> 1.0.0)
|
9
|
+
|
10
|
+
GEM
|
11
|
+
remote: http://rubygems.org/
|
12
|
+
specs:
|
13
|
+
activesupport (3.0.3)
|
14
|
+
awesome_print (0.3.1)
|
15
|
+
configuration (1.2.0)
|
16
|
+
dalli (1.0.1)
|
17
|
+
diff-lcs (1.1.2)
|
18
|
+
growl (1.0.3)
|
19
|
+
guard (0.2.2)
|
20
|
+
open_gem (~> 1.4.2)
|
21
|
+
thor (~> 0.14.3)
|
22
|
+
guard-rspec (0.1.9)
|
23
|
+
guard (>= 0.2.2)
|
24
|
+
i18n (0.5.0)
|
25
|
+
json (1.4.6)
|
26
|
+
kns_endpoint (0.1.9)
|
27
|
+
json (~> 1.4.0)
|
28
|
+
rest-client (>= 1.6.1)
|
29
|
+
launchy (0.3.7)
|
30
|
+
configuration (>= 0.0.5)
|
31
|
+
rake (>= 0.8.1)
|
32
|
+
mail (2.2.14)
|
33
|
+
activesupport (>= 2.3.6)
|
34
|
+
i18n (>= 0.4.0)
|
35
|
+
mime-types (~> 1.16)
|
36
|
+
treetop (~> 1.4.8)
|
37
|
+
mime-types (1.16)
|
38
|
+
open_gem (1.4.2)
|
39
|
+
launchy (~> 0.3.5)
|
40
|
+
polyglot (0.3.1)
|
41
|
+
rake (0.8.7)
|
42
|
+
rb-fsevent (0.3.9)
|
43
|
+
rest-client (1.6.1)
|
44
|
+
mime-types (>= 1.16)
|
45
|
+
rspec (2.4.0)
|
46
|
+
rspec-core (~> 2.4.0)
|
47
|
+
rspec-expectations (~> 2.4.0)
|
48
|
+
rspec-mocks (~> 2.4.0)
|
49
|
+
rspec-core (2.4.0)
|
50
|
+
rspec-expectations (2.4.0)
|
51
|
+
diff-lcs (~> 1.1.2)
|
52
|
+
rspec-mocks (2.4.0)
|
53
|
+
thor (0.14.6)
|
54
|
+
treetop (1.4.9)
|
55
|
+
polyglot (>= 0.3.1)
|
56
|
+
work_queue (1.0.0)
|
57
|
+
|
58
|
+
PLATFORMS
|
59
|
+
ruby
|
60
|
+
|
61
|
+
DEPENDENCIES
|
62
|
+
awesome_print
|
63
|
+
dalli (~> 1.0.0)
|
64
|
+
growl
|
65
|
+
guard-rspec
|
66
|
+
kns_email_endpoint!
|
67
|
+
kns_endpoint (>= 0.1.9)
|
68
|
+
mail (>= 2.2.6.1)
|
69
|
+
rb-fsevent
|
70
|
+
rspec (>= 2.0.0)
|
71
|
+
work_queue (~> 1.0.0)
|
data/Guardfile
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at http://github.com/guard/guard#readme
|
3
|
+
#
|
4
|
+
|
5
|
+
guard 'rspec' do
|
6
|
+
watch('^spec/(.*)_spec.rb')
|
7
|
+
watch('^lib/(.*)\.rb') { |m| "spec/lib/#{m[1]}_spec.rb" }
|
8
|
+
watch('^lib/kns_email_endpoint/(.*)\.rb') { |m| "spec/kns_email_endpoint/lib/#{m[1]}_spec.rb" }
|
9
|
+
end
|
10
|
+
|
data/LICENCE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2010 Kynetx Inc.
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Coming Soon!
|
data/Rakefile
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "kns_email_endpoint/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "kns_email_endpoint"
|
7
|
+
s.version = KnsEmailEndpoint::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Michael Farmer"]
|
10
|
+
s.email = ["mjf@kynetx.com"]
|
11
|
+
s.homepage = "http://code.kynetx.com"
|
12
|
+
s.summary = %q{The Kynetx Email Endpoint is a ruby gem that makes setting up an email endpoint very simple.}
|
13
|
+
s.description = %q{The Kynetx Email Endpoint is a ruby gem that makes setting up an email endpoint very simple.}
|
14
|
+
|
15
|
+
s.rubyforge_project = "kns_email_endpoint"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_dependency "kns_endpoint", ">= 0.1.9"
|
23
|
+
s.add_dependency "mail", ">= 2.2.6.1"
|
24
|
+
s.add_dependency "dalli", "~> 1.0.0"
|
25
|
+
s.add_dependency "work_queue", "~> 1.0.0"
|
26
|
+
|
27
|
+
#activesupport (3.0.0)
|
28
|
+
#daemons (1.1.0)
|
29
|
+
#fastthread (1.0.7)
|
30
|
+
#i18n (0.4.1)
|
31
|
+
#json (1.4.6)
|
32
|
+
#mail (2.2.6.1)
|
33
|
+
#mime-types (1.16)
|
34
|
+
#polyglot (0.3.1)
|
35
|
+
#sqlite3-ruby (1.3.1)
|
36
|
+
#tlsmail (0.0.1)
|
37
|
+
#treetop (1.4.8)
|
38
|
+
#work_queue (1.0.0)
|
39
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'logger'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
module KNSEmailEndpoint
|
6
|
+
|
7
|
+
|
8
|
+
class Configuration
|
9
|
+
cattr_writer :logdir, :work_threads, :poll_delay, :connections
|
10
|
+
cattr_reader :storage_engine
|
11
|
+
|
12
|
+
class << self
|
13
|
+
|
14
|
+
def load_from_file(yaml_file)
|
15
|
+
conf = YAML.load_file(yaml_file)
|
16
|
+
@@logdir = conf["logdir"] if conf["logdir"]
|
17
|
+
@@work_threads = conf["workthreads"] if conf["workthreads"]
|
18
|
+
@@poll_delay = conf["polldelayinseconds"] if conf["polldelayinseconds"]
|
19
|
+
@@log_level = conf["logginglevel"] if conf["logginglevel"]
|
20
|
+
@@connections = conf["connections"] if conf["connections"]
|
21
|
+
self.storage = conf["storage"] || {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_h
|
25
|
+
{
|
26
|
+
:logdir => logdir,
|
27
|
+
:work_threads => work_threads,
|
28
|
+
:poll_delay => poll_delay,
|
29
|
+
:storage => storage,
|
30
|
+
:connections => connections
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
def log
|
35
|
+
return @@logger if defined?(@@logger) && ! @logger.nil?
|
36
|
+
if @@logdir && ! @@logdir.empty?
|
37
|
+
FileUtils.mkdir_p @@logdir unless @@logdir == "" || @@logdir.nil?
|
38
|
+
log_dest = File.join(@@logdir, 'email_endpoint.log')
|
39
|
+
else
|
40
|
+
log_dest = STDOUT
|
41
|
+
end
|
42
|
+
@@logger = Logger.new(log_dest, "daily")
|
43
|
+
@@logger.level = eval("Logger::#{@@log_level.upcase}") rescue Logger::DEBUG
|
44
|
+
return @@logger
|
45
|
+
end
|
46
|
+
|
47
|
+
def log_level=(l)
|
48
|
+
@@log_level = l
|
49
|
+
@@log = nil
|
50
|
+
return @@log_level
|
51
|
+
end
|
52
|
+
|
53
|
+
def storage=(opts)
|
54
|
+
@@storage = opts
|
55
|
+
engine = opts.delete("engine")
|
56
|
+
@@storage_engine = MessageState.set_storage(engine, opts)
|
57
|
+
return @@storage
|
58
|
+
end
|
59
|
+
|
60
|
+
# defaults
|
61
|
+
def work_threads; @@work_threads ||= 10 end
|
62
|
+
def poll_delay; @@poll_delay ||= 30 end
|
63
|
+
def logdir; @@logdir ||= "" end
|
64
|
+
def connections; @@connections ||= [] end
|
65
|
+
def storage; @@storage ||= {} end
|
66
|
+
|
67
|
+
# Connection Handling
|
68
|
+
|
69
|
+
def [](name)
|
70
|
+
@@connections.each do |conn|
|
71
|
+
return conn if conn["name"] == name
|
72
|
+
end
|
73
|
+
raise "Invalid connection (#{name})"
|
74
|
+
end
|
75
|
+
|
76
|
+
def each_connection
|
77
|
+
@@connections.each do |conn|
|
78
|
+
yield Connection.new conn["name"]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'net/smtp'
|
2
|
+
require 'mail'
|
3
|
+
|
4
|
+
module KNSEmailEndpoint
|
5
|
+
class ConnectionException < StandardError
|
6
|
+
end
|
7
|
+
|
8
|
+
|
9
|
+
module ConnectionFactory
|
10
|
+
attr_reader :conn_log, :retriever, :sender, :name, :process_mode, :event_args, :appid, :environment, :max_retry_count
|
11
|
+
|
12
|
+
def setup_mail_connections(name)
|
13
|
+
@config = Configuration
|
14
|
+
@conn_config = @config[name]
|
15
|
+
|
16
|
+
if @config.logdir.empty?
|
17
|
+
log_dest = STDOUT
|
18
|
+
else
|
19
|
+
log_dest = "#{@config.logdir}/#{@conn_config['logfile']}"
|
20
|
+
end
|
21
|
+
@config.log.info "Initializing connection log to: #{log_dest}"
|
22
|
+
@conn_log = Logger.new(log_dest, "daily")
|
23
|
+
@conn_log.level = @config.log.level
|
24
|
+
@name = @conn_config["name"]
|
25
|
+
@process_mode = @conn_config["processmode"].to_sym
|
26
|
+
@max_retry_count = @conn_config["max_retry_count"] || 10
|
27
|
+
@event_args = @conn_config["args"] || {}
|
28
|
+
@appid = @conn_config["appid"]
|
29
|
+
if @conn_config["appversion"] &&
|
30
|
+
(@conn_config["appversion"] == "dev" || @conn_config["appversion"] == "development")
|
31
|
+
@environment = :development
|
32
|
+
else
|
33
|
+
@environment = :production
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
@in_method = @conn_config["incoming"]["method"]
|
38
|
+
@out_method = @conn_config["smtp"]["method"]
|
39
|
+
|
40
|
+
@retriever_settings = {
|
41
|
+
:address => @conn_config["incoming"]["host"],
|
42
|
+
:user_name => @conn_config["incoming"]["username"],
|
43
|
+
:password => @conn_config["incoming"]["password"],
|
44
|
+
:enable_ssl => @conn_config["incoming"]["ssl"],
|
45
|
+
:port => @conn_config["incoming"]["port"],
|
46
|
+
:authentication => @conn_config["incoming"]["authentication"] || nil
|
47
|
+
}
|
48
|
+
|
49
|
+
@mailbox = @conn_config["incoming"]["mailbox"]
|
50
|
+
@retriever = lookup_retriever
|
51
|
+
|
52
|
+
@sender_settings = {
|
53
|
+
:address => @conn_config["smtp"]["host"],
|
54
|
+
:user_name => @conn_config["smtp"]["username"],
|
55
|
+
:password => @conn_config["smtp"]["password"],
|
56
|
+
:port => @conn_config["smtp"]["port"],
|
57
|
+
:domain => @conn_config["smtp"]["helo_domain"],
|
58
|
+
:enable_starttls_auto => @conn_config["smtp"]["tls"]
|
59
|
+
}
|
60
|
+
if @conn_config["smtp"]["authentication"]
|
61
|
+
@sender_settings[:authentication] = @conn_config["smtp"]["authentication"].downcase
|
62
|
+
end
|
63
|
+
|
64
|
+
@sender = lookup_sender
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def lookup_retriever
|
71
|
+
case @in_method
|
72
|
+
when "imap" then return Mail::IMAP.new(@retriever_settings)
|
73
|
+
when "pop3" then return Mail::POP3.new(@retriever_settings)
|
74
|
+
when "test" then return Mail::TestRetriever.new(@retriever_settings)
|
75
|
+
else return Mail::IMAP.new(@retriever_settings)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def lookup_sender
|
80
|
+
case @out_method
|
81
|
+
when "smtp" then return Mail::SMTP.new(@sender_settings)
|
82
|
+
when "test" then return Mail::TestMailer.new(@sender_settings)
|
83
|
+
else return Mail::SMTP.new(@sender_settings)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
class Connection
|
90
|
+
include ConnectionFactory
|
91
|
+
def initialize(name)
|
92
|
+
setup_mail_connections name
|
93
|
+
conn_log.info "Setup Connection for '#{name}'"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# Mail::TestRetriever uses cattr_accessor from rails. Since I don't want to include rails as a dependency
|
2
|
+
# I'm just adding the Rails code here.
|
3
|
+
# (Note, this is older Rails code slightly modified, but it should still work for what I need it for)
|
4
|
+
class Class
|
5
|
+
def cattr_accessor(*syms, &blk)
|
6
|
+
cattr_reader(*syms)
|
7
|
+
cattr_writer(*syms, &blk)
|
8
|
+
end
|
9
|
+
|
10
|
+
def cattr_reader(*syms)
|
11
|
+
syms.flatten.each do |sym|
|
12
|
+
next if sym.is_a?(Hash)
|
13
|
+
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
14
|
+
unless defined? @@#{sym}
|
15
|
+
@@#{sym} = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.#{sym}
|
19
|
+
@@#{sym}
|
20
|
+
end
|
21
|
+
EOS
|
22
|
+
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def cattr_writer(*syms)
|
28
|
+
syms.flatten.each do |sym|
|
29
|
+
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
30
|
+
unless defined? @@#{sym}
|
31
|
+
@@#{sym} = nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.#{sym}=(obj)
|
35
|
+
@@#{sym} = obj
|
36
|
+
end
|
37
|
+
EOS
|
38
|
+
|
39
|
+
self.send("#{sym}=", yield) if block_given?
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Adding methods to imap for easier access to things we do commonly
|
2
|
+
require 'mail'
|
3
|
+
|
4
|
+
module Mail
|
5
|
+
|
6
|
+
class IMAP
|
7
|
+
|
8
|
+
# add a delete message
|
9
|
+
# so that we can delete without calling find again
|
10
|
+
|
11
|
+
def delete_messages(mailbox, messages)
|
12
|
+
mailbox = Net::IMAP.encode_utf7(mailbox)
|
13
|
+
messages.each do |message_hash|
|
14
|
+
imap = message_hash[:connection]
|
15
|
+
message_id = message_hash[:message_id]
|
16
|
+
message = message_hash[:message]
|
17
|
+
imap.uid_store(message_id, "+FLAGS", [Net::IMAP::DELETED]) if message.is_marked_for_delete?
|
18
|
+
end
|
19
|
+
start do |imap|
|
20
|
+
begin
|
21
|
+
imap.select(mailbox)
|
22
|
+
imap.expunge
|
23
|
+
rescue => e
|
24
|
+
puts e.message
|
25
|
+
puts e.backtrace.join("\n")
|
26
|
+
raise e
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Mail
|
2
|
+
class Message
|
3
|
+
def to_h
|
4
|
+
{
|
5
|
+
:to => to,
|
6
|
+
:from => from,
|
7
|
+
:subject => subject,
|
8
|
+
:body => body.raw_source
|
9
|
+
}
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
def delivered_to
|
14
|
+
# Messages can be sent to multiple people AND if it was forwarded will have multiple
|
15
|
+
# Delivered-To headers. What this bit of code does is take the
|
16
|
+
# intersection of the "to" recipients and the "Delivered-To" recipients to find
|
17
|
+
# out who the message was actually delivered to. There will almost never
|
18
|
+
# be more than one recipient in that intersection, but if there is, we'll just
|
19
|
+
# return the first.
|
20
|
+
delivered_to_headers = []
|
21
|
+
self.header_fields.each { |f| delivered_to_headers << f.value if f.name == "Delivered-To"}
|
22
|
+
actual_to = self.to.to_set.intersection delivered_to_headers
|
23
|
+
return actual_to.first
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
File without changes
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# Mail::TestRetriever additional methods
|
2
|
+
require 'mail'
|
3
|
+
module Mail
|
4
|
+
class TestRetriever
|
5
|
+
def delete_all
|
6
|
+
@@emails = []
|
7
|
+
end
|
8
|
+
|
9
|
+
remove_method :find
|
10
|
+
|
11
|
+
# Fix a bug in the find method when only returning a single email
|
12
|
+
def find(options = {}, &block)
|
13
|
+
options[:count] ||= :all
|
14
|
+
options[:order] ||= :asc
|
15
|
+
options[:what] ||= :first
|
16
|
+
emails = @@emails.dup
|
17
|
+
emails.reverse! if options[:what] == :last
|
18
|
+
emails = case count = options[:count]
|
19
|
+
when :all then emails
|
20
|
+
# when 1 then emails.first
|
21
|
+
when Fixnum then emails[0, count]
|
22
|
+
else
|
23
|
+
raise 'Invalid count option value: ' + count.inspect
|
24
|
+
end
|
25
|
+
if options[:what] == :last && options[:order] == :asc || options[:what] == :first && options[:order] == :desc
|
26
|
+
emails.reverse!
|
27
|
+
end
|
28
|
+
emails.each { |email| email.mark_for_delete = true } if options[:delete_after_find]
|
29
|
+
|
30
|
+
if block_given?
|
31
|
+
emails.each { |email| yield email }
|
32
|
+
else
|
33
|
+
emails
|
34
|
+
end.tap do |results|
|
35
|
+
emails.each { |email| @@emails.delete(email) if email.is_marked_for_delete? } if options[:delete_after_find]
|
36
|
+
end
|
37
|
+
return emails.size == 1 && options[:count] == 1 ? emails.first : emails
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'mail'
|
2
|
+
|
3
|
+
module KNSEmailEndpoint
|
4
|
+
|
5
|
+
class EmailEndpoint < Kynetx::Endpoint
|
6
|
+
attr_reader :conn, :message_state
|
7
|
+
|
8
|
+
domain :mail
|
9
|
+
event :received do |p|
|
10
|
+
raise "Missing 'msg' parameter'." unless p[:msg]
|
11
|
+
message = p[:msg]
|
12
|
+
p[:to] = message.to.join(",")
|
13
|
+
p[:from] = message.from.join(",")
|
14
|
+
p[:subject] = message.subject
|
15
|
+
|
16
|
+
p[:unique_id] = message.message_id unless p[:unique_id]
|
17
|
+
|
18
|
+
if p[:extract_label]
|
19
|
+
label = message.delivered_to.match(/\+(.*)@/)[1]
|
20
|
+
p[:label] = label.to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
def before_received
|
26
|
+
@message_state = MessageState.new(@conn, params[:msg])
|
27
|
+
@message_state.state = :processing
|
28
|
+
end
|
29
|
+
|
30
|
+
directive :delete
|
31
|
+
def after_delete(d)
|
32
|
+
@message_state.delete
|
33
|
+
end
|
34
|
+
|
35
|
+
directive :reply
|
36
|
+
def after_reply(d)
|
37
|
+
msg = params[:msg]
|
38
|
+
r_msg = Mail.new(msg)
|
39
|
+
r_msg.to = msg.from
|
40
|
+
r_msg.from = msg.reply_to.nil? ? msg.to : msg.reply_to
|
41
|
+
r_msg.subject = d["subject"] ? d["subject"] : "Re: " + msg.subject.to_s
|
42
|
+
r_msg.body d["body"] ? d["body"] + msg[:body].to_s : msg.body.to_s
|
43
|
+
@sender.deliver!(r_msg) if @sender
|
44
|
+
@message_state.state = :replied
|
45
|
+
|
46
|
+
if d["delete_message"]
|
47
|
+
@message_state.delete
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
directive :forward
|
52
|
+
def after_forward(d)
|
53
|
+
if d["to"] && @sender
|
54
|
+
msg = params[:msg]
|
55
|
+
f_msg = Mail.new do
|
56
|
+
to d["to"]
|
57
|
+
from msg.to
|
58
|
+
subject d["subject"] ? d["subject"] : "Fwd: " + msg.subject.to_s
|
59
|
+
body d["body"] ? d["body"] + msg[:body].to_s : msg.body.to_s
|
60
|
+
end
|
61
|
+
@sender.deliver!(f_msg)
|
62
|
+
@message_state.state = :forwarded
|
63
|
+
else
|
64
|
+
@message_state.state = :error
|
65
|
+
end
|
66
|
+
|
67
|
+
if d["delete_message"] && status == :forwarded
|
68
|
+
@message_state.delete
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
directive :processed
|
73
|
+
def after_processed(d)
|
74
|
+
@message_state.state = :processed
|
75
|
+
end
|
76
|
+
|
77
|
+
def initialize(conn, opts={}, sender=nil)
|
78
|
+
@conn = conn
|
79
|
+
@sender = sender
|
80
|
+
super(opts)
|
81
|
+
end
|
82
|
+
|
83
|
+
def status
|
84
|
+
@message_state.state
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|