1 /* This file is part of GNU Anubis.
2 Copyright (C) 2009-2014 The Anubis Team.
3
4 GNU Anubis is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License as published by the
6 Free Software Foundation; either version 3 of the License, or (at your
7 option) any later version.
8
9 GNU Anubis is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with GNU Anubis. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include "headers.h"
19
20 struct smtp_reply_line
21 {
22 size_t off;
23 size_t len;
24 };
25
26 struct smtp_reply
27 {
28 char *buffer;
29 size_t buffer_size;
30 size_t reply_length;
31 size_t line_count;
32 size_t line_size;
33 struct smtp_reply_line *line_vec;
34 struct
35 {
36 size_t line;
37 char c;
38 } cursor;
39 };
40
41 ANUBIS_SMTP_REPLY
smtp_reply_new()42 smtp_reply_new ()
43 {
44 ANUBIS_SMTP_REPLY p = xzalloc (sizeof (*p));
45 p->cursor.line = -1;
46 return p;
47 }
48
49 void
smtp_reply_free(ANUBIS_SMTP_REPLY reply)50 smtp_reply_free (ANUBIS_SMTP_REPLY reply)
51 {
52 if (reply)
53 {
54 free (reply->buffer);
55 free (reply->line_vec);
56 free (reply);
57 }
58 }
59
60 static void
smtp_reply_alloc_space(ANUBIS_SMTP_REPLY reply,size_t len)61 smtp_reply_alloc_space (ANUBIS_SMTP_REPLY reply, size_t len)
62 {
63 if (reply->buffer_size == 0)
64 {
65 reply->buffer_size = len + 1;
66 reply->buffer = xmalloc (reply->buffer_size);
67 }
68 else
69 while (reply->reply_length + len + 1 >= reply->buffer_size)
70 reply->buffer = x2realloc (reply->buffer, &reply->buffer_size);
71 }
72
73 static void
smtp_reply_alloc_line_space(ANUBIS_SMTP_REPLY reply,size_t lc)74 smtp_reply_alloc_line_space (ANUBIS_SMTP_REPLY reply, size_t lc)
75 {
76 if (lc > reply->line_size)
77 {
78 if (reply->line_size)
79 reply->line_size = lc;
80 reply->line_vec = x2nrealloc (reply->line_vec, &reply->line_size,
81 sizeof (reply->line_vec[0]));
82 }
83 }
84
85 static void
smtp_reply_scan(ANUBIS_SMTP_REPLY reply,size_t lc)86 smtp_reply_scan (ANUBIS_SMTP_REPLY reply, size_t lc)
87 {
88 char *line;
89 size_t off;
90
91 if (lc)
92 smtp_reply_alloc_line_space (reply, lc);
93
94 line = reply->buffer;
95 lc = 0;
96 off = 0;
97 while (*line)
98 {
99 size_t len = strcspn (line, "\r\n");
100
101 smtp_reply_alloc_line_space (reply, lc + 1);
102 reply->line_vec[lc].off = off;
103 reply->line_vec[lc].len = len;
104 lc++;
105 if (line[++len] == '\n')
106 len++;
107 line += len;
108 off += len;
109 }
110 reply->line_count = lc;
111 }
112
113 void
smtp_reply_read(ANUBIS_SMTP_REPLY reply,ssize_t (* reader)(void *,char **,size_t *),void * rdata)114 smtp_reply_read (ANUBIS_SMTP_REPLY reply,
115 ssize_t (*reader) (void *, char **, size_t *),
116 void *rdata)
117 {
118 char *line = NULL;
119 size_t size = 0;
120 size_t lc = 0;
121
122 reply->reply_length = 0;
123 do
124 {
125 size_t len;
126
127 if (reader (rdata, &line, &size) <= 0)
128 break;
129
130 len = strlen (line);
131
132 smtp_reply_alloc_space (reply, len);
133
134 memcpy (reply->buffer + reply->reply_length, line, len);
135 reply->reply_length += len;
136 lc++;
137 }
138 while (line[3] == '-');
139 free (line);
140
141 if (reply->reply_length)
142 {
143 reply->buffer[reply->reply_length] = 0;
144 smtp_reply_scan (reply, lc);
145 }
146 }
147
148 void
smtp_reply_set(ANUBIS_SMTP_REPLY reply,const char * input)149 smtp_reply_set (ANUBIS_SMTP_REPLY reply, const char *input)
150 {
151 size_t len = strlen (input);
152
153 if (len > 2 && memcmp (input + len - 2, CRLF, 2) == 0)
154 len -= 2;
155
156 smtp_reply_alloc_space (reply, len + 3);
157 memcpy (reply->buffer, input, len);
158 memcpy (reply->buffer + len, CRLF, 2);
159 reply->reply_length = len + 2;
160 reply->buffer[reply->reply_length] = 0;
161 smtp_reply_scan (reply, 0);
162 }
163
164 #define __smtp_reply_line_ptr(r,i) ((r)->buffer + (r)->line_vec[i].off)
165 #define __smtp_reply_line_end(r,i) \
166 ((r)->buffer + (r)->line_vec[i].off + (r)->line_vec[i].len)
167
168 size_t
smtp_reply_line_count(ANUBIS_SMTP_REPLY reply)169 smtp_reply_line_count (ANUBIS_SMTP_REPLY reply)
170 {
171 return reply->line_count;
172 }
173
174 const char *
smtp_reply_line_ptr(ANUBIS_SMTP_REPLY reply,size_t index)175 smtp_reply_line_ptr (ANUBIS_SMTP_REPLY reply, size_t index)
176 {
177 if (index < reply->line_count)
178 return __smtp_reply_line_ptr (reply, index);
179 return NULL;
180 }
181
182 const char *
smtp_reply_line_end(ANUBIS_SMTP_REPLY reply,size_t index)183 smtp_reply_line_end (ANUBIS_SMTP_REPLY reply, size_t index)
184 {
185 if (index < reply->line_count)
186 return __smtp_reply_line_end (reply, index);
187 return NULL;
188 }
189
190 void
smtp_reply_clear_cursor(ANUBIS_SMTP_REPLY reply)191 smtp_reply_clear_cursor (ANUBIS_SMTP_REPLY reply)
192 {
193 if (reply->cursor.line >= 0 && reply->cursor.line < reply->line_count)
194 {
195 *(char*)smtp_reply_line_end (reply, reply->cursor.line) = reply->cursor.c;
196 reply->cursor.line = -1;
197 }
198 }
199
200 char const *
smtp_reply_line(ANUBIS_SMTP_REPLY reply,size_t index)201 smtp_reply_line (ANUBIS_SMTP_REPLY reply, size_t index)
202 {
203 smtp_reply_clear_cursor (reply);
204 if (index < reply->line_count)
205 {
206 char *ptr = __smtp_reply_line_end (reply, index);
207 reply->cursor.c = *ptr;
208 reply->cursor.line = index;
209 *ptr = 0;
210 return __smtp_reply_line_ptr (reply, index);
211 }
212 return NULL;
213 }
214
215 int
smtp_reply_get_line(ANUBIS_SMTP_REPLY reply,size_t index,char ** pstr,size_t * psize)216 smtp_reply_get_line (ANUBIS_SMTP_REPLY reply, size_t index,
217 char **pstr, size_t *psize)
218 {
219 if (index < reply->line_count)
220 {
221 size_t len = reply->line_vec[index].len;
222 char *p = xmalloc (len + 1);
223 memcpy (p, __smtp_reply_line_ptr (reply, index), len);
224 p[len] = 0;
225 *pstr = p;
226 if (psize)
227 *psize = len;
228 return 0;
229 }
230 return 1;
231 }
232
233 int
smtp_reply_code_eq(ANUBIS_SMTP_REPLY reply,const char * code)234 smtp_reply_code_eq (ANUBIS_SMTP_REPLY reply, const char *code)
235 {
236 if (reply->line_count > 0)
237 {
238 size_t len = strlen (code);
239 if (len > 3)
240 len = 3;
241 return strncmp (reply->buffer, code, len) == 0;
242 }
243 return 0;
244 }
245
246 int
smtp_reply_has_capa(ANUBIS_SMTP_REPLY reply,const char * capa,size_t * pind)247 smtp_reply_has_capa (ANUBIS_SMTP_REPLY reply, const char *capa, size_t *pind)
248 {
249 size_t i;
250 size_t capa_len = strlen (capa);
251
252 for (i = 0; i < reply->line_count; i++)
253 {
254 char const *p = smtp_reply_line (reply, i) + 4;
255 size_t len = strcspn (p, " ");
256
257 if (len == capa_len
258 && memcmp (smtp_reply_line (reply, i) + 4, capa, len) == 0)
259 {
260 if (pind)
261 *pind = i;
262 return 1;
263 }
264 }
265 return 0;
266 }
267
268 int
smtp_reply_has_string(ANUBIS_SMTP_REPLY reply,size_t index,const char * key,size_t * pind)269 smtp_reply_has_string (ANUBIS_SMTP_REPLY reply, size_t index,
270 const char *key, size_t *pind)
271 {
272 if (index < reply->line_count)
273 if (strstr (smtp_reply_line (reply, index) + 4, key) != NULL)
274 {
275 if (pind)
276 *pind = index;
277 return 1;
278 }
279 return 0;
280 }
281
282 char const *
smtp_reply_string(ANUBIS_SMTP_REPLY reply)283 smtp_reply_string (ANUBIS_SMTP_REPLY reply)
284 {
285 smtp_reply_clear_cursor (reply);
286 return reply->buffer;
287 }
288
289 void
smtp_reply_replace_line(ANUBIS_SMTP_REPLY reply,size_t index,const char * str)290 smtp_reply_replace_line (ANUBIS_SMTP_REPLY reply, size_t index,
291 const char *str)
292 {
293 size_t new_len = strlen (str);
294 size_t old_len = reply->line_vec[index].len;
295 ssize_t delta = 4 + new_len - old_len;
296 char *p;
297
298 smtp_reply_clear_cursor (reply);
299
300 if (delta > 0)
301 smtp_reply_alloc_space (reply, delta);
302 if (index != reply->line_count - 1)
303 {
304 size_t i;
305
306 memmove (reply->buffer + reply->line_vec[index+1].off + delta,
307 reply->buffer + reply->line_vec[index+1].off,
308 reply->reply_length - reply->line_vec[index+1].off + 1);
309 for (i = index + 1; i < reply->line_count; i++)
310 reply->line_vec[i].off += delta;
311 }
312 reply->reply_length += delta;
313
314 p = __smtp_reply_line_ptr (reply, index);
315 memcpy (p, reply->buffer, 3);
316 p[3] = (index != reply->line_count - 1) ? '-' : ' ';
317 memcpy (p + 4, str, new_len);
318 reply->line_vec[index].len = 4 + new_len;
319 memcpy (__smtp_reply_line_end (reply, index), CRLF, 3);
320 }
321
322 void
smtp_reply_add_line(ANUBIS_SMTP_REPLY reply,const char * str)323 smtp_reply_add_line (ANUBIS_SMTP_REPLY reply, const char *str)
324 {
325 size_t new_len = strlen (str);
326 struct smtp_reply_line *lp;
327 char *p;
328
329 smtp_reply_clear_cursor (reply);
330
331 p = __smtp_reply_line_ptr (reply, reply->line_count - 1);
332 p[3] = '-';
333
334 smtp_reply_alloc_space (reply, new_len + 7);
335 reply->line_count++;
336 smtp_reply_alloc_line_space (reply, reply->line_count);
337 lp = reply->line_vec + reply->line_count - 1;
338 lp->off = reply->reply_length;
339 lp->len = 4 + new_len;
340 p = reply->buffer + lp->off;
341 memcpy (p, reply->buffer, 3);
342 p[3] = ' ';
343 memcpy (p + 4, str, new_len);
344 memcpy (p + 4 + new_len, CRLF, 2);
345 p[4 + new_len + 2] = 0;
346 reply->reply_length += new_len + 6;
347 }
348
349 void
smtp_reply_remove_line(ANUBIS_SMTP_REPLY reply,size_t index)350 smtp_reply_remove_line (ANUBIS_SMTP_REPLY reply, size_t index)
351 {
352 size_t len;
353
354 smtp_reply_clear_cursor (reply);
355
356 len = reply->line_vec[index].len + 2;
357 if (index == reply->line_count - 1)
358 {
359 __smtp_reply_line_ptr (reply, index)[0] = 0;
360 if (index > 0)
361 __smtp_reply_line_ptr (reply, index - 1)[3] = ' ';
362 }
363 else
364 {
365 memmove (__smtp_reply_line_ptr (reply, index),
366 __smtp_reply_line_ptr (reply, index + 1),
367 reply->reply_length - reply->line_vec[index+1].off + 1);
368 memmove (reply->line_vec + index,
369 reply->line_vec + index + 1,
370 (reply->line_count - index - 1) * sizeof reply->line_vec[0]);
371 for (; index < reply->line_count; index++)
372 reply->line_vec[index].off -= len;
373 }
374 reply->reply_length -= len;
375 reply->line_count--;
376 }
377
378
379