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