/src/dovecot/src/lib-mail/message-size.c
Line | Count | Source |
1 | | /* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "istream.h" |
5 | | #include "message-parser.h" |
6 | | #include "message-size.h" |
7 | | |
8 | | int message_get_header_size(struct istream *input, struct message_size *hdr, |
9 | | bool *has_nuls_r) |
10 | 0 | { |
11 | 0 | const unsigned char *msg; |
12 | 0 | size_t i, size, startpos, missing_cr_count; |
13 | 0 | int ret; |
14 | |
|
15 | 0 | memset(hdr, 0, sizeof(struct message_size)); |
16 | 0 | *has_nuls_r = FALSE; |
17 | |
|
18 | 0 | missing_cr_count = 0; startpos = 0; |
19 | 0 | while ((ret = i_stream_read_bytes(input, &msg, &size, startpos + 1)) > 0) { |
20 | 0 | for (i = startpos; i < size; i++) { |
21 | 0 | if (msg[i] != '\n') { |
22 | 0 | if (msg[i] == '\0') |
23 | 0 | *has_nuls_r = TRUE; |
24 | 0 | continue; |
25 | 0 | } |
26 | | |
27 | 0 | hdr->lines++; |
28 | 0 | if (i == 0 || msg[i-1] != '\r') { |
29 | | /* missing CR */ |
30 | 0 | missing_cr_count++; |
31 | 0 | } |
32 | |
|
33 | 0 | if (i == 0 || (i == 1 && msg[i-1] == '\r')) { |
34 | | /* no headers at all */ |
35 | 0 | break; |
36 | 0 | } |
37 | | |
38 | 0 | if ((i > 0 && msg[i-1] == '\n') || |
39 | 0 | (i > 1 && msg[i-2] == '\n' && msg[i-1] == '\r')) { |
40 | | /* \n\n or \n\r\n - end of headers */ |
41 | 0 | break; |
42 | 0 | } |
43 | 0 | } |
44 | |
|
45 | 0 | if (i < size) { |
46 | | /* end of header */ |
47 | 0 | startpos = i+1; |
48 | 0 | break; |
49 | 0 | } |
50 | | |
51 | | /* leave the last two characters, they may be \r\n */ |
52 | 0 | startpos = size == 1 ? 1 : 2; |
53 | 0 | i_stream_skip(input, i - startpos); |
54 | |
|
55 | 0 | hdr->physical_size += i - startpos; |
56 | 0 | } |
57 | 0 | i_assert(ret == -1 || ret > 0); |
58 | | |
59 | 0 | ret = input->stream_errno != 0 ? -1 : 0; |
60 | 0 | i_stream_skip(input, startpos); |
61 | 0 | hdr->physical_size += startpos; |
62 | |
|
63 | 0 | hdr->virtual_size = hdr->physical_size + missing_cr_count; |
64 | 0 | i_assert(hdr->virtual_size >= hdr->physical_size); |
65 | 0 | return ret; |
66 | 0 | } |
67 | | |
68 | | int message_get_body_size(struct istream *input, struct message_size *body, |
69 | | bool *has_nuls_r) |
70 | 0 | { |
71 | 0 | const unsigned char *msg; |
72 | 0 | size_t i, size, missing_cr_count; |
73 | 0 | int ret; |
74 | |
|
75 | 0 | memset(body, 0, sizeof(struct message_size)); |
76 | 0 | *has_nuls_r = FALSE; |
77 | |
|
78 | 0 | missing_cr_count = 0; |
79 | 0 | if ((ret = i_stream_read_more(input, &msg, &size)) <= 0) { |
80 | 0 | i_assert(ret == -1); |
81 | 0 | return ret < 0 && input->stream_errno != 0 ? -1 : 0; |
82 | 0 | } |
83 | | |
84 | 0 | if (msg[0] == '\n') |
85 | 0 | missing_cr_count++; |
86 | |
|
87 | 0 | do { |
88 | 0 | for (i = 1; i < size; i++) { |
89 | 0 | if (msg[i] > '\n') |
90 | 0 | continue; |
91 | | |
92 | 0 | if (msg[i] == '\n') { |
93 | 0 | if (msg[i-1] != '\r') { |
94 | | /* missing CR */ |
95 | 0 | missing_cr_count++; |
96 | 0 | } |
97 | | |
98 | | /* increase after making sure we didn't break |
99 | | at virtual \r */ |
100 | 0 | body->lines++; |
101 | 0 | } else if (msg[i] == '\0') { |
102 | 0 | *has_nuls_r = TRUE; |
103 | 0 | } |
104 | 0 | } |
105 | | |
106 | | /* leave the last character, it may be \r */ |
107 | 0 | i_stream_skip(input, i - 1); |
108 | 0 | body->physical_size += i - 1; |
109 | 0 | } while ((ret = i_stream_read_bytes(input, &msg, &size, 2)) > 0); |
110 | 0 | i_assert(ret == -1); |
111 | | |
112 | 0 | ret = input->stream_errno != 0 ? -1 : 0; |
113 | |
|
114 | 0 | i_stream_skip(input, 1); |
115 | 0 | body->physical_size++; |
116 | |
|
117 | 0 | body->virtual_size = body->physical_size + missing_cr_count; |
118 | 0 | i_assert(body->virtual_size >= body->physical_size); |
119 | 0 | return ret; |
120 | 0 | } |
121 | | |
122 | | void message_size_add(struct message_size *dest, |
123 | | const struct message_size *src) |
124 | 570k | { |
125 | 570k | dest->virtual_size += src->virtual_size; |
126 | 570k | dest->physical_size += src->physical_size; |
127 | 570k | dest->lines += src->lines; |
128 | 570k | } |
129 | | |
130 | | int message_skip_virtual(struct istream *input, uoff_t virtual_skip, |
131 | | bool *last_virtual_cr_r) |
132 | 0 | { |
133 | 0 | const unsigned char *msg; |
134 | 0 | size_t i, size; |
135 | 0 | bool cr_skipped = FALSE; |
136 | 0 | int ret; |
137 | |
|
138 | 0 | *last_virtual_cr_r = FALSE; |
139 | 0 | if (virtual_skip == 0) |
140 | 0 | return 0; |
141 | | |
142 | 0 | while ((ret = i_stream_read_bytes(input, &msg, &size, 1)) > 0) { |
143 | 0 | size = I_MIN(virtual_skip, size); |
144 | 0 | const unsigned char *p = memchr(msg, '\n', size); |
145 | 0 | if (p == NULL) { |
146 | 0 | i_stream_skip(input, size); |
147 | 0 | virtual_skip -= size; |
148 | 0 | if (virtual_skip == 0) |
149 | 0 | return 0; |
150 | 0 | continue; |
151 | 0 | } |
152 | 0 | i = p - msg; |
153 | 0 | virtual_skip -= i + 1; |
154 | | |
155 | | /* LF */ |
156 | 0 | if ((i == 0 && !cr_skipped) || |
157 | 0 | (i > 0 && msg[i-1] != '\r')) { |
158 | 0 | if (virtual_skip == 0) { |
159 | | /* CR/LF boundary */ |
160 | 0 | *last_virtual_cr_r = TRUE; |
161 | 0 | } else { |
162 | 0 | virtual_skip--; |
163 | 0 | i++; |
164 | 0 | } |
165 | 0 | } else { |
166 | 0 | i++; |
167 | 0 | } |
168 | 0 | i_stream_skip(input, i); |
169 | 0 | if (virtual_skip == 0) |
170 | 0 | return 0; |
171 | | |
172 | 0 | i_assert(i > 0); |
173 | 0 | cr_skipped = msg[i-1] == '\r'; |
174 | 0 | } |
175 | 0 | i_assert(ret == -1); |
176 | 0 | return input->stream_errno == 0 ? 0 : -1; |
177 | 0 | } |