mail-portertech 2.6.2.edge
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.
- checksums.yaml +7 -0
- data/CHANGELOG.rdoc +753 -0
- data/CONTRIBUTING.md +60 -0
- data/Dependencies.txt +2 -0
- data/Gemfile +15 -0
- data/MIT-LICENSE +20 -0
- data/README.md +683 -0
- data/Rakefile +29 -0
- data/TODO.rdoc +9 -0
- data/lib/mail.rb +91 -0
- data/lib/mail/attachments_list.rb +104 -0
- data/lib/mail/body.rb +291 -0
- data/lib/mail/check_delivery_params.rb +20 -0
- data/lib/mail/configuration.rb +75 -0
- data/lib/mail/core_extensions/nil.rb +19 -0
- data/lib/mail/core_extensions/object.rb +13 -0
- data/lib/mail/core_extensions/smtp.rb +24 -0
- data/lib/mail/core_extensions/string.rb +43 -0
- data/lib/mail/core_extensions/string/access.rb +145 -0
- data/lib/mail/core_extensions/string/multibyte.rb +78 -0
- data/lib/mail/elements.rb +14 -0
- data/lib/mail/elements/address.rb +270 -0
- data/lib/mail/elements/address_list.rb +51 -0
- data/lib/mail/elements/content_disposition_element.rb +26 -0
- data/lib/mail/elements/content_location_element.rb +21 -0
- data/lib/mail/elements/content_transfer_encoding_element.rb +17 -0
- data/lib/mail/elements/content_type_element.rb +31 -0
- data/lib/mail/elements/date_time_element.rb +22 -0
- data/lib/mail/elements/envelope_from_element.rb +39 -0
- data/lib/mail/elements/message_ids_element.rb +24 -0
- data/lib/mail/elements/mime_version_element.rb +22 -0
- data/lib/mail/elements/phrase_list.rb +16 -0
- data/lib/mail/elements/received_element.rb +26 -0
- data/lib/mail/encodings.rb +304 -0
- data/lib/mail/encodings/7bit.rb +31 -0
- data/lib/mail/encodings/8bit.rb +31 -0
- data/lib/mail/encodings/base64.rb +33 -0
- data/lib/mail/encodings/binary.rb +31 -0
- data/lib/mail/encodings/quoted_printable.rb +39 -0
- data/lib/mail/encodings/transfer_encoding.rb +58 -0
- data/lib/mail/envelope.rb +30 -0
- data/lib/mail/field.rb +247 -0
- data/lib/mail/field_list.rb +33 -0
- data/lib/mail/fields.rb +35 -0
- data/lib/mail/fields/bcc_field.rb +56 -0
- data/lib/mail/fields/cc_field.rb +55 -0
- data/lib/mail/fields/comments_field.rb +41 -0
- data/lib/mail/fields/common/address_container.rb +16 -0
- data/lib/mail/fields/common/common_address.rb +135 -0
- data/lib/mail/fields/common/common_date.rb +35 -0
- data/lib/mail/fields/common/common_field.rb +57 -0
- data/lib/mail/fields/common/common_message_id.rb +48 -0
- data/lib/mail/fields/common/parameter_hash.rb +58 -0
- data/lib/mail/fields/content_description_field.rb +19 -0
- data/lib/mail/fields/content_disposition_field.rb +70 -0
- data/lib/mail/fields/content_id_field.rb +62 -0
- data/lib/mail/fields/content_location_field.rb +42 -0
- data/lib/mail/fields/content_transfer_encoding_field.rb +44 -0
- data/lib/mail/fields/content_type_field.rb +201 -0
- data/lib/mail/fields/date_field.rb +57 -0
- data/lib/mail/fields/from_field.rb +55 -0
- data/lib/mail/fields/in_reply_to_field.rb +56 -0
- data/lib/mail/fields/keywords_field.rb +44 -0
- data/lib/mail/fields/message_id_field.rb +82 -0
- data/lib/mail/fields/mime_version_field.rb +53 -0
- data/lib/mail/fields/optional_field.rb +13 -0
- data/lib/mail/fields/received_field.rb +75 -0
- data/lib/mail/fields/references_field.rb +56 -0
- data/lib/mail/fields/reply_to_field.rb +55 -0
- data/lib/mail/fields/resent_bcc_field.rb +55 -0
- data/lib/mail/fields/resent_cc_field.rb +55 -0
- data/lib/mail/fields/resent_date_field.rb +35 -0
- data/lib/mail/fields/resent_from_field.rb +55 -0
- data/lib/mail/fields/resent_message_id_field.rb +34 -0
- data/lib/mail/fields/resent_sender_field.rb +62 -0
- data/lib/mail/fields/resent_to_field.rb +55 -0
- data/lib/mail/fields/return_path_field.rb +65 -0
- data/lib/mail/fields/sender_field.rb +67 -0
- data/lib/mail/fields/structured_field.rb +51 -0
- data/lib/mail/fields/subject_field.rb +16 -0
- data/lib/mail/fields/to_field.rb +55 -0
- data/lib/mail/fields/unstructured_field.rb +204 -0
- data/lib/mail/header.rb +274 -0
- data/lib/mail/indifferent_hash.rb +146 -0
- data/lib/mail/mail.rb +267 -0
- data/lib/mail/matchers/has_sent_mail.rb +157 -0
- data/lib/mail/message.rb +2160 -0
- data/lib/mail/multibyte.rb +42 -0
- data/lib/mail/multibyte/chars.rb +474 -0
- data/lib/mail/multibyte/exceptions.rb +8 -0
- data/lib/mail/multibyte/unicode.rb +400 -0
- data/lib/mail/multibyte/utils.rb +60 -0
- data/lib/mail/network.rb +14 -0
- data/lib/mail/network/delivery_methods/exim.rb +52 -0
- data/lib/mail/network/delivery_methods/file_delivery.rb +45 -0
- data/lib/mail/network/delivery_methods/sendmail.rb +89 -0
- data/lib/mail/network/delivery_methods/smtp.rb +142 -0
- data/lib/mail/network/delivery_methods/smtp_connection.rb +61 -0
- data/lib/mail/network/delivery_methods/test_mailer.rb +44 -0
- data/lib/mail/network/retriever_methods/base.rb +63 -0
- data/lib/mail/network/retriever_methods/imap.rb +173 -0
- data/lib/mail/network/retriever_methods/pop3.rb +140 -0
- data/lib/mail/network/retriever_methods/test_retriever.rb +43 -0
- data/lib/mail/parsers.rb +26 -0
- data/lib/mail/parsers/address_lists_parser.rb +132 -0
- data/lib/mail/parsers/content_disposition_parser.rb +67 -0
- data/lib/mail/parsers/content_location_parser.rb +35 -0
- data/lib/mail/parsers/content_transfer_encoding_parser.rb +33 -0
- data/lib/mail/parsers/content_type_parser.rb +64 -0
- data/lib/mail/parsers/date_time_parser.rb +36 -0
- data/lib/mail/parsers/envelope_from_parser.rb +45 -0
- data/lib/mail/parsers/message_ids_parser.rb +39 -0
- data/lib/mail/parsers/mime_version_parser.rb +41 -0
- data/lib/mail/parsers/phrase_lists_parser.rb +33 -0
- data/lib/mail/parsers/ragel.rb +17 -0
- data/lib/mail/parsers/ragel/common.rl +184 -0
- data/lib/mail/parsers/ragel/date_time.rl +30 -0
- data/lib/mail/parsers/ragel/parser_info.rb +61 -0
- data/lib/mail/parsers/ragel/ruby.rb +39 -0
- data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +14864 -0
- data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +751 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +614 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +447 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +825 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +817 -0
- data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +2129 -0
- data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +1570 -0
- data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +440 -0
- data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +564 -0
- data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +51 -0
- data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +5144 -0
- data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +37 -0
- data/lib/mail/parsers/received_parser.rb +47 -0
- data/lib/mail/part.rb +120 -0
- data/lib/mail/parts_list.rb +57 -0
- data/lib/mail/patterns.rb +37 -0
- data/lib/mail/utilities.rb +225 -0
- data/lib/mail/values/unicode_tables.dat +0 -0
- data/lib/mail/version.rb +4 -0
- data/lib/mail/version_specific/ruby_1_8.rb +119 -0
- data/lib/mail/version_specific/ruby_1_9.rb +159 -0
- metadata +276 -0
@@ -0,0 +1,37 @@
|
|
1
|
+
%%{
|
2
|
+
machine received;
|
3
|
+
|
4
|
+
include rb_actions "rb_actions.rl";
|
5
|
+
include common "../../common.rl";
|
6
|
+
|
7
|
+
getkey data_unpacked[p];
|
8
|
+
|
9
|
+
main := received;
|
10
|
+
}%%
|
11
|
+
|
12
|
+
module Mail
|
13
|
+
module Parsers
|
14
|
+
module Ragel
|
15
|
+
module ReceivedMachine
|
16
|
+
%%write data;
|
17
|
+
|
18
|
+
def self.parse(data)
|
19
|
+
p = 0
|
20
|
+
eof = data.length
|
21
|
+
stack = []
|
22
|
+
|
23
|
+
actions = []
|
24
|
+
data_unpacked = data.bytes.to_a
|
25
|
+
%%write init;
|
26
|
+
%%write exec;
|
27
|
+
|
28
|
+
if p == eof && cs >= %%{ write first_final; }%%
|
29
|
+
return actions, nil
|
30
|
+
else
|
31
|
+
return [], "Only able to parse up to #{data[0..p]}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
%%{
|
2
|
+
machine <%= parser_name %>;
|
3
|
+
|
4
|
+
include rb_actions "rb_actions.rl";
|
5
|
+
include common "../../common.rl";
|
6
|
+
|
7
|
+
getkey data_unpacked[p];
|
8
|
+
|
9
|
+
main := <%= parser_name %>;
|
10
|
+
}%%
|
11
|
+
|
12
|
+
module Mail
|
13
|
+
module Parsers
|
14
|
+
module Ragel
|
15
|
+
module <%= parser_name_cc %>
|
16
|
+
%%write data;
|
17
|
+
|
18
|
+
def self.parse(data)
|
19
|
+
p = 0
|
20
|
+
eof = data.length
|
21
|
+
stack = []
|
22
|
+
|
23
|
+
actions = []
|
24
|
+
data_unpacked = data.bytes.to_a
|
25
|
+
%%write init;
|
26
|
+
%%write exec;
|
27
|
+
|
28
|
+
if p == eof && cs >= %%{ write first_final; }%%
|
29
|
+
return actions, nil
|
30
|
+
else
|
31
|
+
return [], "Only able to parse up to #{data[0..p]}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Mail::Parsers
|
2
|
+
class ReceivedParser
|
3
|
+
|
4
|
+
def parse(s)
|
5
|
+
actions, error = Ragel.parse(:received, s)
|
6
|
+
if error
|
7
|
+
raise Mail::Field::ParseError.new(Mail::ReceivedElement, s, error)
|
8
|
+
end
|
9
|
+
|
10
|
+
received = ReceivedStruct.new
|
11
|
+
|
12
|
+
received_tokens_s = date_s = time_s = nil
|
13
|
+
actions.each_slice(2) do |action_id, p|
|
14
|
+
action = Mail::Parsers::Ragel::ACTIONS[action_id]
|
15
|
+
case action
|
16
|
+
|
17
|
+
# Received Tokens:
|
18
|
+
when :received_tokens_s then received_tokens_s = p
|
19
|
+
when :received_tokens_e
|
20
|
+
received.info = s[received_tokens_s..(p-1)]
|
21
|
+
|
22
|
+
# Date
|
23
|
+
when :date_s then date_s = p
|
24
|
+
when :date_e
|
25
|
+
received.date = s[date_s..(p-1)].strip
|
26
|
+
|
27
|
+
# Time
|
28
|
+
when :time_s then time_s = p
|
29
|
+
when :time_e
|
30
|
+
received.time = s[time_s..(p-1)]
|
31
|
+
|
32
|
+
when :angle_addr_s, :comment_e, :comment_s,
|
33
|
+
:domain_e, :domain_s, :local_dot_atom_e,
|
34
|
+
:local_dot_atom_pre_comment_e,
|
35
|
+
:local_dot_atom_pre_comment_s,
|
36
|
+
:local_dot_atom_s, :qstr_e, :qstr_s,
|
37
|
+
:local_quoted_string_s, :local_quoted_string_e
|
38
|
+
# ignored actions
|
39
|
+
|
40
|
+
else
|
41
|
+
raise Mail::Field::ParseError.new(Mail::ReceivedElement, s, "Failed to process unknown action: #{action}")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
received
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/mail/part.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mail
|
3
|
+
class Part < Message
|
4
|
+
|
5
|
+
# Creates a new empty Content-ID field and inserts it in the correct order
|
6
|
+
# into the Header. The ContentIdField object will automatically generate
|
7
|
+
# a unique content ID if you try and encode it or output it to_s without
|
8
|
+
# specifying a content id.
|
9
|
+
#
|
10
|
+
# It will preserve the content ID you specify if you do.
|
11
|
+
def add_content_id(content_id_val = '')
|
12
|
+
header['content-id'] = content_id_val
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns true if the part has a content ID field, the field may or may
|
16
|
+
# not have a value, but the field exists or not.
|
17
|
+
def has_content_id?
|
18
|
+
header.has_content_id?
|
19
|
+
end
|
20
|
+
|
21
|
+
def inline_content_id
|
22
|
+
# TODO: Deprecated in 2.2.2 - Remove in 2.3
|
23
|
+
STDERR.puts("Part#inline_content_id is deprecated, please call Part#cid instead")
|
24
|
+
cid
|
25
|
+
end
|
26
|
+
|
27
|
+
def cid
|
28
|
+
add_content_id unless has_content_id?
|
29
|
+
uri_escape(unbracket(content_id))
|
30
|
+
end
|
31
|
+
|
32
|
+
def url
|
33
|
+
"cid:#{cid}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def inline?
|
37
|
+
header[:content_disposition].disposition_type == 'inline' if header[:content_disposition]
|
38
|
+
end
|
39
|
+
|
40
|
+
def add_required_fields
|
41
|
+
super
|
42
|
+
add_content_id if !has_content_id? && inline?
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_required_message_fields
|
46
|
+
# Override so we don't add Date, MIME-Version, or Message-ID.
|
47
|
+
end
|
48
|
+
|
49
|
+
def delivery_status_report_part?
|
50
|
+
(main_type =~ /message/i && sub_type =~ /delivery-status/i) && body =~ /Status:/
|
51
|
+
end
|
52
|
+
|
53
|
+
def delivery_status_data
|
54
|
+
delivery_status_report_part? ? parse_delivery_status_report : {}
|
55
|
+
end
|
56
|
+
|
57
|
+
def bounced?
|
58
|
+
if action.is_a?(Array)
|
59
|
+
!!(action.first =~ /failed/i)
|
60
|
+
else
|
61
|
+
!!(action =~ /failed/i)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
# Either returns the action if the message has just a single report, or an
|
67
|
+
# array of all the actions, one for each report
|
68
|
+
def action
|
69
|
+
get_return_values('action')
|
70
|
+
end
|
71
|
+
|
72
|
+
def final_recipient
|
73
|
+
get_return_values('final-recipient')
|
74
|
+
end
|
75
|
+
|
76
|
+
def error_status
|
77
|
+
get_return_values('status')
|
78
|
+
end
|
79
|
+
|
80
|
+
def diagnostic_code
|
81
|
+
get_return_values('diagnostic-code')
|
82
|
+
end
|
83
|
+
|
84
|
+
def remote_mta
|
85
|
+
get_return_values('remote-mta')
|
86
|
+
end
|
87
|
+
|
88
|
+
def retryable?
|
89
|
+
!(error_status =~ /^5/)
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def get_return_values(key)
|
95
|
+
if delivery_status_data[key].is_a?(Array)
|
96
|
+
delivery_status_data[key].map { |a| a.value }
|
97
|
+
else
|
98
|
+
delivery_status_data[key].value
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# A part may not have a header.... so, just init a body if no header
|
103
|
+
def parse_message
|
104
|
+
header_part, body_part = raw_source.split(/#{CRLF}#{WSP}*#{CRLF}/m, 2)
|
105
|
+
if header_part =~ HEADER_LINE
|
106
|
+
self.header = header_part
|
107
|
+
self.body = body_part
|
108
|
+
else
|
109
|
+
self.header = "Content-Type: text/plain\r\n"
|
110
|
+
self.body = raw_source
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def parse_delivery_status_report
|
115
|
+
@delivery_status_data ||= Header.new(body.to_s.gsub("\r\n\r\n", "\r\n"))
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Mail
|
2
|
+
class PartsList < Array
|
3
|
+
|
4
|
+
def attachments
|
5
|
+
Mail::AttachmentsList.new(self)
|
6
|
+
end
|
7
|
+
|
8
|
+
def collect
|
9
|
+
if block_given?
|
10
|
+
ary = PartsList.new
|
11
|
+
each { |o| ary << yield(o) }
|
12
|
+
ary
|
13
|
+
else
|
14
|
+
to_a
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
undef :map
|
19
|
+
alias_method :map, :collect
|
20
|
+
|
21
|
+
def map!
|
22
|
+
raise NoMethodError, "#map! is not defined, please call #collect and create a new PartsList"
|
23
|
+
end
|
24
|
+
|
25
|
+
def collect!
|
26
|
+
raise NoMethodError, "#collect! is not defined, please call #collect and create a new PartsList"
|
27
|
+
end
|
28
|
+
|
29
|
+
def sort
|
30
|
+
self.class.new(super)
|
31
|
+
end
|
32
|
+
|
33
|
+
def sort!(order)
|
34
|
+
# stable sort should be used to maintain the relative order as the parts are added
|
35
|
+
i = 0;
|
36
|
+
sorted = self.sort_by do |a|
|
37
|
+
# OK, 10000 is arbitrary... if anyone actually wants to explicitly sort 10000 parts of a
|
38
|
+
# single email message... please show me a use case and I'll put more work into this method,
|
39
|
+
# in the meantime, it works :)
|
40
|
+
[get_order_value(a, order), i += 1]
|
41
|
+
end
|
42
|
+
self.clear
|
43
|
+
sorted.each { |p| self << p }
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def get_order_value(part, order)
|
49
|
+
if part.respond_to?(:content_type) && !part[:content_type].nil?
|
50
|
+
order.index(part[:content_type].string.downcase) || 10000
|
51
|
+
else
|
52
|
+
10000
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# encoding: us-ascii
|
2
|
+
module Mail
|
3
|
+
module Patterns
|
4
|
+
white_space = %Q|\x9\x20|
|
5
|
+
text = %Q|\x1-\x8\xB\xC\xE-\x7f|
|
6
|
+
field_name = %Q|\x21-\x39\x3b-\x7e|
|
7
|
+
qp_safe = %Q|\x20-\x3c\x3e-\x7e|
|
8
|
+
|
9
|
+
aspecial = %Q|()<>[]:;@\\,."| # RFC5322
|
10
|
+
tspecial = %Q|()<>@,;:\\"/[]?=| # RFC2045
|
11
|
+
sp = %Q| |
|
12
|
+
control = %Q|\x00-\x1f\x7f-\xff|
|
13
|
+
|
14
|
+
if control.respond_to?(:force_encoding)
|
15
|
+
control = control.force_encoding(Encoding::BINARY)
|
16
|
+
end
|
17
|
+
|
18
|
+
CRLF = /\r\n/
|
19
|
+
WSP = /[#{white_space}]/
|
20
|
+
FWS = /#{CRLF}#{WSP}*/
|
21
|
+
TEXT = /[#{text}]/ # + obs-text
|
22
|
+
FIELD_NAME = /[#{field_name}]+/
|
23
|
+
FIELD_PREFIX = /\A(#{FIELD_NAME})/
|
24
|
+
FIELD_BODY = /.+/m
|
25
|
+
FIELD_LINE = /^[#{field_name}]+:\s*.+$/
|
26
|
+
FIELD_SPLIT = /^(#{FIELD_NAME})\s*:\s*(#{FIELD_BODY})?$/
|
27
|
+
HEADER_LINE = /^([#{field_name}]+:\s*.+)$/
|
28
|
+
HEADER_SPLIT = /#{CRLF}(?!#{WSP})/
|
29
|
+
|
30
|
+
QP_UNSAFE = /[^#{qp_safe}]/
|
31
|
+
QP_SAFE = /[#{qp_safe}]/
|
32
|
+
CONTROL_CHAR = /[#{control}]/n
|
33
|
+
ATOM_UNSAFE = /[#{Regexp.quote aspecial}#{control}#{sp}]/n
|
34
|
+
PHRASE_UNSAFE = /[#{Regexp.quote aspecial}#{control}]/n
|
35
|
+
TOKEN_UNSAFE = /[#{Regexp.quote tspecial}#{control}#{sp}]/n
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,225 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mail
|
3
|
+
module Utilities
|
4
|
+
include Patterns
|
5
|
+
|
6
|
+
# Returns true if the string supplied is free from characters not allowed as an ATOM
|
7
|
+
def atom_safe?( str )
|
8
|
+
not ATOM_UNSAFE === str
|
9
|
+
end
|
10
|
+
|
11
|
+
# If the string supplied has ATOM unsafe characters in it, will return the string quoted
|
12
|
+
# in double quotes, otherwise returns the string unmodified
|
13
|
+
def quote_atom( str )
|
14
|
+
atom_safe?( str ) ? str : dquote(str)
|
15
|
+
end
|
16
|
+
|
17
|
+
# If the string supplied has PHRASE unsafe characters in it, will return the string quoted
|
18
|
+
# in double quotes, otherwise returns the string unmodified
|
19
|
+
def quote_phrase( str )
|
20
|
+
if RUBY_VERSION >= '1.9'
|
21
|
+
original_encoding = str.encoding
|
22
|
+
str.force_encoding('ASCII-8BIT')
|
23
|
+
if (PHRASE_UNSAFE === str)
|
24
|
+
quoted_str = dquote(str).force_encoding(original_encoding)
|
25
|
+
str.force_encoding(original_encoding)
|
26
|
+
quoted_str
|
27
|
+
else
|
28
|
+
str.force_encoding(original_encoding)
|
29
|
+
end
|
30
|
+
else
|
31
|
+
(PHRASE_UNSAFE === str) ? dquote(str) : str
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns true if the string supplied is free from characters not allowed as a TOKEN
|
36
|
+
def token_safe?( str )
|
37
|
+
not TOKEN_UNSAFE === str
|
38
|
+
end
|
39
|
+
|
40
|
+
# If the string supplied has TOKEN unsafe characters in it, will return the string quoted
|
41
|
+
# in double quotes, otherwise returns the string unmodified
|
42
|
+
def quote_token( str )
|
43
|
+
token_safe?( str ) ? str : dquote(str)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Wraps supplied string in double quotes and applies \-escaping as necessary,
|
47
|
+
# unless it is already wrapped.
|
48
|
+
#
|
49
|
+
# Example:
|
50
|
+
#
|
51
|
+
# string = 'This is a string'
|
52
|
+
# dquote(string) #=> '"This is a string"'
|
53
|
+
#
|
54
|
+
# string = 'This is "a string"'
|
55
|
+
# dquote(string #=> '"This is \"a string\"'
|
56
|
+
def dquote( str )
|
57
|
+
'"' + unquote(str).gsub(/[\\"]/n) {|s| '\\' + s } + '"'
|
58
|
+
end
|
59
|
+
|
60
|
+
# Unwraps supplied string from inside double quotes and
|
61
|
+
# removes any \-escaping.
|
62
|
+
#
|
63
|
+
# Example:
|
64
|
+
#
|
65
|
+
# string = '"This is a string"'
|
66
|
+
# unquote(string) #=> 'This is a string'
|
67
|
+
#
|
68
|
+
# string = '"This is \"a string\""'
|
69
|
+
# unqoute(string) #=> 'This is "a string"'
|
70
|
+
def unquote( str )
|
71
|
+
if str =~ /^"(.*?)"$/
|
72
|
+
$1.gsub(/\\(.)/, '\1')
|
73
|
+
else
|
74
|
+
str
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Wraps a string in parenthesis and escapes any that are in the string itself.
|
79
|
+
#
|
80
|
+
# Example:
|
81
|
+
#
|
82
|
+
# paren( 'This is a string' ) #=> '(This is a string)'
|
83
|
+
def paren( str )
|
84
|
+
RubyVer.paren( str )
|
85
|
+
end
|
86
|
+
|
87
|
+
# Unwraps a string from being wrapped in parenthesis
|
88
|
+
#
|
89
|
+
# Example:
|
90
|
+
#
|
91
|
+
# str = '(This is a string)'
|
92
|
+
# unparen( str ) #=> 'This is a string'
|
93
|
+
def unparen( str )
|
94
|
+
match = str.match(/^\((.*?)\)$/)
|
95
|
+
match ? match[1] : str
|
96
|
+
end
|
97
|
+
|
98
|
+
# Wraps a string in angle brackets and escapes any that are in the string itself
|
99
|
+
#
|
100
|
+
# Example:
|
101
|
+
#
|
102
|
+
# bracket( 'This is a string' ) #=> '<This is a string>'
|
103
|
+
def bracket( str )
|
104
|
+
RubyVer.bracket( str )
|
105
|
+
end
|
106
|
+
|
107
|
+
# Unwraps a string from being wrapped in parenthesis
|
108
|
+
#
|
109
|
+
# Example:
|
110
|
+
#
|
111
|
+
# str = '<This is a string>'
|
112
|
+
# unbracket( str ) #=> 'This is a string'
|
113
|
+
def unbracket( str )
|
114
|
+
match = str.match(/^\<(.*?)\>$/)
|
115
|
+
match ? match[1] : str
|
116
|
+
end
|
117
|
+
|
118
|
+
# Escape parenthesies in a string
|
119
|
+
#
|
120
|
+
# Example:
|
121
|
+
#
|
122
|
+
# str = 'This is (a) string'
|
123
|
+
# escape_paren( str ) #=> 'This is \(a\) string'
|
124
|
+
def escape_paren( str )
|
125
|
+
RubyVer.escape_paren( str )
|
126
|
+
end
|
127
|
+
|
128
|
+
def uri_escape( str )
|
129
|
+
uri_parser.escape(str)
|
130
|
+
end
|
131
|
+
|
132
|
+
def uri_unescape( str )
|
133
|
+
uri_parser.unescape(str)
|
134
|
+
end
|
135
|
+
|
136
|
+
def uri_parser
|
137
|
+
@uri_parser ||= URI.const_defined?(:Parser) ? URI::Parser.new : URI
|
138
|
+
end
|
139
|
+
|
140
|
+
# Matches two objects with their to_s values case insensitively
|
141
|
+
#
|
142
|
+
# Example:
|
143
|
+
#
|
144
|
+
# obj2 = "This_is_An_object"
|
145
|
+
# obj1 = :this_IS_an_object
|
146
|
+
# match_to_s( obj1, obj2 ) #=> true
|
147
|
+
def match_to_s( obj1, obj2 )
|
148
|
+
obj1.to_s.casecmp(obj2.to_s) == 0
|
149
|
+
end
|
150
|
+
|
151
|
+
# Capitalizes a string that is joined by hyphens correctly.
|
152
|
+
#
|
153
|
+
# Example:
|
154
|
+
#
|
155
|
+
# string = 'resent-from-field'
|
156
|
+
# capitalize_field( string ) #=> 'Resent-From-Field'
|
157
|
+
def capitalize_field( str )
|
158
|
+
str.to_s.split("-").map { |v| v.capitalize }.join("-")
|
159
|
+
end
|
160
|
+
|
161
|
+
# Takes an underscored word and turns it into a class name
|
162
|
+
#
|
163
|
+
# Example:
|
164
|
+
#
|
165
|
+
# constantize("hello") #=> "Hello"
|
166
|
+
# constantize("hello-there") #=> "HelloThere"
|
167
|
+
# constantize("hello-there-mate") #=> "HelloThereMate"
|
168
|
+
def constantize( str )
|
169
|
+
str.to_s.split(/[-_]/).map { |v| v.capitalize }.to_s
|
170
|
+
end
|
171
|
+
|
172
|
+
# Swaps out all underscores (_) for hyphens (-) good for stringing from symbols
|
173
|
+
# a field name.
|
174
|
+
#
|
175
|
+
# Example:
|
176
|
+
#
|
177
|
+
# string = :resent_from_field
|
178
|
+
# dasherize ( string ) #=> 'resent_from_field'
|
179
|
+
def dasherize( str )
|
180
|
+
str.to_s.gsub('_', '-')
|
181
|
+
end
|
182
|
+
|
183
|
+
# Swaps out all hyphens (-) for underscores (_) good for stringing to symbols
|
184
|
+
# a field name.
|
185
|
+
#
|
186
|
+
# Example:
|
187
|
+
#
|
188
|
+
# string = :resent_from_field
|
189
|
+
# underscoreize ( string ) #=> 'resent_from_field'
|
190
|
+
def underscoreize( str )
|
191
|
+
str.to_s.downcase.gsub('-', '_')
|
192
|
+
end
|
193
|
+
|
194
|
+
if RUBY_VERSION <= '1.8.6'
|
195
|
+
|
196
|
+
def map_lines( str, &block )
|
197
|
+
results = []
|
198
|
+
str.each_line do |line|
|
199
|
+
results << yield(line)
|
200
|
+
end
|
201
|
+
results
|
202
|
+
end
|
203
|
+
|
204
|
+
def map_with_index( enum, &block )
|
205
|
+
results = []
|
206
|
+
enum.each_with_index do |token, i|
|
207
|
+
results[i] = yield(token, i)
|
208
|
+
end
|
209
|
+
results
|
210
|
+
end
|
211
|
+
|
212
|
+
else
|
213
|
+
|
214
|
+
def map_lines( str, &block )
|
215
|
+
str.each_line.map(&block)
|
216
|
+
end
|
217
|
+
|
218
|
+
def map_with_index( enum, &block )
|
219
|
+
enum.each_with_index.map(&block)
|
220
|
+
end
|
221
|
+
|
222
|
+
end
|
223
|
+
|
224
|
+
end
|
225
|
+
end
|