kns_email_endpoint 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/.gitignore +4 -0
  2. data/Gemfile +15 -0
  3. data/Gemfile.lock +71 -0
  4. data/Guardfile +10 -0
  5. data/LICENCE +21 -0
  6. data/README.md +1 -0
  7. data/Rakefile +2 -0
  8. data/kns_email_endpoint.gemspec +39 -0
  9. data/lib/kns_email_endpoint/configuration.rb +87 -0
  10. data/lib/kns_email_endpoint/connection.rb +97 -0
  11. data/lib/kns_email_endpoint/core_extensions/class.rb +44 -0
  12. data/lib/kns_email_endpoint/core_extensions/hash.rb +14 -0
  13. data/lib/kns_email_endpoint/core_extensions/imap.rb +36 -0
  14. data/lib/kns_email_endpoint/core_extensions/message.rb +26 -0
  15. data/lib/kns_email_endpoint/core_extensions/pop3.rb +0 -0
  16. data/lib/kns_email_endpoint/core_extensions/test_retriever.rb +42 -0
  17. data/lib/kns_email_endpoint/email_endpoint.rb +88 -0
  18. data/lib/kns_email_endpoint/message_state.rb +89 -0
  19. data/lib/kns_email_endpoint/process_email.rb +123 -0
  20. data/lib/kns_email_endpoint/storage/abstract_storage.rb +93 -0
  21. data/lib/kns_email_endpoint/storage/file_storage.rb +74 -0
  22. data/lib/kns_email_endpoint/storage/memcache_storage.rb +72 -0
  23. data/lib/kns_email_endpoint/storage/storage.rb +15 -0
  24. data/lib/kns_email_endpoint/version.rb +3 -0
  25. data/lib/kns_email_endpoint.rb +19 -0
  26. data/spec/a18x34/.app +4 -0
  27. data/spec/a18x34/a18x34.krl +91 -0
  28. data/spec/lib/kns_email_endpoint/configuration_spec.rb +44 -0
  29. data/spec/lib/kns_email_endpoint/connection_spec.rb +133 -0
  30. data/spec/lib/kns_email_endpoint/email_endpoint_spec.rb +97 -0
  31. data/spec/lib/kns_email_endpoint/message_state_spec.rb +69 -0
  32. data/spec/lib/kns_email_endpoint/process_email_spec.rb +67 -0
  33. data/spec/lib/kns_email_endpoint/storage/file_storage_spec.rb +16 -0
  34. data/spec/lib/kns_email_endpoint/storage/memcache_storage_spec.rb +17 -0
  35. data/spec/lib/kns_email_endpoint/storage/shared_storage.rb +65 -0
  36. data/spec/lib/kns_email_endpoint/storage/storage_spec.rb +14 -0
  37. data/spec/test_config_file.yml +65 -0
  38. data/spec/test_email.eml +43 -0
  39. metadata +181 -0
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ .rvmrc
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,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -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,14 @@
1
+ class Hash
2
+
3
+ def symbolize_keys
4
+ inject({}) do |options, (key, value)|
5
+ options[(key.to_sym rescue key) || key] = value
6
+ options
7
+ end
8
+ end
9
+
10
+ def symbolize_keys!
11
+ self.replace(self.symbolize_keys)
12
+ end
13
+
14
+ 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