nano-fast-tool 0.0.1
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/mail-2.9.1/MIT-LICENSE +20 -0
- data/mail-2.9.1/README.md +771 -0
- data/mail-2.9.1/lib/mail/attachments_list.rb +109 -0
- data/mail-2.9.1/lib/mail/body.rb +305 -0
- data/mail-2.9.1/lib/mail/configuration.rb +78 -0
- data/mail-2.9.1/lib/mail/constants.rb +79 -0
- data/mail-2.9.1/lib/mail/elements/address.rb +258 -0
- data/mail-2.9.1/lib/mail/elements/address_list.rb +34 -0
- data/mail-2.9.1/lib/mail/elements/content_disposition_element.rb +20 -0
- data/mail-2.9.1/lib/mail/elements/content_location_element.rb +17 -0
- data/mail-2.9.1/lib/mail/elements/content_transfer_encoding_element.rb +13 -0
- data/mail-2.9.1/lib/mail/elements/content_type_element.rb +25 -0
- data/mail-2.9.1/lib/mail/elements/date_time_element.rb +15 -0
- data/mail-2.9.1/lib/mail/elements/envelope_from_element.rb +39 -0
- data/mail-2.9.1/lib/mail/elements/message_ids_element.rb +31 -0
- data/mail-2.9.1/lib/mail/elements/mime_version_element.rb +15 -0
- data/mail-2.9.1/lib/mail/elements/phrase_list.rb +19 -0
- data/mail-2.9.1/lib/mail/elements/received_element.rb +35 -0
- data/mail-2.9.1/lib/mail/elements.rb +15 -0
- data/mail-2.9.1/lib/mail/encodings/7bit.rb +27 -0
- data/mail-2.9.1/lib/mail/encodings/8bit.rb +18 -0
- data/mail-2.9.1/lib/mail/encodings/base64.rb +38 -0
- data/mail-2.9.1/lib/mail/encodings/binary.rb +13 -0
- data/mail-2.9.1/lib/mail/encodings/identity.rb +24 -0
- data/mail-2.9.1/lib/mail/encodings/quoted_printable.rb +45 -0
- data/mail-2.9.1/lib/mail/encodings/transfer_encoding.rb +77 -0
- data/mail-2.9.1/lib/mail/encodings/unix_to_unix.rb +20 -0
- data/mail-2.9.1/lib/mail/encodings.rb +314 -0
- data/mail-2.9.1/lib/mail/envelope.rb +28 -0
- data/mail-2.9.1/lib/mail/field.rb +313 -0
- data/mail-2.9.1/lib/mail/field_list.rb +87 -0
- data/mail-2.9.1/lib/mail/fields/bcc_field.rb +49 -0
- data/mail-2.9.1/lib/mail/fields/cc_field.rb +33 -0
- data/mail-2.9.1/lib/mail/fields/comments_field.rb +31 -0
- data/mail-2.9.1/lib/mail/fields/common_address_field.rb +162 -0
- data/mail-2.9.1/lib/mail/fields/common_date_field.rb +56 -0
- data/mail-2.9.1/lib/mail/fields/common_field.rb +77 -0
- data/mail-2.9.1/lib/mail/fields/common_message_id_field.rb +41 -0
- data/mail-2.9.1/lib/mail/fields/content_description_field.rb +12 -0
- data/mail-2.9.1/lib/mail/fields/content_disposition_field.rb +44 -0
- data/mail-2.9.1/lib/mail/fields/content_id_field.rb +35 -0
- data/mail-2.9.1/lib/mail/fields/content_location_field.rb +28 -0
- data/mail-2.9.1/lib/mail/fields/content_transfer_encoding_field.rb +44 -0
- data/mail-2.9.1/lib/mail/fields/content_type_field.rb +171 -0
- data/mail-2.9.1/lib/mail/fields/date_field.rb +28 -0
- data/mail-2.9.1/lib/mail/fields/from_field.rb +33 -0
- data/mail-2.9.1/lib/mail/fields/in_reply_to_field.rb +45 -0
- data/mail-2.9.1/lib/mail/fields/keywords_field.rb +30 -0
- data/mail-2.9.1/lib/mail/fields/message_id_field.rb +36 -0
- data/mail-2.9.1/lib/mail/fields/mime_version_field.rb +42 -0
- data/mail-2.9.1/lib/mail/fields/named_structured_field.rb +10 -0
- data/mail-2.9.1/lib/mail/fields/named_unstructured_field.rb +10 -0
- data/mail-2.9.1/lib/mail/fields/optional_field.rb +15 -0
- data/mail-2.9.1/lib/mail/fields/parameter_hash.rb +61 -0
- data/mail-2.9.1/lib/mail/fields/received_field.rb +61 -0
- data/mail-2.9.1/lib/mail/fields/references_field.rb +42 -0
- data/mail-2.9.1/lib/mail/fields/reply_to_field.rb +33 -0
- data/mail-2.9.1/lib/mail/fields/resent_bcc_field.rb +33 -0
- data/mail-2.9.1/lib/mail/fields/resent_cc_field.rb +33 -0
- data/mail-2.9.1/lib/mail/fields/resent_date_field.rb +10 -0
- data/mail-2.9.1/lib/mail/fields/resent_from_field.rb +33 -0
- data/mail-2.9.1/lib/mail/fields/resent_message_id_field.rb +10 -0
- data/mail-2.9.1/lib/mail/fields/resent_sender_field.rb +32 -0
- data/mail-2.9.1/lib/mail/fields/resent_to_field.rb +33 -0
- data/mail-2.9.1/lib/mail/fields/return_path_field.rb +60 -0
- data/mail-2.9.1/lib/mail/fields/sender_field.rb +45 -0
- data/mail-2.9.1/lib/mail/fields/structured_field.rb +24 -0
- data/mail-2.9.1/lib/mail/fields/subject_field.rb +14 -0
- data/mail-2.9.1/lib/mail/fields/to_field.rb +33 -0
- data/mail-2.9.1/lib/mail/fields/unstructured_field.rb +189 -0
- data/mail-2.9.1/lib/mail/fields.rb +45 -0
- data/mail-2.9.1/lib/mail/header.rb +237 -0
- data/mail-2.9.1/lib/mail/indifferent_hash.rb +147 -0
- data/mail-2.9.1/lib/mail/mail.rb +267 -0
- data/mail-2.9.1/lib/mail/matchers/attachment_matchers.rb +44 -0
- data/mail-2.9.1/lib/mail/matchers/has_sent_mail.rb +201 -0
- data/mail-2.9.1/lib/mail/message.rb +2159 -0
- data/mail-2.9.1/lib/mail/multibyte/chars.rb +318 -0
- data/mail-2.9.1/lib/mail/multibyte/unicode.rb +405 -0
- data/mail-2.9.1/lib/mail/multibyte/utils.rb +44 -0
- data/mail-2.9.1/lib/mail/multibyte.rb +82 -0
- data/mail-2.9.1/lib/mail/network/delivery_methods/exim.rb +50 -0
- data/mail-2.9.1/lib/mail/network/delivery_methods/file_delivery.rb +43 -0
- data/mail-2.9.1/lib/mail/network/delivery_methods/logger_delivery.rb +34 -0
- data/mail-2.9.1/lib/mail/network/delivery_methods/sendmail.rb +87 -0
- data/mail-2.9.1/lib/mail/network/delivery_methods/smtp.rb +200 -0
- data/mail-2.9.1/lib/mail/network/delivery_methods/smtp_connection.rb +57 -0
- data/mail-2.9.1/lib/mail/network/delivery_methods/test_mailer.rb +44 -0
- data/mail-2.9.1/lib/mail/network/retriever_methods/base.rb +64 -0
- data/mail-2.9.1/lib/mail/network/retriever_methods/imap.rb +190 -0
- data/mail-2.9.1/lib/mail/network/retriever_methods/pop3.rb +143 -0
- data/mail-2.9.1/lib/mail/network/retriever_methods/test_retriever.rb +45 -0
- data/mail-2.9.1/lib/mail/network.rb +16 -0
- data/mail-2.9.1/lib/mail/parser_tools.rb +15 -0
- data/mail-2.9.1/lib/mail/parsers/address_lists_parser.rb +33245 -0
- data/mail-2.9.1/lib/mail/parsers/address_lists_parser.rl +183 -0
- data/mail-2.9.1/lib/mail/parsers/content_disposition_parser.rb +904 -0
- data/mail-2.9.1/lib/mail/parsers/content_disposition_parser.rl +93 -0
- data/mail-2.9.1/lib/mail/parsers/content_location_parser.rb +825 -0
- data/mail-2.9.1/lib/mail/parsers/content_location_parser.rl +82 -0
- data/mail-2.9.1/lib/mail/parsers/content_transfer_encoding_parser.rb +525 -0
- data/mail-2.9.1/lib/mail/parsers/content_transfer_encoding_parser.rl +75 -0
- data/mail-2.9.1/lib/mail/parsers/content_type_parser.rb +1051 -0
- data/mail-2.9.1/lib/mail/parsers/content_type_parser.rl +94 -0
- data/mail-2.9.1/lib/mail/parsers/date_time_parser.rb +894 -0
- data/mail-2.9.1/lib/mail/parsers/date_time_parser.rl +73 -0
- data/mail-2.9.1/lib/mail/parsers/envelope_from_parser.rb +3678 -0
- data/mail-2.9.1/lib/mail/parsers/envelope_from_parser.rl +93 -0
- data/mail-2.9.1/lib/mail/parsers/message_ids_parser.rb +5164 -0
- data/mail-2.9.1/lib/mail/parsers/message_ids_parser.rl +97 -0
- data/mail-2.9.1/lib/mail/parsers/mime_version_parser.rb +516 -0
- data/mail-2.9.1/lib/mail/parsers/mime_version_parser.rl +72 -0
- data/mail-2.9.1/lib/mail/parsers/phrase_lists_parser.rb +887 -0
- data/mail-2.9.1/lib/mail/parsers/phrase_lists_parser.rl +94 -0
- data/mail-2.9.1/lib/mail/parsers/received_parser.rb +8785 -0
- data/mail-2.9.1/lib/mail/parsers/received_parser.rl +95 -0
- data/mail-2.9.1/lib/mail/parsers/rfc2045_content_transfer_encoding.rl +13 -0
- data/mail-2.9.1/lib/mail/parsers/rfc2045_content_type.rl +25 -0
- data/mail-2.9.1/lib/mail/parsers/rfc2045_mime.rl +16 -0
- data/mail-2.9.1/lib/mail/parsers/rfc2183_content_disposition.rl +15 -0
- data/mail-2.9.1/lib/mail/parsers/rfc3629_utf8.rl +19 -0
- data/mail-2.9.1/lib/mail/parsers/rfc5234_abnf_core_rules.rl +22 -0
- data/mail-2.9.1/lib/mail/parsers/rfc5322.rl +74 -0
- data/mail-2.9.1/lib/mail/parsers/rfc5322_address.rl +72 -0
- data/mail-2.9.1/lib/mail/parsers/rfc5322_date_time.rl +37 -0
- data/mail-2.9.1/lib/mail/parsers/rfc5322_lexical_tokens.rl +60 -0
- data/mail-2.9.1/lib/mail/parsers.rb +13 -0
- data/mail-2.9.1/lib/mail/part.rb +119 -0
- data/mail-2.9.1/lib/mail/parts_list.rb +131 -0
- data/mail-2.9.1/lib/mail/smtp_envelope.rb +57 -0
- data/mail-2.9.1/lib/mail/utilities.rb +576 -0
- data/mail-2.9.1/lib/mail/values/unicode_tables.dat +0 -0
- data/mail-2.9.1/lib/mail/version.rb +17 -0
- data/mail-2.9.1/lib/mail/yaml.rb +30 -0
- data/mail-2.9.1/lib/mail.rb +65 -0
- data/nano-fast-tool.gemspec +12 -0
- metadata +177 -0
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Mail
|
|
3
|
+
module Multibyte
|
|
4
|
+
module Unicode
|
|
5
|
+
# Adapted from https://github.com/rails/rails/blob/master/activesupport/lib/active_support/multibyte/unicode.rb
|
|
6
|
+
# under the MIT license
|
|
7
|
+
# The Unicode version that is supported by the implementation
|
|
8
|
+
UNICODE_VERSION = '7.0.0'
|
|
9
|
+
|
|
10
|
+
# Holds data about a codepoint in the Unicode database.
|
|
11
|
+
class Codepoint
|
|
12
|
+
attr_accessor :code, :combining_class, :decomp_type, :decomp_mapping, :uppercase_mapping, :lowercase_mapping
|
|
13
|
+
|
|
14
|
+
# Initializing Codepoint object with default values
|
|
15
|
+
def initialize
|
|
16
|
+
@combining_class = 0
|
|
17
|
+
@uppercase_mapping = 0
|
|
18
|
+
@lowercase_mapping = 0
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def swapcase_mapping
|
|
22
|
+
uppercase_mapping > 0 ? uppercase_mapping : lowercase_mapping
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
extend self
|
|
27
|
+
|
|
28
|
+
# A list of all available normalization forms. See http://www.unicode.org/reports/tr15/tr15-29.html for more
|
|
29
|
+
# information about normalization.
|
|
30
|
+
NORMALIZATION_FORMS = [:c, :kc, :d, :kd]
|
|
31
|
+
|
|
32
|
+
# The default normalization used for operations that require normalization. It can be set to any of the
|
|
33
|
+
# normalizations in NORMALIZATION_FORMS.
|
|
34
|
+
#
|
|
35
|
+
# Example:
|
|
36
|
+
# Mail::Multibyte::Unicode.default_normalization_form = :c
|
|
37
|
+
attr_accessor :default_normalization_form
|
|
38
|
+
@default_normalization_form = :kc
|
|
39
|
+
|
|
40
|
+
# Hangul character boundaries and properties
|
|
41
|
+
HANGUL_SBASE = 0xAC00
|
|
42
|
+
HANGUL_LBASE = 0x1100
|
|
43
|
+
HANGUL_VBASE = 0x1161
|
|
44
|
+
HANGUL_TBASE = 0x11A7
|
|
45
|
+
HANGUL_LCOUNT = 19
|
|
46
|
+
HANGUL_VCOUNT = 21
|
|
47
|
+
HANGUL_TCOUNT = 28
|
|
48
|
+
HANGUL_NCOUNT = HANGUL_VCOUNT * HANGUL_TCOUNT
|
|
49
|
+
HANGUL_SCOUNT = 11172
|
|
50
|
+
HANGUL_SLAST = HANGUL_SBASE + HANGUL_SCOUNT
|
|
51
|
+
HANGUL_JAMO_FIRST = 0x1100
|
|
52
|
+
HANGUL_JAMO_LAST = 0x11FF
|
|
53
|
+
|
|
54
|
+
# All the unicode whitespace
|
|
55
|
+
WHITESPACE = [
|
|
56
|
+
(0x0009..0x000D).to_a, # White_Space # Cc [5] <control-0009>..<control-000D>
|
|
57
|
+
0x0020, # White_Space # Zs SPACE
|
|
58
|
+
0x0085, # White_Space # Cc <control-0085>
|
|
59
|
+
0x00A0, # White_Space # Zs NO-BREAK SPACE
|
|
60
|
+
0x1680, # White_Space # Zs OGHAM SPACE MARK
|
|
61
|
+
0x180E, # White_Space # Zs MONGOLIAN VOWEL SEPARATOR
|
|
62
|
+
(0x2000..0x200A).to_a, # White_Space # Zs [11] EN QUAD..HAIR SPACE
|
|
63
|
+
0x2028, # White_Space # Zl LINE SEPARATOR
|
|
64
|
+
0x2029, # White_Space # Zp PARAGRAPH SEPARATOR
|
|
65
|
+
0x202F, # White_Space # Zs NARROW NO-BREAK SPACE
|
|
66
|
+
0x205F, # White_Space # Zs MEDIUM MATHEMATICAL SPACE
|
|
67
|
+
0x3000, # White_Space # Zs IDEOGRAPHIC SPACE
|
|
68
|
+
].flatten.freeze
|
|
69
|
+
|
|
70
|
+
# BOM (byte order mark) can also be seen as whitespace, it's a non-rendering character used to distinguish
|
|
71
|
+
# between little and big endian. This is not an issue in utf-8, so it must be ignored.
|
|
72
|
+
LEADERS_AND_TRAILERS = WHITESPACE + [65279] # ZERO-WIDTH NO-BREAK SPACE aka BOM
|
|
73
|
+
|
|
74
|
+
# Returns a regular expression pattern that matches the passed Unicode codepoints
|
|
75
|
+
def self.codepoints_to_pattern(array_of_codepoints) #:nodoc:
|
|
76
|
+
array_of_codepoints.collect{ |e| [e].pack 'U*' }.join('|')
|
|
77
|
+
end
|
|
78
|
+
TRAILERS_PAT = /(#{codepoints_to_pattern(LEADERS_AND_TRAILERS)})+\Z/u
|
|
79
|
+
LEADERS_PAT = /\A(#{codepoints_to_pattern(LEADERS_AND_TRAILERS)})+/u
|
|
80
|
+
|
|
81
|
+
# Unpack the string at codepoints boundaries. Raises an EncodingError when the encoding of the string isn't
|
|
82
|
+
# valid UTF-8.
|
|
83
|
+
#
|
|
84
|
+
# Example:
|
|
85
|
+
# Unicode.u_unpack('Café') # => [67, 97, 102, 233]
|
|
86
|
+
def u_unpack(string)
|
|
87
|
+
begin
|
|
88
|
+
string.unpack 'U*'
|
|
89
|
+
rescue ArgumentError
|
|
90
|
+
raise EncodingError, 'malformed UTF-8 character'
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Detect whether the codepoint is in a certain character class. Returns +true+ when it's in the specified
|
|
95
|
+
# character class and +false+ otherwise. Valid character classes are: <tt>:cr</tt>, <tt>:lf</tt>, <tt>:l</tt>,
|
|
96
|
+
# <tt>:v</tt>, <tt>:lv</tt>, <tt>:lvt</tt> and <tt>:t</tt>.
|
|
97
|
+
#
|
|
98
|
+
# Primarily used by the grapheme cluster support.
|
|
99
|
+
def in_char_class?(codepoint, classes)
|
|
100
|
+
classes.detect { |c| database.boundary[c] === codepoint } ? true : false
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Unpack the string at grapheme boundaries. Returns a list of character lists.
|
|
104
|
+
#
|
|
105
|
+
# Example:
|
|
106
|
+
# Unicode.g_unpack('क्षि') # => [[2325, 2381], [2359], [2367]]
|
|
107
|
+
# Unicode.g_unpack('Café') # => [[67], [97], [102], [233]]
|
|
108
|
+
def g_unpack(string)
|
|
109
|
+
codepoints = u_unpack(string)
|
|
110
|
+
unpacked = []
|
|
111
|
+
pos = 0
|
|
112
|
+
marker = 0
|
|
113
|
+
eoc = codepoints.length
|
|
114
|
+
while(pos < eoc)
|
|
115
|
+
pos += 1
|
|
116
|
+
previous = codepoints[pos-1]
|
|
117
|
+
current = codepoints[pos]
|
|
118
|
+
if (
|
|
119
|
+
# CR X LF
|
|
120
|
+
( previous == database.boundary[:cr] and current == database.boundary[:lf] ) or
|
|
121
|
+
# L X (L|V|LV|LVT)
|
|
122
|
+
( database.boundary[:l] === previous and in_char_class?(current, [:l,:v,:lv,:lvt]) ) or
|
|
123
|
+
# (LV|V) X (V|T)
|
|
124
|
+
( in_char_class?(previous, [:lv,:v]) and in_char_class?(current, [:v,:t]) ) or
|
|
125
|
+
# (LVT|T) X (T)
|
|
126
|
+
( in_char_class?(previous, [:lvt,:t]) and database.boundary[:t] === current ) or
|
|
127
|
+
# X Extend
|
|
128
|
+
(database.boundary[:extend] === current)
|
|
129
|
+
)
|
|
130
|
+
else
|
|
131
|
+
unpacked << codepoints[marker..pos-1]
|
|
132
|
+
marker = pos
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
unpacked
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Reverse operation of g_unpack.
|
|
139
|
+
#
|
|
140
|
+
# Example:
|
|
141
|
+
# Unicode.g_pack(Unicode.g_unpack('क्षि')) # => 'क्षि'
|
|
142
|
+
def g_pack(unpacked)
|
|
143
|
+
(unpacked.flatten).pack('U*')
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Re-order codepoints so the string becomes canonical.
|
|
147
|
+
def reorder_characters(codepoints)
|
|
148
|
+
length = codepoints.length- 1
|
|
149
|
+
pos = 0
|
|
150
|
+
while pos < length do
|
|
151
|
+
cp1, cp2 = database.codepoints[codepoints[pos]], database.codepoints[codepoints[pos+1]]
|
|
152
|
+
if (cp1.combining_class > cp2.combining_class) && (cp2.combining_class > 0)
|
|
153
|
+
codepoints[pos..pos+1] = cp2.code, cp1.code
|
|
154
|
+
pos += (pos > 0 ? -1 : 1)
|
|
155
|
+
else
|
|
156
|
+
pos += 1
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
codepoints
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Decompose composed characters to the decomposed form.
|
|
163
|
+
def decompose_codepoints(type, codepoints)
|
|
164
|
+
codepoints.inject([]) do |decomposed, cp|
|
|
165
|
+
# if it's a hangul syllable starter character
|
|
166
|
+
if HANGUL_SBASE <= cp and cp < HANGUL_SLAST
|
|
167
|
+
sindex = cp - HANGUL_SBASE
|
|
168
|
+
ncp = [] # new codepoints
|
|
169
|
+
ncp << HANGUL_LBASE + sindex / HANGUL_NCOUNT
|
|
170
|
+
ncp << HANGUL_VBASE + (sindex % HANGUL_NCOUNT) / HANGUL_TCOUNT
|
|
171
|
+
tindex = sindex % HANGUL_TCOUNT
|
|
172
|
+
ncp << (HANGUL_TBASE + tindex) unless tindex == 0
|
|
173
|
+
decomposed.concat ncp
|
|
174
|
+
# if the codepoint is decomposable in with the current decomposition type
|
|
175
|
+
elsif (ncp = database.codepoints[cp].decomp_mapping) and (!database.codepoints[cp].decomp_type || type == :compatability)
|
|
176
|
+
decomposed.concat decompose_codepoints(type, ncp.dup)
|
|
177
|
+
else
|
|
178
|
+
decomposed << cp
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Compose decomposed characters to the composed form.
|
|
184
|
+
def compose_codepoints(codepoints)
|
|
185
|
+
pos = 0
|
|
186
|
+
eoa = codepoints.length - 1
|
|
187
|
+
starter_pos = 0
|
|
188
|
+
starter_char = codepoints[0]
|
|
189
|
+
previous_combining_class = -1
|
|
190
|
+
while pos < eoa
|
|
191
|
+
pos += 1
|
|
192
|
+
lindex = starter_char - HANGUL_LBASE
|
|
193
|
+
# -- Hangul
|
|
194
|
+
if 0 <= lindex and lindex < HANGUL_LCOUNT
|
|
195
|
+
vindex = codepoints[starter_pos+1] - HANGUL_VBASE rescue vindex = -1
|
|
196
|
+
if 0 <= vindex and vindex < HANGUL_VCOUNT
|
|
197
|
+
tindex = codepoints[starter_pos+2] - HANGUL_TBASE rescue tindex = -1
|
|
198
|
+
if 0 <= tindex and tindex < HANGUL_TCOUNT
|
|
199
|
+
j = starter_pos + 2
|
|
200
|
+
eoa -= 2
|
|
201
|
+
else
|
|
202
|
+
tindex = 0
|
|
203
|
+
j = starter_pos + 1
|
|
204
|
+
eoa -= 1
|
|
205
|
+
end
|
|
206
|
+
codepoints[starter_pos..j] = (lindex * HANGUL_VCOUNT + vindex) * HANGUL_TCOUNT + tindex + HANGUL_SBASE
|
|
207
|
+
end
|
|
208
|
+
starter_pos += 1
|
|
209
|
+
starter_char = codepoints[starter_pos]
|
|
210
|
+
# -- Other characters
|
|
211
|
+
else
|
|
212
|
+
current_char = codepoints[pos]
|
|
213
|
+
current = database.codepoints[current_char]
|
|
214
|
+
if current.combining_class > previous_combining_class
|
|
215
|
+
if ref = database.composition_map[starter_char]
|
|
216
|
+
composition = ref[current_char]
|
|
217
|
+
else
|
|
218
|
+
composition = nil
|
|
219
|
+
end
|
|
220
|
+
unless composition.nil?
|
|
221
|
+
codepoints[starter_pos] = composition
|
|
222
|
+
starter_char = composition
|
|
223
|
+
codepoints.delete_at pos
|
|
224
|
+
eoa -= 1
|
|
225
|
+
pos -= 1
|
|
226
|
+
previous_combining_class = -1
|
|
227
|
+
else
|
|
228
|
+
previous_combining_class = current.combining_class
|
|
229
|
+
end
|
|
230
|
+
else
|
|
231
|
+
previous_combining_class = current.combining_class
|
|
232
|
+
end
|
|
233
|
+
if current.combining_class == 0
|
|
234
|
+
starter_pos = pos
|
|
235
|
+
starter_char = codepoints[pos]
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
codepoints
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent resulting in a valid UTF-8 string.
|
|
243
|
+
#
|
|
244
|
+
# Passing +true+ will forcibly tidy all bytes, assuming that the string's encoding is entirely CP1252 or ISO-8859-1.
|
|
245
|
+
def tidy_bytes(string, force = false)
|
|
246
|
+
if force
|
|
247
|
+
return string.unpack("C*").map do |b|
|
|
248
|
+
tidy_byte(b)
|
|
249
|
+
end.flatten.compact.pack("C*").unpack("U*").pack("U*")
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
bytes = string.unpack("C*")
|
|
253
|
+
conts_expected = 0
|
|
254
|
+
last_lead = 0
|
|
255
|
+
|
|
256
|
+
bytes.each_index do |i|
|
|
257
|
+
|
|
258
|
+
byte = bytes[i]
|
|
259
|
+
is_cont = byte > 127 && byte < 192
|
|
260
|
+
is_lead = byte > 191 && byte < 245
|
|
261
|
+
is_unused = byte > 240
|
|
262
|
+
is_restricted = byte > 244
|
|
263
|
+
|
|
264
|
+
# Impossible or highly unlikely byte? Clean it.
|
|
265
|
+
if is_unused || is_restricted
|
|
266
|
+
bytes[i] = tidy_byte(byte)
|
|
267
|
+
elsif is_cont
|
|
268
|
+
# Not expecting contination byte? Clean up. Otherwise, now expect one less.
|
|
269
|
+
conts_expected == 0 ? bytes[i] = tidy_byte(byte) : conts_expected -= 1
|
|
270
|
+
else
|
|
271
|
+
if conts_expected > 0
|
|
272
|
+
# Expected continuation, but got ASCII or leading? Clean backwards up to
|
|
273
|
+
# the leading byte.
|
|
274
|
+
(1..(i - last_lead)).each {|j| bytes[i - j] = tidy_byte(bytes[i - j])}
|
|
275
|
+
conts_expected = 0
|
|
276
|
+
end
|
|
277
|
+
if is_lead
|
|
278
|
+
# Final byte is leading? Clean it.
|
|
279
|
+
if i == bytes.length - 1
|
|
280
|
+
bytes[i] = tidy_byte(bytes.last)
|
|
281
|
+
else
|
|
282
|
+
# Valid leading byte? Expect continuations determined by position of
|
|
283
|
+
# first zero bit, with max of 3.
|
|
284
|
+
conts_expected = byte < 224 ? 1 : byte < 240 ? 2 : 3
|
|
285
|
+
last_lead = i
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
bytes.empty? ? "" : bytes.flatten.compact.pack("C*").unpack("U*").pack("U*")
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
# Returns the KC normalization of the string by default. NFKC is considered the best normalization form for
|
|
294
|
+
# passing strings to databases and validations.
|
|
295
|
+
#
|
|
296
|
+
# * <tt>string</tt> - The string to perform normalization on.
|
|
297
|
+
# * <tt>form</tt> - The form you want to normalize in. Should be one of the following:
|
|
298
|
+
# <tt>:c</tt>, <tt>:kc</tt>, <tt>:d</tt>, or <tt>:kd</tt>. Default is
|
|
299
|
+
# Mail::Multibyte.default_normalization_form
|
|
300
|
+
def normalize(string, form=nil)
|
|
301
|
+
form ||= @default_normalization_form
|
|
302
|
+
# See http://www.unicode.org/reports/tr15, Table 1
|
|
303
|
+
codepoints = u_unpack(string)
|
|
304
|
+
case form
|
|
305
|
+
when :d
|
|
306
|
+
reorder_characters(decompose_codepoints(:canonical, codepoints))
|
|
307
|
+
when :c
|
|
308
|
+
compose_codepoints(reorder_characters(decompose_codepoints(:canonical, codepoints)))
|
|
309
|
+
when :kd
|
|
310
|
+
reorder_characters(decompose_codepoints(:compatability, codepoints))
|
|
311
|
+
when :kc
|
|
312
|
+
compose_codepoints(reorder_characters(decompose_codepoints(:compatability, codepoints)))
|
|
313
|
+
else
|
|
314
|
+
raise ArgumentError, "#{form} is not a valid normalization variant", caller
|
|
315
|
+
end.pack('U*')
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
def apply_mapping(string, mapping) #:nodoc:
|
|
319
|
+
u_unpack(string).map do |codepoint|
|
|
320
|
+
cp = database.codepoints[codepoint]
|
|
321
|
+
if cp and (ncp = cp.send(mapping)) and ncp > 0
|
|
322
|
+
ncp
|
|
323
|
+
else
|
|
324
|
+
codepoint
|
|
325
|
+
end
|
|
326
|
+
end.pack('U*')
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
# Holds static data from the Unicode database
|
|
330
|
+
class UnicodeDatabase
|
|
331
|
+
ATTRIBUTES = :codepoints, :composition_exclusion, :composition_map, :boundary, :cp1252
|
|
332
|
+
|
|
333
|
+
attr_writer(*ATTRIBUTES)
|
|
334
|
+
|
|
335
|
+
def initialize
|
|
336
|
+
@codepoints = Hash.new(Codepoint.new)
|
|
337
|
+
@composition_exclusion = []
|
|
338
|
+
@composition_map = {}
|
|
339
|
+
@boundary = {}
|
|
340
|
+
@cp1252 = {}
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
# Lazy load the Unicode database so it's only loaded when it's actually used
|
|
344
|
+
ATTRIBUTES.each do |attr_name|
|
|
345
|
+
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
|
346
|
+
def #{attr_name} # def codepoints
|
|
347
|
+
load # load
|
|
348
|
+
@#{attr_name} # @codepoints
|
|
349
|
+
end # end
|
|
350
|
+
EOS
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
# Loads the Unicode database and returns all the internal objects of UnicodeDatabase.
|
|
354
|
+
def load
|
|
355
|
+
begin
|
|
356
|
+
@codepoints, @composition_exclusion, @composition_map, @boundary, @cp1252 = File.open(self.class.filename, 'rb') { |f| Marshal.load f.read }
|
|
357
|
+
rescue => e
|
|
358
|
+
raise IOError.new("Couldn't load the Unicode tables for UTF8Handler (#{e.message}), Mail::Multibyte is unusable")
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
# Redefine the === method so we can write shorter rules for grapheme cluster breaks
|
|
362
|
+
@boundary.each do |k,_|
|
|
363
|
+
@boundary[k].instance_eval do
|
|
364
|
+
def ===(other)
|
|
365
|
+
detect { |i| i === other } ? true : false
|
|
366
|
+
end
|
|
367
|
+
end if @boundary[k].kind_of?(Array)
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
# define attr_reader methods for the instance variables
|
|
371
|
+
class << self
|
|
372
|
+
attr_reader(*ATTRIBUTES)
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
# Returns the directory in which the data files are stored
|
|
377
|
+
def self.dirname
|
|
378
|
+
__dir__ + '/../values/'
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
# Returns the filename for the data file for this version
|
|
382
|
+
def self.filename
|
|
383
|
+
File.expand_path File.join(dirname, "unicode_tables.dat")
|
|
384
|
+
end
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
private
|
|
388
|
+
|
|
389
|
+
def tidy_byte(byte)
|
|
390
|
+
if byte < 160
|
|
391
|
+
[database.cp1252[byte] || byte].pack("U").unpack("C*")
|
|
392
|
+
elsif byte < 192
|
|
393
|
+
[194, byte]
|
|
394
|
+
else
|
|
395
|
+
[195, byte - 64]
|
|
396
|
+
end
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
def database
|
|
400
|
+
@database ||= UnicodeDatabase.new
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Mail #:nodoc:
|
|
5
|
+
module Multibyte #:nodoc:
|
|
6
|
+
# Returns a regular expression that matches valid characters in the current encoding
|
|
7
|
+
def self.valid_character
|
|
8
|
+
VALID_CHARACTER[Encoding.default_external.to_s]
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Returns true if string has valid utf-8 encoding
|
|
12
|
+
def self.is_utf8?(string)
|
|
13
|
+
case string.encoding
|
|
14
|
+
when Encoding::UTF_8
|
|
15
|
+
verify(string)
|
|
16
|
+
when Encoding::ASCII_8BIT, Encoding::US_ASCII
|
|
17
|
+
verify(to_utf8(string))
|
|
18
|
+
else
|
|
19
|
+
false
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Verifies the encoding of a string
|
|
24
|
+
def self.verify(string)
|
|
25
|
+
string.valid_encoding?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Verifies the encoding of the string and raises an exception when it's not valid
|
|
29
|
+
def self.verify!(string)
|
|
30
|
+
raise EncodingError.new("Found characters with invalid encoding") unless verify(string)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Removes all invalid characters from the string.
|
|
34
|
+
#
|
|
35
|
+
# Note: this method is a no-op in Ruby 1.9
|
|
36
|
+
def self.clean(string)
|
|
37
|
+
string
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.to_utf8(string)
|
|
41
|
+
string.dup.force_encoding(Encoding::UTF_8)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
require 'mail/multibyte/chars'
|
|
4
|
+
|
|
5
|
+
module Mail #:nodoc:
|
|
6
|
+
module Multibyte
|
|
7
|
+
# Raised when a problem with the encoding was found.
|
|
8
|
+
class EncodingError < StandardError; end
|
|
9
|
+
|
|
10
|
+
class << self
|
|
11
|
+
# The proxy class returned when calling mb_chars. You can use this accessor to configure your own proxy
|
|
12
|
+
# class so you can support other encodings. See the Mail::Multibyte::Chars implementation for
|
|
13
|
+
# an example how to do this.
|
|
14
|
+
#
|
|
15
|
+
# Example:
|
|
16
|
+
# Mail::Multibyte.proxy_class = CharsForUTF32
|
|
17
|
+
attr_accessor :proxy_class
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
self.proxy_class = Mail::Multibyte::Chars
|
|
21
|
+
|
|
22
|
+
# == Multibyte proxy
|
|
23
|
+
#
|
|
24
|
+
# +mb_chars+ is a multibyte safe proxy for string methods.
|
|
25
|
+
#
|
|
26
|
+
# In Ruby 1.8 and older it creates and returns an instance of the Mail::Multibyte::Chars class which
|
|
27
|
+
# encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy
|
|
28
|
+
# class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsuled string.
|
|
29
|
+
#
|
|
30
|
+
# name = 'Claus Müller'
|
|
31
|
+
# name.reverse # => "rell??M sualC"
|
|
32
|
+
# name.length # => 13
|
|
33
|
+
#
|
|
34
|
+
# name.mb_chars.reverse.to_s # => "rellüM sualC"
|
|
35
|
+
# name.mb_chars.length # => 12
|
|
36
|
+
#
|
|
37
|
+
# In Ruby 1.9 and newer +mb_chars+ returns +self+ because String is (mostly) encoding aware. This means that
|
|
38
|
+
# it becomes easy to run one version of your code on multiple Ruby versions.
|
|
39
|
+
#
|
|
40
|
+
# == Method chaining
|
|
41
|
+
#
|
|
42
|
+
# All the methods on the Chars proxy which normally return a string will return a Chars object. This allows
|
|
43
|
+
# method chaining on the result of any of these methods.
|
|
44
|
+
#
|
|
45
|
+
# name.mb_chars.reverse.length # => 12
|
|
46
|
+
#
|
|
47
|
+
# == Interoperability and configuration
|
|
48
|
+
#
|
|
49
|
+
# The Chars object tries to be as interchangeable with String objects as possible: sorting and comparing between
|
|
50
|
+
# String and Char work like expected. The bang! methods change the internal string representation in the Chars
|
|
51
|
+
# object. Interoperability problems can be resolved easily with a +to_s+ call.
|
|
52
|
+
#
|
|
53
|
+
# For more information about the methods defined on the Chars proxy see Mail::Multibyte::Chars. For
|
|
54
|
+
# information about how to change the default Multibyte behaviour see Mail::Multibyte.
|
|
55
|
+
def self.mb_chars(str)
|
|
56
|
+
if is_utf8?(str)
|
|
57
|
+
proxy_class.new(str)
|
|
58
|
+
else
|
|
59
|
+
str
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Regular expressions that describe valid byte sequences for a character
|
|
64
|
+
VALID_CHARACTER = {
|
|
65
|
+
# Borrowed from the Kconv library by Shinji KONO - (also as seen on the W3C site)
|
|
66
|
+
'UTF-8' => /\A(?:
|
|
67
|
+
[\x00-\x7f] |
|
|
68
|
+
[\xc2-\xdf] [\x80-\xbf] |
|
|
69
|
+
\xe0 [\xa0-\xbf] [\x80-\xbf] |
|
|
70
|
+
[\xe1-\xef] [\x80-\xbf] [\x80-\xbf] |
|
|
71
|
+
\xf0 [\x90-\xbf] [\x80-\xbf] [\x80-\xbf] |
|
|
72
|
+
[\xf1-\xf3] [\x80-\xbf] [\x80-\xbf] [\x80-\xbf] |
|
|
73
|
+
\xf4 [\x80-\x8f] [\x80-\xbf] [\x80-\xbf])\z /xn,
|
|
74
|
+
# Quick check for valid Shift-JIS characters, disregards the odd-even pairing
|
|
75
|
+
'Shift_JIS' => /\A(?:
|
|
76
|
+
[\x00-\x7e\xa1-\xdf] |
|
|
77
|
+
[\x81-\x9f\xe0-\xef] [\x40-\x7e\x80-\x9e\x9f-\xfc])\z /xn
|
|
78
|
+
}
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
require 'mail/multibyte/utils'
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mail
|
|
4
|
+
# A delivery method implementation which sends via exim.
|
|
5
|
+
#
|
|
6
|
+
# To use this, first find out where the exim binary is on your computer,
|
|
7
|
+
# if you are on a mac or unix box, it is usually in /usr/sbin/exim, this will
|
|
8
|
+
# be your exim location.
|
|
9
|
+
#
|
|
10
|
+
# Mail.defaults do
|
|
11
|
+
# delivery_method :exim
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
# Or if your exim binary is not at '/usr/sbin/exim'
|
|
15
|
+
#
|
|
16
|
+
# Mail.defaults do
|
|
17
|
+
# delivery_method :exim, :location => '/absolute/path/to/your/exim'
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# Then just deliver the email as normal:
|
|
21
|
+
#
|
|
22
|
+
# Mail.deliver do
|
|
23
|
+
# to 'mikel@test.lindsaar.net'
|
|
24
|
+
# from 'ada@test.lindsaar.net'
|
|
25
|
+
# subject 'testing exim'
|
|
26
|
+
# body 'testing exim'
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
# Or by calling deliver on a Mail message
|
|
30
|
+
#
|
|
31
|
+
# mail = Mail.new do
|
|
32
|
+
# to 'mikel@test.lindsaar.net'
|
|
33
|
+
# from 'ada@test.lindsaar.net'
|
|
34
|
+
# subject 'testing exim'
|
|
35
|
+
# body 'testing exim'
|
|
36
|
+
# end
|
|
37
|
+
#
|
|
38
|
+
# mail.deliver!
|
|
39
|
+
class Exim < Sendmail
|
|
40
|
+
DEFAULTS = {
|
|
41
|
+
:location => '/usr/sbin/exim',
|
|
42
|
+
:arguments => %w[ -i -t ]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# Uses -t option to extract recipients from the message.
|
|
46
|
+
def destinations_for(envelope)
|
|
47
|
+
nil
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'mail/smtp_envelope'
|
|
3
|
+
|
|
4
|
+
module Mail
|
|
5
|
+
# FileDelivery class delivers emails into multiple files based on the destination
|
|
6
|
+
# address. Each file is appended to if it already exists.
|
|
7
|
+
#
|
|
8
|
+
# So if you have an email going to fred@test, bob@test, joe@anothertest, and you
|
|
9
|
+
# set your location path to /path/to/mails then FileDelivery will create the directory
|
|
10
|
+
# if it does not exist, and put one copy of the email in three files, called
|
|
11
|
+
# by their message id
|
|
12
|
+
#
|
|
13
|
+
# Make sure the path you specify with :location is writable by the Ruby process
|
|
14
|
+
# running Mail.
|
|
15
|
+
class FileDelivery
|
|
16
|
+
require 'fileutils'
|
|
17
|
+
|
|
18
|
+
attr_accessor :settings
|
|
19
|
+
|
|
20
|
+
def initialize(values)
|
|
21
|
+
self.settings = { :location => './mails', :extension => '' }.merge!(values)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def deliver!(mail)
|
|
25
|
+
envelope = Mail::SmtpEnvelope.new(mail)
|
|
26
|
+
|
|
27
|
+
if ::File.respond_to?(:makedirs)
|
|
28
|
+
::File.makedirs settings[:location]
|
|
29
|
+
else
|
|
30
|
+
::FileUtils.mkdir_p settings[:location]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
envelope.to.uniq.each do |to|
|
|
34
|
+
path = ::File.join(settings[:location], File.basename(to.to_s+settings[:extension]))
|
|
35
|
+
|
|
36
|
+
::File.open(path, 'a') do |f|
|
|
37
|
+
f.write envelope.message
|
|
38
|
+
f.write "\r\n\r\n"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require 'mail/smtp_envelope'
|
|
2
|
+
|
|
3
|
+
module Mail
|
|
4
|
+
class LoggerDelivery
|
|
5
|
+
attr_reader :logger, :severity, :settings
|
|
6
|
+
|
|
7
|
+
def initialize(settings)
|
|
8
|
+
@settings = settings
|
|
9
|
+
@logger = settings.fetch(:logger) { default_logger }
|
|
10
|
+
@severity = derive_severity(settings[:severity])
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def deliver!(mail)
|
|
14
|
+
logger.log(severity) { Mail::SmtpEnvelope.new(mail).message }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
def default_logger
|
|
19
|
+
require 'logger'
|
|
20
|
+
::Logger.new($stdout)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def derive_severity(severity)
|
|
24
|
+
case severity
|
|
25
|
+
when nil
|
|
26
|
+
Logger::INFO
|
|
27
|
+
when Integer
|
|
28
|
+
severity
|
|
29
|
+
else
|
|
30
|
+
Logger.const_get(severity.to_s.upcase)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|