-
Notifications
You must be signed in to change notification settings - Fork 61
/
quoted_printable.ex
101 lines (83 loc) · 2.85 KB
/
quoted_printable.ex
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
defmodule Mail.Encoders.QuotedPrintable do
@moduledoc """
Encodes/decodes quoted-printable strings according to RFC 2045.
See the following links for reference:
- <https://tools.ietf.org/html/rfc2045#section-6.7>
"""
@new_line "=\r\n"
@max_length 76
@doc """
Encodes a string into a quoted-printable encoded string.
## Examples
Mail.Encoders.QuotedPrintable.encode("façade")
"fa=C3=A7ade"
"""
@spec encode(binary) :: binary
@spec encode(binary, integer, list, non_neg_integer) :: binary
def encode(string, max_length \\ @max_length, acc \\ <<>>, line_length \\ 0)
def encode(<<>>, _, acc, _), do: acc
# Encode ASCII characters in range 0x20..0x3C.
# Encode ASCII characters in range 0x3E..0x7E, except 0x3F (question mark)
def encode(<<char, tail::binary>>, max_length, acc, line_length)
when char in ?!..?< or char in ?@..?~ or char == ?> do
if line_length < max_length - 1 do
encode(tail, max_length, acc <> <<char>>, line_length + 1)
else
encode(tail, max_length, acc <> @new_line <> <<char>>, 1)
end
end
# Encode ASCII tab and space characters.
def encode(<<char, tail::binary>>, max_length, acc, line_length) when char in [?\t, ?\s] do
# if remaining > 0 do
if byte_size(tail) > 0 do
if line_length < max_length - 1 do
encode(tail, max_length, acc <> <<char>>, line_length + 1)
else
encode(tail, max_length, acc <> @new_line <> <<char>>, 1)
end
else
escaped = "=" <> Base.encode16(<<char>>)
line_length = line_length + byte_size(escaped)
if line_length <= max_length do
encode(tail, max_length, acc <> escaped, line_length)
else
encode(tail, max_length, acc <> @new_line <> escaped, byte_size(escaped))
end
end
end
# Encode all other characters.
def encode(<<char, tail::binary>>, max_length, acc, line_length) do
escaped = "=" <> Base.encode16(<<char>>)
line_length = line_length + byte_size(escaped)
if line_length < max_length do
encode(tail, max_length, acc <> escaped, line_length)
else
encode(tail, max_length, acc <> @new_line <> escaped, byte_size(escaped))
end
end
@doc """
Decodes a quoted-printable encoded string.
## Examples
Mail.QuotedPrintable.decode("fa=C3=A7ade")
"façade"
"""
@spec decode(binary) :: binary
def decode(string, acc \\ [])
def decode(<<>>, acc) do
acc
|> Enum.reverse()
|> Enum.join()
end
def decode(<<?=, ?\r, ?\n, tail::binary>>, acc) do
decode(tail, acc)
end
def decode(<<?=, chars::binary-size(2), tail::binary>>, acc) do
case Base.decode16(chars, case: :mixed) do
{:ok, decoded} -> decode(tail, [decoded | acc])
:error -> decode(tail, [chars, "=" | acc])
end
end
def decode(<<char::binary-size(1), tail::binary>>, acc) do
decode(tail, [char | acc])
end
end