forked from mikel/mail
/
unstructured_field.rb
160 lines (141 loc) · 4.64 KB
/
unstructured_field.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# encoding: utf-8
require 'mail/fields/common/common_field'
module Mail
# Provides access to an unstructured header field
#
# ===Per RFC 2822:
# 2.2.1. Unstructured Header Field Bodies
#
# Some field bodies in this standard are defined simply as
# "unstructured" (which is specified below as any US-ASCII characters,
# except for CR and LF) with no further restrictions. These are
# referred to as unstructured field bodies. Semantically, unstructured
# field bodies are simply to be treated as a single line of characters
# with no further processing (except for header "folding" and
# "unfolding" as described in section 2.2.3).
class UnstructuredField
include Mail::CommonField
include Mail::Utilities
def initialize(name, value, charset = nil)
self.charset = charset
@errors = []
if charset
self.charset = charset
else
if value.to_s.respond_to?(:encoding)
self.charset = value.to_s.encoding
else
self.charset = $KCODE
end
end
self.name = name
self.value = value
self
end
def charset
@charset
end
def charset=(val)
@charset = val
end
def errors
@errors
end
def encoded
do_encode(self.name)
end
def decoded
do_decode
end
def default
decoded
end
def parse # An unstructured field does not parse
self
end
private
def do_encode(name)
value.nil? ? '' : "#{wrapped_value}\r\n"
end
def do_decode
value.blank? ? nil : Encodings.decode_encode(value, :decode)
end
# 2.2.3. Long Header Fields
#
# Each header field is logically a single line of characters comprising
# the field name, the colon, and the field body. For convenience
# however, and to deal with the 998/78 character limitations per line,
# the field body portion of a header field can be split into a multiple
# line representation; this is called "folding". The general rule is
# that wherever this standard allows for folding white space (not
# simply WSP characters), a CRLF may be inserted before any WSP. For
# example, the header field:
#
# Subject: This is a test
#
# can be represented as:
#
# Subject: This
# is a test
#
# Note: Though structured field bodies are defined in such a way that
# folding can take place between many of the lexical tokens (and even
# within some of the lexical tokens), folding SHOULD be limited to
# placing the CRLF at higher-level syntactic breaks. For instance, if
# a field body is defined as comma-separated values, it is recommended
# that folding occur after the comma separating the structured items in
# preference to other places where the field could be folded, even if
# it is allowed elsewhere.
def wrapped_value # :nodoc:
@folded_line = []
@unfolded_line = decoded.to_s.clone
fold("#{name}: ".length)
wrap_lines(name, @folded_line)
end
def wrap_lines(name, folded_lines)
result = []
index = 0
result[index] = "#{name}: #{folded_lines.shift}"
folded_lines.each do |line|
if (result[index] + line).length < 77
result[index] << " " + line
else
result[index] << "\r\n\t"
index += 1
result[index] = line
end
end
result.join
end
def fold(prepend = 0) # :nodoc:
# Get the last whitespace character, OR we'll just choose
# 78 if there is no whitespace, or 23 for non ascii (23 * 3 for QP Encoding == 69)
@unfolded_line.ascii_only? ? (limit = 78 - prepend) : (limit = 23 - prepend)
# find the last white space character within the limit
if wspp = @unfolded_line.slice(0..limit) =~ /[ \t][^ \T]*$/
wrap = true
wspp = limit if wspp == 0
@folded_line << encode(@unfolded_line.slice!(0...wspp).strip)
@folded_line.flatten!
# if no last whitespace, find the first
elsif wspp = @unfolded_line =~ /[ \t][^ \T]/
wrap = true
wspp = limit if wspp == 0
@folded_line << encode(@unfolded_line.slice!(0...wspp).strip)
@folded_line.flatten!
# if no whitespace, don't wrap
else
wrap = false
end
if wrap && @unfolded_line.length > limit
fold
else
@folded_line << encode(@unfolded_line)
@folded_line.flatten!
end
end
def encode(value)
Encodings.q_value_encode(value, @charset).split(" ")
end
end
end