inbox-sync 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/inbox-sync/mail_item.rb +50 -26
- data/lib/inbox-sync/runner.rb +56 -29
- data/lib/inbox-sync/sync.rb +12 -6
- data/lib/inbox-sync/version.rb +1 -1
- data/test/helper.rb +6 -3
- data/test/mail_item_test.rb +9 -9
- data/test/runner_test.rb +1 -0
- metadata +4 -4
data/lib/inbox-sync/mail_item.rb
CHANGED
@@ -5,32 +5,51 @@ module InboxSync
|
|
5
5
|
class MailItem
|
6
6
|
|
7
7
|
def self.find(imap)
|
8
|
-
imap.uid_search(['ALL']).
|
9
|
-
|
10
|
-
|
11
|
-
end.
|
12
|
-
map do |uid_meta|
|
13
|
-
self.new(
|
14
|
-
uid_meta.first,
|
15
|
-
uid_meta.last.attr['RFC822'],
|
16
|
-
uid_meta.last.attr["INTERNALDATE"]
|
17
|
-
)
|
18
|
-
end
|
8
|
+
imap.uid_search(['ALL']).map do |uid|
|
9
|
+
self.new(imap, uid)
|
10
|
+
end
|
19
11
|
end
|
20
12
|
|
21
|
-
attr_reader :
|
13
|
+
attr_reader :imap, :uid
|
22
14
|
|
23
|
-
def initialize(
|
15
|
+
def initialize(imap, uid, attrs={})
|
16
|
+
@imap = imap
|
24
17
|
@uid = uid
|
25
|
-
@
|
26
|
-
|
27
|
-
|
28
|
-
}
|
29
|
-
@message = ::Mail.new(rfc822)
|
18
|
+
@rfc822 = attrs[:rfc822]
|
19
|
+
@internal_date = attrs[:internal_date]
|
20
|
+
@message = attrs[:message]
|
30
21
|
end
|
31
22
|
|
32
23
|
def name
|
33
|
-
"[#{
|
24
|
+
@name ||= "[#{self.uid}] #{self.message.from}: #{self.message.subject.inspect} (#{time_s(self.message.date)})"
|
25
|
+
end
|
26
|
+
|
27
|
+
def meta
|
28
|
+
@meta ||= @imap.uid_fetch(self.uid, ['RFC822', 'INTERNALDATE']).first
|
29
|
+
end
|
30
|
+
|
31
|
+
def rfc822
|
32
|
+
@rfc822 ||= self.meta.attr['RFC822']
|
33
|
+
end
|
34
|
+
|
35
|
+
def rfc822=(value)
|
36
|
+
@rfc822 = value
|
37
|
+
end
|
38
|
+
|
39
|
+
def internal_date
|
40
|
+
@internal_date ||= self.meta.attr['INTERNALDATE']
|
41
|
+
end
|
42
|
+
|
43
|
+
def internal_date=(value)
|
44
|
+
@internal_date = value
|
45
|
+
end
|
46
|
+
|
47
|
+
def message
|
48
|
+
@message ||= ::Mail.new(self.rfc822)
|
49
|
+
end
|
50
|
+
|
51
|
+
def message=(value)
|
52
|
+
@message = value
|
34
53
|
end
|
35
54
|
|
36
55
|
# Returns a stripped down version of the mail item
|
@@ -40,15 +59,11 @@ module InboxSync
|
|
40
59
|
# This implies that stripped down mail items have no attachments.
|
41
60
|
|
42
61
|
def stripped
|
43
|
-
@stripped ||= strip_down(
|
44
|
-
self.uid,
|
45
|
-
self.meta['RFC822'],
|
46
|
-
self.meta["INTERNALDATE"]
|
47
|
-
))
|
62
|
+
@stripped ||= strip_down(copy_mail_item(self))
|
48
63
|
end
|
49
64
|
|
50
65
|
def inspect
|
51
|
-
"#<#{self.class}:#{'0x%x' % (self.object_id << 1)}: @uid=#{
|
66
|
+
"#<#{self.class}:#{'0x%x' % (self.object_id << 1)}: @uid=#{self.uid.inspect}, from=#{self.message.from.inspect}, subject=#{self.message.subject.inspect}, 'INTERNALDATE'=#{self.internal_date.inspect}>"
|
52
67
|
end
|
53
68
|
|
54
69
|
private
|
@@ -57,6 +72,14 @@ module InboxSync
|
|
57
72
|
datetime.strftime("%a %b %-d %Y, %I:%M %p")
|
58
73
|
end
|
59
74
|
|
75
|
+
def copy_mail_item(item)
|
76
|
+
MailItem.new(item.imap, item.uid, {
|
77
|
+
:rfc822 => item.rfc822,
|
78
|
+
:internal_date => item.internal_date,
|
79
|
+
:message => item.message
|
80
|
+
})
|
81
|
+
end
|
82
|
+
|
60
83
|
def strip_down(mail_item)
|
61
84
|
message = mail_item.message
|
62
85
|
if message.multipart?
|
@@ -64,7 +87,8 @@ module InboxSync
|
|
64
87
|
!part.content_type.match(/text\/plain/)
|
65
88
|
end
|
66
89
|
message.parts.first.body = strip_down_body_s(message.parts.first.body)
|
67
|
-
mail_item.
|
90
|
+
mail_item.message = message
|
91
|
+
mail_item.rfc822 = message.to_s
|
68
92
|
end
|
69
93
|
mail_item
|
70
94
|
end
|
data/lib/inbox-sync/runner.rb
CHANGED
@@ -18,67 +18,94 @@ module InboxSync
|
|
18
18
|
@shutdown = false
|
19
19
|
@running_syncs_thread = nil
|
20
20
|
|
21
|
-
Signal.trap('SIGINT',
|
22
|
-
Signal.trap('SIGQUIT', lambda{
|
21
|
+
Signal.trap('SIGINT', lambda{ raise Interrupt, 'SIGINT' })
|
22
|
+
Signal.trap('SIGQUIT', lambda{ raise Interrupt, 'SIGQUIT' })
|
23
|
+
Signal.trap('TERM', lambda{ raise Interrupt, 'TERM' })
|
23
24
|
end
|
24
25
|
|
25
26
|
def start
|
26
|
-
startup
|
27
|
+
startup!
|
27
28
|
loop do
|
28
|
-
break if @shutdown
|
29
29
|
loop_run
|
30
|
+
break if shutdown?
|
30
31
|
end
|
31
|
-
shutdown
|
32
|
+
shutdown!
|
32
33
|
end
|
33
34
|
|
34
35
|
def stop
|
35
|
-
|
36
|
-
|
36
|
+
if @shutdown != true
|
37
|
+
main_log "Stop signal - waiting for any running syncs to finish."
|
38
|
+
@shutdown = true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def shutdown?
|
43
|
+
!!@shutdown
|
37
44
|
end
|
38
45
|
|
39
46
|
protected
|
40
47
|
|
41
|
-
def startup
|
48
|
+
def startup!
|
42
49
|
main_log "Starting up the runner."
|
50
|
+
if @interval < 0
|
51
|
+
main_log "run-once interval - signaling stop"
|
52
|
+
stop
|
53
|
+
@interval = 0
|
54
|
+
end
|
43
55
|
end
|
44
56
|
|
45
|
-
def shutdown
|
57
|
+
def shutdown!
|
46
58
|
main_log "Shutting down the runner"
|
47
59
|
end
|
48
60
|
|
49
61
|
def loop_run
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
thread_log_error(err, :error)
|
62
|
+
begin
|
63
|
+
main_log "Starting syncs in fresh thread."
|
64
|
+
@running_syncs_thread = Thread.new { run_syncs_thread }
|
65
|
+
|
66
|
+
if @interval > 0
|
67
|
+
main_log "Sleeping for #{@interval} second interval."
|
68
|
+
sleep(@interval)
|
69
|
+
main_log "Woke from sleep"
|
59
70
|
end
|
60
|
-
|
61
|
-
|
71
|
+
rescue Interrupt => err
|
72
|
+
stop
|
73
|
+
ensure
|
74
|
+
join_syncs_thread
|
62
75
|
end
|
76
|
+
end
|
63
77
|
|
64
|
-
|
65
|
-
|
78
|
+
def join_syncs_thread
|
79
|
+
begin
|
80
|
+
if @running_syncs_thread
|
81
|
+
main_log "Waiting for running syncs thread to join..."
|
82
|
+
@running_syncs_thread.join
|
83
|
+
main_log "... running syncs thread has joined."
|
84
|
+
end
|
85
|
+
@running_syncs_thread = nil
|
86
|
+
rescue Interrupt => err
|
66
87
|
stop
|
67
|
-
|
88
|
+
join_syncs_thread
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def run_syncs_thread
|
93
|
+
thread_log "starting syncs..."
|
94
|
+
|
95
|
+
begin
|
96
|
+
run_syncs
|
97
|
+
rescue Exception => err
|
98
|
+
thread_log_error(err, :error)
|
68
99
|
end
|
69
100
|
|
70
|
-
|
71
|
-
sleep(@interval)
|
72
|
-
main_log "Woke from sleep - waiting for running syncs thread to join..."
|
73
|
-
@running_syncs_thread.join
|
74
|
-
main_log "... running sycs thread joined."
|
101
|
+
thread_log "...syncs finished"
|
75
102
|
end
|
76
103
|
|
77
104
|
def run_syncs
|
78
105
|
@syncs.each do |sync|
|
79
106
|
begin
|
80
107
|
sync.setup
|
81
|
-
sync.run
|
108
|
+
sync.run(self)
|
82
109
|
rescue Exception => err
|
83
110
|
run_sync_handle_error(sync, err)
|
84
111
|
end
|
data/lib/inbox-sync/sync.rb
CHANGED
@@ -52,8 +52,9 @@ module InboxSync
|
|
52
52
|
logger.info "=== #{config_log_detail(@config.source)} sync finished. ==="
|
53
53
|
end
|
54
54
|
|
55
|
-
def run
|
56
|
-
|
55
|
+
def run(runner=nil)
|
56
|
+
return if runner && runner.shutdown?
|
57
|
+
each_source_mail_item(runner) do |mail_item|
|
57
58
|
begin
|
58
59
|
response = send_to_dest(mail_item)
|
59
60
|
dest_uid = parse_append_response_uid(response)
|
@@ -95,11 +96,16 @@ module InboxSync
|
|
95
96
|
true
|
96
97
|
end
|
97
98
|
|
98
|
-
def each_source_mail_item
|
99
|
+
def each_source_mail_item(runner=nil)
|
100
|
+
logger.info "* find: #{config_log_detail(@config.source)}, #{@config.source.inbox.inspect}..."
|
99
101
|
items = MailItem.find(@source_imap)
|
100
|
-
logger.info "* found #{items.size}
|
102
|
+
logger.info "* ...found #{items.size} mail items"
|
101
103
|
|
102
104
|
items.each do |mail_item|
|
105
|
+
if runner && runner.shutdown?
|
106
|
+
logger.info "* the runner has been shutdown - aborting the sync"
|
107
|
+
break
|
108
|
+
end
|
103
109
|
logger.debug "** #{mail_item.inspect}"
|
104
110
|
yield mail_item
|
105
111
|
end
|
@@ -128,9 +134,9 @@ module InboxSync
|
|
128
134
|
logger.info "** Appending #{desc}#{mail_item.uid} to dest #{@config.dest.inbox}..."
|
129
135
|
|
130
136
|
inbox = @config.dest.inbox
|
131
|
-
mail_s = mail_item.
|
137
|
+
mail_s = mail_item.rfc822
|
132
138
|
flags = []
|
133
|
-
date = mail_item.
|
139
|
+
date = mail_item.internal_date
|
134
140
|
|
135
141
|
using_dest_imap do |imap|
|
136
142
|
imap.append(inbox, mail_s, flags, date)
|
data/lib/inbox-sync/version.rb
CHANGED
data/test/helper.rb
CHANGED
@@ -41,7 +41,10 @@ class Assert::Context
|
|
41
41
|
}
|
42
42
|
|
43
43
|
def test_mail_item
|
44
|
-
InboxSync::MailItem.new('
|
44
|
+
InboxSync::MailItem.new('dummy_imap', '12345', {
|
45
|
+
:rfc822 => TEST_MAIL_DATA['RFC822'],
|
46
|
+
:internal_date => TEST_MAIL_DATA['INTERNALDATE']
|
47
|
+
})
|
45
48
|
end
|
46
49
|
|
47
50
|
def setup_sync_mail_item
|
@@ -64,9 +67,9 @@ class Assert::Context
|
|
64
67
|
|
65
68
|
# append the test mail on the source imap
|
66
69
|
inbox = sync.config.source.inbox
|
67
|
-
mail_s = test_mail_item.
|
70
|
+
mail_s = test_mail_item.rfc822
|
68
71
|
flags = []
|
69
|
-
date = test_mail_item.
|
72
|
+
date = test_mail_item.internal_date
|
70
73
|
|
71
74
|
sync.source_imap.append(inbox, mail_s, flags, date)
|
72
75
|
end
|
data/test/mail_item_test.rb
CHANGED
@@ -11,20 +11,20 @@ module InboxSync
|
|
11
11
|
end
|
12
12
|
subject { @item }
|
13
13
|
|
14
|
-
should have_readers :
|
14
|
+
should have_readers :imap, :uid
|
15
15
|
should have_class_method :find
|
16
|
-
should
|
16
|
+
should have_instance_methods :name, :meta, :stripped
|
17
|
+
should have_instance_methods :rfc822, :rfc822=
|
18
|
+
should have_instance_methods :internal_date, :internal_date=
|
19
|
+
should have_instance_methods :message, :message=
|
17
20
|
|
18
21
|
should "build a Mail Message from the raw IMAP attr data" do
|
19
22
|
assert_kind_of ::Mail::Message, subject.message
|
20
23
|
end
|
21
24
|
|
22
|
-
should "provide the
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
assert_equal TEST_MAIL_DATA['RFC822'], subject.meta['RFC822']
|
27
|
-
assert_equal TEST_MAIL_DATA['INTERNALDATE'], subject.meta['INTERNALDATE']
|
25
|
+
should "provide the IMAP rfc822 and internal_date" do
|
26
|
+
assert_equal TEST_MAIL_DATA['RFC822'], subject.rfc822
|
27
|
+
assert_equal TEST_MAIL_DATA['INTERNALDATE'], subject.internal_date
|
28
28
|
end
|
29
29
|
|
30
30
|
should "be named by its uid, from, subject, and date" do
|
@@ -55,7 +55,7 @@ module InboxSync
|
|
55
55
|
end
|
56
56
|
|
57
57
|
should "set its RFC822 to the stripped message string" do
|
58
|
-
assert_equal subject.
|
58
|
+
assert_equal subject.rfc822, subject.message.to_s
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
data/test/runner_test.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: inbox-sync
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 2
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Kelly Redding
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-06-
|
18
|
+
date: 2012-06-06 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
type: :development
|