-
Notifications
You must be signed in to change notification settings - Fork 0
/
rfc822-gmime-filter-flowed.vala
170 lines (139 loc) · 6.76 KB
/
rfc822-gmime-filter-flowed.vala
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
161
162
163
164
165
166
167
168
169
170
/* Copyright 2012 Yorba Foundation
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
*/
// Filter to correctly handle flowed text as described in RFC 2646.
class GMime.FilterFlowed : GMime.Filter {
private char quote_marker;
// Invariant: True iff the last character seen was a space OR the penultimate character seen
// was a space and the last character seen was a \r.
private bool saw_space;
// Invariant: True iff the last character seen was a \r.
private bool saw_cr;
// Invariant: True iff the last \r\n encountered was preceded by a space.
private bool last_line_was_flowed;
// Invariant: True iff we are in the prefix for the first line.
private bool in_first_prefix;
// Invariant: True iff we are either at the beginning of a line, or all characters seen so far
// have been '>'s.
private bool in_prefix;
// Invariant: The quote depth of the last complete line seen, or 0 if we have not yet seen a
// complete line.
private uint last_quote_level;
// Invariant: The number of '>'s seen so far if we are parsing the prefix, or 0 if we are not
// parsing the prefix.
private uint current_quote_level;
public FilterFlowed(bool to_html) {
// TODO_: HTML filter converts unicode not in 0x20 to 0x7f to "&#num;"
quote_marker = to_html ? '\x7f' : '>';
reset();
}
public override void reset() {
saw_space = false;
saw_cr = false;
last_line_was_flowed = false;
in_first_prefix = true;
in_prefix = true;
last_quote_level = 0;
current_quote_level = 0;
}
public override GMime.Filter copy() {
FilterFlowed new_filter = new FilterFlowed(quote_marker == '\x7f');
new_filter.saw_space = saw_space;
new_filter.saw_cr = saw_cr;
new_filter.last_line_was_flowed = last_line_was_flowed;
new_filter.in_first_prefix = in_first_prefix;
new_filter.in_prefix = in_prefix;
new_filter.last_quote_level = last_quote_level;
new_filter.current_quote_level = current_quote_level;
return new_filter;
}
public override void filter(char[] inbuf, size_t prespace, out unowned char[] processed_buffer,
out size_t outprespace) {
// Worst-case scenario: We are about to leave the prefix, resulting in an extra
// (current_quote_level + 2) characters being written.
set_size(inbuf.length + current_quote_level + 2, false);
uint out_index = 0;
for (uint i = 0; i < inbuf.length; i++) {
char c = inbuf[i];
if (in_prefix) {
if (c == '>') {
// Don't write the prefix right away, because we don't want to write it if the
// last line was flowed.
current_quote_level++;
continue;
}
if (in_first_prefix) {
for (uint j = 0; j < current_quote_level; j++)
outbuf[out_index++] = quote_marker;
} else if (!last_line_was_flowed || current_quote_level != last_quote_level ||
(out_index > 3 && Geary.RFC822.Utils.comp_char_arr_slice(outbuf, out_index-4, "\n-- "))) {
// We encountered a non-flowed line-break, so insert a CRLF.
outbuf[out_index++] = '\r';
outbuf[out_index++] = '\n';
// We haven't been writing the quote prefix as we've scanned it, so we need to
// write it now.
for (uint j = 0; j < current_quote_level; j++)
outbuf[out_index++] = quote_marker;
}
// We saw a character other than '>', so we're done scanning the prefix.
in_first_prefix = false;
in_prefix = false;
last_quote_level = current_quote_level;
current_quote_level = 0;
// A single space following the prefix is space stuffed
if (c == ' ')
continue;
}
switch (c) {
case ' ':
saw_space = true;
saw_cr = false;
// It's safe to write the space right away, because trailing spaces even for
// flowed lines are still considered part of that line's content (see RFC 2646
// Section 4.2).
outbuf[out_index++] = c;
break;
case '\r':
if (saw_cr) {
// The last 3 charcters were ' \r\r', so we can't have ' \r\n'.
saw_space = false;
// We didn't write the preceding CR when we saw it, so we write it now.
outbuf[out_index++] = '\r';
}
saw_cr = true;
// We can't write the CR until we know it isn't part of a flowed line.
break;
case '\n':
if (saw_cr) {
// If the last 3 charcters were ' \r\n', the line was flowed.
last_line_was_flowed = saw_space;
// We are done with this line, so we are in the prefix of the next line
// (and have not yet seen any '>' charcacters in the next line).
in_prefix = true;
current_quote_level = 0;
} else {
// The LF wasn't part of a CRLF, so just write it.
outbuf[out_index++] = c;
}
saw_space = false;
saw_cr = false;
break;
default:
// We cannot be in a ' \r\n' sequence, so just write the character.
saw_space = false;
saw_cr = false;
outbuf[out_index++] = c;
break;
}
}
// Slicing the buffer is important, because the buffer is not null-terminated,
processed_buffer = outbuf[0:out_index];
outprespace = this.outpre;
}
public override void complete(char[] inbuf, size_t prespace, out unowned char[] processed_buffer,
out size_t outprespace) {
filter(inbuf, prespace, out processed_buffer, out outprespace);
}
}