kns_email_endpoint 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
module KNSEmailEndpoint
|
3
|
+
class MessageState
|
4
|
+
|
5
|
+
class << self
|
6
|
+
attr_accessor :storage
|
7
|
+
|
8
|
+
def set_storage(storage, opts)
|
9
|
+
@storage = Storage.get_storage(storage, opts)
|
10
|
+
return @storage
|
11
|
+
end
|
12
|
+
|
13
|
+
def gen_unique_id(conn,msg)
|
14
|
+
raise "Message must have a valid message_id" unless msg.message_id.to_s != ""
|
15
|
+
Digest::SHA1.hexdigest "#{conn}::#{msg.message_id}::K-KEY"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
attr_reader :message, :message_id, :retry_count, :unique_id, :state
|
21
|
+
|
22
|
+
def initialize(conn_name, message)
|
23
|
+
# setup the state
|
24
|
+
@conn_name = conn_name
|
25
|
+
@message = message
|
26
|
+
@message_id = get_message_id
|
27
|
+
@unique_id = get_unique_id
|
28
|
+
@storage = self.class.storage
|
29
|
+
raise "Unknown Storage" unless @storage
|
30
|
+
|
31
|
+
# get from storage
|
32
|
+
@storage.find_or_create(:unique_id => @unique_id, :message_id => @message_id)
|
33
|
+
@state = @storage.state
|
34
|
+
@retry_count = @storage.retry_count
|
35
|
+
|
36
|
+
# Do not auto delete email.
|
37
|
+
@message.mark_for_delete = false
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
def state=(s)
|
42
|
+
@state = s
|
43
|
+
@storage.state = @state if @storage.unique_id
|
44
|
+
@message.mark_for_delete = @state == :deleted
|
45
|
+
return @state
|
46
|
+
end
|
47
|
+
|
48
|
+
def retry
|
49
|
+
@retry_count += 1
|
50
|
+
@storage.retry_count = @retry_count
|
51
|
+
return @retry_count
|
52
|
+
end
|
53
|
+
|
54
|
+
def reset
|
55
|
+
@retry_count = 0
|
56
|
+
@storage.retry_count = @retry_count
|
57
|
+
return @retry_count
|
58
|
+
end
|
59
|
+
|
60
|
+
def reset_state
|
61
|
+
@storage.delete
|
62
|
+
@storage.create(:unique_id => @unique_id, :message_id => @message_id)
|
63
|
+
@retry_count = @storage.retry_count
|
64
|
+
@state = @storage.state
|
65
|
+
end
|
66
|
+
|
67
|
+
def delete
|
68
|
+
@storage.delete
|
69
|
+
self.state = :deleted
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def get_message_id
|
76
|
+
return @message_id if @message_id
|
77
|
+
if @message.has_message_id?
|
78
|
+
@message_id = @message.message_id
|
79
|
+
else
|
80
|
+
raise "The mail message does not have a valid message_id."
|
81
|
+
end
|
82
|
+
return @message_id
|
83
|
+
end
|
84
|
+
|
85
|
+
def get_unique_id
|
86
|
+
return @unique_id ||= self.class.gen_unique_id(@conn_name, @message)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'work_queue'
|
2
|
+
module KNSEmailEndpoint
|
3
|
+
|
4
|
+
class ProcessEmail
|
5
|
+
# if in repeat mode, these states will be processed
|
6
|
+
$REPEATABLE_STATES = [:forwarded, :replied, :unprocessed, :error]
|
7
|
+
# if in single mode, these states will be processed
|
8
|
+
$SINGLE_STATES = [:unprocessed, :error]
|
9
|
+
|
10
|
+
class << self
|
11
|
+
|
12
|
+
def go(conn)
|
13
|
+
raise "Need config" unless Configuration[conn.name]
|
14
|
+
log = conn.conn_log
|
15
|
+
3.times { log.debug "" }
|
16
|
+
log.info "Processing Email from connection: #{conn.name}"
|
17
|
+
endpoint_opts = {
|
18
|
+
:ruleset => conn.appid,
|
19
|
+
:environment => conn.environment,
|
20
|
+
:use_session => true,
|
21
|
+
:logging => log.debug?
|
22
|
+
}
|
23
|
+
|
24
|
+
begin
|
25
|
+
queue = WorkQueue.new(Configuration.work_threads)
|
26
|
+
email_processed_count = 0
|
27
|
+
email_errors_count = 0
|
28
|
+
conn.retriever.find({
|
29
|
+
:count => :all,
|
30
|
+
:delete_after_find => true,
|
31
|
+
:what => :first,
|
32
|
+
:order => :asc
|
33
|
+
}) do |msg|
|
34
|
+
# worker enqueue
|
35
|
+
queue.enqueue_b {
|
36
|
+
begin
|
37
|
+
msg_state = MessageState.new(conn.name, msg)
|
38
|
+
log.debug "Processing Message #{msg_state.unique_id}"
|
39
|
+
log.debug "STATE: #{msg_state.state}"
|
40
|
+
if (conn.process_mode == :single && $SINGLE_STATES.include?(msg_state.state)) ||
|
41
|
+
(conn.process_mode == :repeat && $REPEATABLE_STATES.include?(msg_state.state))
|
42
|
+
|
43
|
+
ee = EmailEndpoint.new(conn.name, endpoint_opts, conn.sender)
|
44
|
+
event_args = {
|
45
|
+
:msg => msg,
|
46
|
+
:unique_id => msg_state.unique_id
|
47
|
+
}.merge! conn.event_args
|
48
|
+
|
49
|
+
log.debug "Raising Event\n #{event_args.inspect}"
|
50
|
+
result = ee.received(event_args)
|
51
|
+
if log.debug?
|
52
|
+
log.debug "--- Endpoint Log ---"
|
53
|
+
log.debug ee.log.join("\n")
|
54
|
+
log.debug "--------------------"
|
55
|
+
end
|
56
|
+
if ee.message_state.state == :processing
|
57
|
+
# there was no directive returned or endpoint failed.
|
58
|
+
log.debug "UNEXPECTED DIRECTIVE RECEIVED: \n#{result.inspect}"
|
59
|
+
raise "No directive matched message (#{msg.message_id})"
|
60
|
+
|
61
|
+
end
|
62
|
+
log.debug "NEW STATE: " + ee.message_state.state.to_s
|
63
|
+
log.debug "Delete message? #{msg.is_marked_for_delete?}"
|
64
|
+
else
|
65
|
+
log.debug "Skipping #{msg.message_id} (#{msg_state.state})"
|
66
|
+
log.debug "Delete message? #{msg.is_marked_for_delete?}"
|
67
|
+
end
|
68
|
+
email_processed_count += 1
|
69
|
+
|
70
|
+
rescue => e
|
71
|
+
rc = msg_state.retry
|
72
|
+
if rc >= (conn.max_retry_count - 1)
|
73
|
+
msg_state.state = :failed
|
74
|
+
else
|
75
|
+
msg_state.state = :error
|
76
|
+
end
|
77
|
+
log.error e.message
|
78
|
+
log.error "RETRY COUNT: #{rc}"
|
79
|
+
log.error "NEW STATE: #{msg_state.state}"
|
80
|
+
log.error "Delete message? #{msg.is_marked_for_delete?}"
|
81
|
+
email_errors_count += 1
|
82
|
+
end
|
83
|
+
|
84
|
+
}
|
85
|
+
queue.join
|
86
|
+
end
|
87
|
+
log.info "Number of email successfully processed for connection #{conn.name}: #{email_processed_count}"
|
88
|
+
log.info "Number of email unsuccesfully processed for connection #{conn.name}: #{email_errors_count}"
|
89
|
+
rescue => e
|
90
|
+
log.error "There was an error processing email for #{conn.name}: #{e.message}"
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
def go_async
|
97
|
+
threads = []
|
98
|
+
begin
|
99
|
+
Configuration.each_connection do |conn|
|
100
|
+
threads << Thread.new { go conn }
|
101
|
+
end
|
102
|
+
threads.each { |t| t.join }
|
103
|
+
rescue => e
|
104
|
+
Configuration.log.error e.message
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def go_all
|
109
|
+
begin
|
110
|
+
Configuration.each_connection { |conn| go conn }
|
111
|
+
rescue => e
|
112
|
+
Configuration.log.error e.message
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def flush
|
117
|
+
# flush all message states
|
118
|
+
Configuration.storage_engine.delete_all
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module KNSEmailEndpoint
|
2
|
+
module Storage
|
3
|
+
class AbstractStorage
|
4
|
+
attr_reader :message_id, :unique_id, :retry_count, :state
|
5
|
+
|
6
|
+
def initialize(*args)
|
7
|
+
|
8
|
+
reset_storage
|
9
|
+
end
|
10
|
+
|
11
|
+
# delete should return either true if successful, or false if unsuccessful
|
12
|
+
# delete should also call reset_storage if successful
|
13
|
+
def delete
|
14
|
+
return false # override me
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
# create should call set_vars and save! if successful and return true
|
19
|
+
# otherwise, it should raise an exception
|
20
|
+
def create(opts={})
|
21
|
+
return false #override me
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
# find should return self if a record is found after calling set_vars
|
26
|
+
# it should return nil if not found and call reset_storage
|
27
|
+
def find(unique_id)
|
28
|
+
return nil #override me
|
29
|
+
end
|
30
|
+
|
31
|
+
def retry_count=(r)
|
32
|
+
raise "Unknown unique_id" unless @unique_id
|
33
|
+
@retry_count = r
|
34
|
+
save!
|
35
|
+
end
|
36
|
+
|
37
|
+
def state=(s)
|
38
|
+
raise "Unknown unique_id" unless @unique_id
|
39
|
+
@state = s
|
40
|
+
save!
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_h
|
44
|
+
return {} unless @unique_id
|
45
|
+
return {
|
46
|
+
:unique_id => @unique_id,
|
47
|
+
:message_id => @message_id,
|
48
|
+
:retry_count => @retry_count,
|
49
|
+
:state => @state
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
def find_or_create(opts={})
|
54
|
+
unless opts[:unique_id] && opts[:message_id]
|
55
|
+
raise "Must provide at least a unique_id and message_id"
|
56
|
+
end
|
57
|
+
|
58
|
+
s = find(opts[:unique_id])
|
59
|
+
return s if s
|
60
|
+
|
61
|
+
return create(opts) ? self : nil
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def save!
|
67
|
+
# override me
|
68
|
+
end
|
69
|
+
|
70
|
+
def set_vars(h)
|
71
|
+
h.symbolize_keys!
|
72
|
+
raise "Invalid unique_id" unless h[:unique_id]
|
73
|
+
raise "Invalid message_id" unless h[:message_id]
|
74
|
+
raise "Invalid retry_count" unless h[:retry_count]
|
75
|
+
raise "Invalid state" unless h[:state]
|
76
|
+
@unique_id = h[:unique_id]
|
77
|
+
@message_id = h[:message_id]
|
78
|
+
@retry_count = h[:retry_count]
|
79
|
+
@state = h[:state].to_sym
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
def reset_storage
|
84
|
+
@unique_id = nil
|
85
|
+
@message_id = nil
|
86
|
+
@retry_count = nil
|
87
|
+
@state = nil
|
88
|
+
@current_file = nil
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'fileutils'
|
3
|
+
module KNSEmailEndpoint
|
4
|
+
module Storage
|
5
|
+
class FileStorage < AbstractStorage
|
6
|
+
attr_reader :name
|
7
|
+
|
8
|
+
def initialize(settings={})
|
9
|
+
@dir = settings[:file_location]
|
10
|
+
raise "Unknown file_location" unless @dir
|
11
|
+
FileUtils.mkdir_p @dir
|
12
|
+
|
13
|
+
super(settings)
|
14
|
+
@name = "file"
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
def delete
|
19
|
+
return false unless @current_file
|
20
|
+
FileUtils.rm @current_file if File.exists? @current_file
|
21
|
+
reset_storage
|
22
|
+
return true
|
23
|
+
end
|
24
|
+
|
25
|
+
def create(opts={})
|
26
|
+
options = {
|
27
|
+
:state => :unprocessed,
|
28
|
+
:retry_count => 0
|
29
|
+
}.merge! opts
|
30
|
+
raise ":unique_id is required" unless options[:unique_id]
|
31
|
+
raise ":message_id is required" unless options[:message_id]
|
32
|
+
|
33
|
+
@current_file = File.join(@dir, options[:unique_id])
|
34
|
+
|
35
|
+
if File.exists? @current_file
|
36
|
+
raise "unique_id #{options[:unique_id]} already exists"
|
37
|
+
end
|
38
|
+
|
39
|
+
set_vars(options)
|
40
|
+
save!
|
41
|
+
|
42
|
+
return true
|
43
|
+
end
|
44
|
+
|
45
|
+
def find(unique_id)
|
46
|
+
lookup_file = File.join(@dir, unique_id)
|
47
|
+
if File.exists? lookup_file
|
48
|
+
@current_file = lookup_file
|
49
|
+
set_vars JSON.parse(File.open(@current_file, 'r').read)
|
50
|
+
return self
|
51
|
+
else
|
52
|
+
reset_storage
|
53
|
+
return nil
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def delete_all
|
58
|
+
FileUtils.remove_dir(@dir, true)
|
59
|
+
FileUtils.mkdir_p @dir
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def save!
|
66
|
+
File.open(@current_file, "w") do |f|
|
67
|
+
f.write to_h.to_json
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'dalli'
|
2
|
+
module KNSEmailEndpoint
|
3
|
+
module Storage
|
4
|
+
class MemcacheStorage < AbstractStorage
|
5
|
+
class << self
|
6
|
+
attr_accessor :client
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(settings)
|
10
|
+
options = {
|
11
|
+
:host => "localhost",
|
12
|
+
:port => 11211,
|
13
|
+
:ttl => nil
|
14
|
+
}.merge!(settings)
|
15
|
+
|
16
|
+
|
17
|
+
# This simple bit of magic allows usage of
|
18
|
+
# a class connection rather than an instance connection
|
19
|
+
# so we never have more than one active connection to memcache
|
20
|
+
self.class.client ||= Dalli::Client.new("#{options[:host]}:#{options[:port]}")
|
21
|
+
@client = self.class.client
|
22
|
+
@ttl = options[:ttl]
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
def create(opts={})
|
27
|
+
options = {
|
28
|
+
:state => :unprocessed,
|
29
|
+
:retry_count => 0
|
30
|
+
}.merge! opts
|
31
|
+
raise ":unique_id is required" unless options[:unique_id]
|
32
|
+
raise ":message_id is required" unless options[:message_id]
|
33
|
+
|
34
|
+
set_vars(options)
|
35
|
+
save!
|
36
|
+
|
37
|
+
return true
|
38
|
+
end
|
39
|
+
|
40
|
+
def find(unique_id)
|
41
|
+
s = @client.get(unique_id)
|
42
|
+
if s
|
43
|
+
set_vars(s)
|
44
|
+
return self
|
45
|
+
else
|
46
|
+
reset_storage
|
47
|
+
return nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def delete
|
52
|
+
return false unless @unique_id
|
53
|
+
@client.delete @unique_id
|
54
|
+
reset_storage
|
55
|
+
return true
|
56
|
+
end
|
57
|
+
|
58
|
+
def delete_all
|
59
|
+
@client.flush
|
60
|
+
reset_storage
|
61
|
+
return true
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def save!
|
67
|
+
@client.set(@unique_id, to_h, @ttl)
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'kns_email_endpoint/storage/abstract_storage'
|
2
|
+
module KNSEmailEndpoint
|
3
|
+
module Storage
|
4
|
+
autoload :FileStorage, 'kns_email_endpoint/storage/file_storage'
|
5
|
+
autoload :MemcacheStorage, 'kns_email_endpoint/storage/memcache_storage'
|
6
|
+
|
7
|
+
def self.get_storage(engine, settings)
|
8
|
+
case engine.to_sym
|
9
|
+
when :file then return FileStorage.new(settings)
|
10
|
+
when :memcache then return MemcacheStorage.new(settings)
|
11
|
+
else return nil
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
# require some extensions
|
4
|
+
require 'kns_email_endpoint/core_extensions/class'
|
5
|
+
require 'kns_email_endpoint/core_extensions/hash'
|
6
|
+
require 'kns_email_endpoint/core_extensions/imap'
|
7
|
+
require 'kns_email_endpoint/core_extensions/message'
|
8
|
+
require 'kns_email_endpoint/core_extensions/test_retriever'
|
9
|
+
|
10
|
+
# all the gem pieces
|
11
|
+
require 'kns_endpoint'
|
12
|
+
require 'kns_email_endpoint/connection'
|
13
|
+
require 'kns_email_endpoint/configuration'
|
14
|
+
require 'kns_email_endpoint/email_endpoint'
|
15
|
+
require 'kns_email_endpoint/storage/storage'
|
16
|
+
require 'kns_email_endpoint/message_state'
|
17
|
+
require 'kns_email_endpoint/process_email'
|
18
|
+
|
19
|
+
|
data/spec/a18x34/.app
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
ruleset a18x34 {
|
2
|
+
meta {
|
3
|
+
name "Test App for Email Endpoint"
|
4
|
+
description <<
|
5
|
+
Testing application for the Email Endpoint
|
6
|
+
>>
|
7
|
+
author "Michael Farmer"
|
8
|
+
// Uncomment this line to require Marketplace purchase to use this app.
|
9
|
+
// authz require user
|
10
|
+
logging on
|
11
|
+
}
|
12
|
+
|
13
|
+
|
14
|
+
global {
|
15
|
+
|
16
|
+
}
|
17
|
+
|
18
|
+
rule receive_new_email is active {
|
19
|
+
select when mail received test_rule "parts"
|
20
|
+
pre {
|
21
|
+
envelope = event:param("msg");
|
22
|
+
from = event:param("from");
|
23
|
+
to = event:param("to");
|
24
|
+
subject = event:param("subject");
|
25
|
+
label = event:param("label");
|
26
|
+
unique_id = event:param("unique_id");
|
27
|
+
collection = {
|
28
|
+
"from": from,
|
29
|
+
"to": to,
|
30
|
+
"subject": subject,
|
31
|
+
"label": label,
|
32
|
+
"unique_id": unique_id
|
33
|
+
}
|
34
|
+
}
|
35
|
+
{
|
36
|
+
send_directive("processed");
|
37
|
+
}
|
38
|
+
|
39
|
+
fired {
|
40
|
+
log collection.encode();
|
41
|
+
}
|
42
|
+
|
43
|
+
}
|
44
|
+
|
45
|
+
rule delete_mail is active {
|
46
|
+
select when mail received test_rule "delete_me"
|
47
|
+
{
|
48
|
+
email:delete();
|
49
|
+
}
|
50
|
+
|
51
|
+
}
|
52
|
+
|
53
|
+
rule reply_mail is active {
|
54
|
+
select when mail received test_rule "reply"
|
55
|
+
|
56
|
+
{
|
57
|
+
email:reply() with body = "This is a reply message";
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
rule reply_and_delete_mail is active {
|
62
|
+
select when mail received test_rule "reply and delete"
|
63
|
+
|
64
|
+
{
|
65
|
+
email:reply() with body = "This is a reply message" and delete_message = true;
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
|
70
|
+
rule forward_mail is active {
|
71
|
+
select when mail received test_rule "forward"
|
72
|
+
pre {
|
73
|
+
fwd_to = event:param("forward_to");
|
74
|
+
}
|
75
|
+
{
|
76
|
+
email:forward() with to = fwd_to and body = "This is a forwarded message"
|
77
|
+
}
|
78
|
+
|
79
|
+
}
|
80
|
+
|
81
|
+
rule forward_and_delete_mail is active {
|
82
|
+
select when mail received test_rule "forward and delete"
|
83
|
+
pre {
|
84
|
+
fwd_to = event:param("forward_to");
|
85
|
+
}
|
86
|
+
{
|
87
|
+
email:forward() with to = fwd_to and body = "This is a forwarded message" and delete_message = true;
|
88
|
+
}
|
89
|
+
|
90
|
+
}
|
91
|
+
}
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'lib/kns_email_endpoint'
|
2
|
+
|
3
|
+
$CONFIG_FILE = File.join(File.dirname(__FILE__), '../..', 'test_config_file.yml')
|
4
|
+
|
5
|
+
module KNSEmailEndpoint
|
6
|
+
describe Configuration do
|
7
|
+
|
8
|
+
Configuration.load_from_file $CONFIG_FILE
|
9
|
+
let(:c) { Configuration }
|
10
|
+
|
11
|
+
it "should have a storage engine" do
|
12
|
+
c.storage_engine.should_not be_nil
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "loaded" do
|
16
|
+
|
17
|
+
specify { c.work_threads.should eql 5}
|
18
|
+
specify { c.poll_delay.should eql 30}
|
19
|
+
specify { c.logdir.should eql "/tmp/email_endpoint"}
|
20
|
+
specify { c.connections.should_not be_empty }
|
21
|
+
specify { c.storage.should_not be_empty }
|
22
|
+
specify { c.log.class.should == Logger }
|
23
|
+
specify { c.storage_engine.class.should == KNSEmailEndpoint::Storage::MemcacheStorage }
|
24
|
+
specify { c.log.level.should == 0 }
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
describe "access connections" do
|
30
|
+
|
31
|
+
it 'should return a connection by name' do
|
32
|
+
c["test"]["name"].should eql "test"
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should let me loop through the connections" do
|
36
|
+
c.each_connection do |conn|
|
37
|
+
["test", "gmail"].should include(conn.name)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|