mlist 0.1.9
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +59 -0
- data/README +204 -0
- data/Rakefile +27 -0
- data/TODO +36 -0
- data/VERSION.yml +4 -0
- data/lib/mlist/email.rb +69 -0
- data/lib/mlist/email_post.rb +126 -0
- data/lib/mlist/email_server/base.rb +33 -0
- data/lib/mlist/email_server/default.rb +31 -0
- data/lib/mlist/email_server/fake.rb +16 -0
- data/lib/mlist/email_server/pop.rb +28 -0
- data/lib/mlist/email_server/smtp.rb +24 -0
- data/lib/mlist/email_server.rb +2 -0
- data/lib/mlist/email_subscriber.rb +6 -0
- data/lib/mlist/list.rb +183 -0
- data/lib/mlist/mail_list.rb +277 -0
- data/lib/mlist/manager/database.rb +48 -0
- data/lib/mlist/manager/notifier.rb +31 -0
- data/lib/mlist/manager.rb +30 -0
- data/lib/mlist/message.rb +150 -0
- data/lib/mlist/server.rb +62 -0
- data/lib/mlist/thread.rb +98 -0
- data/lib/mlist/util/email_helpers.rb +155 -0
- data/lib/mlist/util/header_sanitizer.rb +71 -0
- data/lib/mlist/util/quoting.rb +70 -0
- data/lib/mlist/util/tmail_builder.rb +42 -0
- data/lib/mlist/util/tmail_methods.rb +138 -0
- data/lib/mlist/util.rb +12 -0
- data/lib/mlist.rb +46 -0
- data/lib/pop_ssl.rb +999 -0
- data/rails/init.rb +22 -0
- data/spec/fixtures/schema.rb +94 -0
- data/spec/integration/date_formats_spec.rb +12 -0
- data/spec/integration/mlist_spec.rb +232 -0
- data/spec/integration/pop_email_server_spec.rb +22 -0
- data/spec/integration/proof_spec.rb +74 -0
- data/spec/matchers/equal_tmail.rb +53 -0
- data/spec/matchers/have_address.rb +48 -0
- data/spec/matchers/have_header.rb +104 -0
- data/spec/models/email_post_spec.rb +100 -0
- data/spec/models/email_server/base_spec.rb +11 -0
- data/spec/models/email_spec.rb +54 -0
- data/spec/models/mail_list_spec.rb +469 -0
- data/spec/models/message_spec.rb +109 -0
- data/spec/models/thread_spec.rb +83 -0
- data/spec/models/util/email_helpers_spec.rb +47 -0
- data/spec/models/util/header_sanitizer_spec.rb +19 -0
- data/spec/models/util/quoting_spec.rb +96 -0
- data/spec/spec_helper.rb +76 -0
- metadata +103 -0
@@ -0,0 +1,138 @@
|
|
1
|
+
module MList
|
2
|
+
module Util
|
3
|
+
|
4
|
+
module TMailReaders
|
5
|
+
def date
|
6
|
+
if date = tmail.header_string('date')
|
7
|
+
Time.parse(date)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def from_address
|
12
|
+
tmail.from.first.downcase
|
13
|
+
end
|
14
|
+
|
15
|
+
def html
|
16
|
+
case tmail.content_type
|
17
|
+
when 'text/html'
|
18
|
+
tmail.body.strip
|
19
|
+
when 'multipart/alternative'
|
20
|
+
html_part = tmail.parts.detect {|part| part.content_type == 'text/html'}
|
21
|
+
html_part.body.strip if html_part
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def identifier
|
26
|
+
remove_brackets(tmail.header_string('message-id'))
|
27
|
+
end
|
28
|
+
|
29
|
+
def mailer
|
30
|
+
tmail.header_string('x-mailer')
|
31
|
+
end
|
32
|
+
|
33
|
+
def text
|
34
|
+
text_content = ''
|
35
|
+
extract_text_content(tmail, text_content)
|
36
|
+
return text_content unless text_content.blank?
|
37
|
+
|
38
|
+
html_content = ''
|
39
|
+
extract_html_content(tmail, html_content)
|
40
|
+
return html_to_text(html_content) unless html_content.blank?
|
41
|
+
|
42
|
+
return nil
|
43
|
+
end
|
44
|
+
|
45
|
+
# Answers the first text/plain part it can find, the tmail itself if
|
46
|
+
# it's content type is text/plain.
|
47
|
+
#
|
48
|
+
def text_plain_part(part = tmail)
|
49
|
+
case part.content_type
|
50
|
+
when 'text/plain'
|
51
|
+
part
|
52
|
+
when 'multipart/alternative'
|
53
|
+
part.parts.detect {|part| text_plain_part(part)}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
def extract_html_content(part, collector)
|
59
|
+
case part.content_type
|
60
|
+
when 'text/html'
|
61
|
+
collector << part.body.strip
|
62
|
+
when 'multipart/alternative', 'multipart/mixed', 'multipart/related'
|
63
|
+
part.parts.each {|part| extract_html_content(part, collector)}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def extract_text_content(part, collector)
|
68
|
+
case part.content_type
|
69
|
+
when 'text/plain'
|
70
|
+
collector << part.body.strip
|
71
|
+
when 'multipart/alternative', 'multipart/mixed', 'multipart/related'
|
72
|
+
part.parts.each {|part| extract_text_content(part, collector)}
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
module TMailWriters
|
78
|
+
def charset
|
79
|
+
'utf-8'
|
80
|
+
end
|
81
|
+
|
82
|
+
def delete_header(name)
|
83
|
+
tmail[name] = nil
|
84
|
+
end
|
85
|
+
|
86
|
+
def headers=(updates)
|
87
|
+
updates.each do |k,v|
|
88
|
+
if TMail::Mail::ALLOW_MULTIPLE.include?(k.downcase)
|
89
|
+
prepend_header(k,v)
|
90
|
+
else
|
91
|
+
write_header(k,v)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Add another value for the named header, it's position being earlier in
|
97
|
+
# the email than those that are already present. This will raise an error
|
98
|
+
# if the header does not allow multiple values according to
|
99
|
+
# TMail::Mail::ALLOW_MULTIPLE.
|
100
|
+
#
|
101
|
+
def prepend_header(name, value)
|
102
|
+
original = tmail[name] || []
|
103
|
+
tmail[name] = nil
|
104
|
+
tmail[name] = sanitize_header(charset, name, value)
|
105
|
+
tmail[name] = tmail[name] + original
|
106
|
+
end
|
107
|
+
|
108
|
+
def write_header(name, value)
|
109
|
+
tmail[name] = sanitize_header(charset, name, value)
|
110
|
+
end
|
111
|
+
|
112
|
+
def to=(recipient_addresses)
|
113
|
+
tmail.to = sanitize_header(charset, 'to', recipient_addresses)
|
114
|
+
end
|
115
|
+
|
116
|
+
def bcc=(recipient_addresses)
|
117
|
+
tmail.bcc = sanitize_header(charset, 'bcc', recipient_addresses)
|
118
|
+
end
|
119
|
+
|
120
|
+
def from=(from_address)
|
121
|
+
tmail.from = sanitize_header(charset, 'from', from_address)
|
122
|
+
end
|
123
|
+
|
124
|
+
def in_reply_to=(*values)
|
125
|
+
tmail.in_reply_to = sanitize_header(charset, 'in-reply-to', *values)
|
126
|
+
end
|
127
|
+
|
128
|
+
def mailer=(value)
|
129
|
+
write_header('x-mailer', value)
|
130
|
+
end
|
131
|
+
|
132
|
+
def message_id=(value)
|
133
|
+
tmail.message_id = sanitize_header(charset, 'message-id', value)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
end
|
data/lib/mlist/util.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'mlist/util/quoting'
|
2
|
+
require 'mlist/util/header_sanitizer'
|
3
|
+
require 'mlist/util/email_helpers'
|
4
|
+
require 'mlist/util/tmail_methods'
|
5
|
+
require 'mlist/util/tmail_builder'
|
6
|
+
|
7
|
+
module MList
|
8
|
+
module Util
|
9
|
+
mattr_accessor :default_header_sanitizers
|
10
|
+
self.default_header_sanitizers = HeaderSanitizerHash.new
|
11
|
+
end
|
12
|
+
end
|
data/lib/mlist.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
require 'uuid'
|
4
|
+
require 'tmail'
|
5
|
+
require 'hpricot'
|
6
|
+
require 'active_support'
|
7
|
+
require 'active_record'
|
8
|
+
|
9
|
+
require 'mlist/util'
|
10
|
+
require 'mlist/email'
|
11
|
+
require 'mlist/message'
|
12
|
+
require 'mlist/list'
|
13
|
+
require 'mlist/mail_list'
|
14
|
+
require 'mlist/email_post'
|
15
|
+
require 'mlist/email_server'
|
16
|
+
require 'mlist/email_subscriber'
|
17
|
+
require 'mlist/server'
|
18
|
+
require 'mlist/thread'
|
19
|
+
|
20
|
+
require 'mlist/manager'
|
21
|
+
|
22
|
+
module MList
|
23
|
+
mattr_reader :version
|
24
|
+
@@version = YAML.load_file(File.join(File.dirname(__FILE__), '..', "VERSION.yml"))
|
25
|
+
class << @@version
|
26
|
+
def to_s
|
27
|
+
@to_s ||= [self[:major], self[:minor], self[:patch]].join('.')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class DoubleDeliveryError < StandardError
|
32
|
+
def initialize(message)
|
33
|
+
super("A message should never be delivered more than once. An attempt was made to deliver this message:\n#{message.inspect}")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
Time::DATE_FORMATS[:mlist_reply_timestamp] = Date::DATE_FORMATS[:mlist_reply_timestamp] = lambda do |time|
|
39
|
+
time.strftime('%a, %b %d, %Y at %I:%M %p').sub(/0(\d,)/, '\1').sub(/0(\d:)/, '\1')
|
40
|
+
end
|
41
|
+
|
42
|
+
# In order to keep the inline images in email intact. Certainly a scary bit of
|
43
|
+
# hacking, but this is the solution out there on the internet.
|
44
|
+
TMail::HeaderField::FNAME_TO_CLASS.delete 'content-id'
|
45
|
+
|
46
|
+
TMail::Mail::ALLOW_MULTIPLE['x-beenthere'] = true
|