1 /*
2  *  Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
3  *  Copyright (C) 2007-2013 Sourcefire, Inc.
4  *
5  *  Authors: Nigel Horne
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  *  MA 02110-1301, USA.
20  *
21  * $Log: text.c,v $
22  * Revision 1.25  2007/02/12 20:46:09  njh
23  * Various tidy
24  *
25  * Revision 1.24  2006/09/13 20:53:50  njh
26  * Added debug
27  *
28  * Revision 1.23  2006/07/14 12:13:08  njh
29  * Typo
30  *
31  * Revision 1.22  2006/07/01 21:03:36  njh
32  * Better use of destroy mode
33  *
34  * Revision 1.21  2006/07/01 16:17:35  njh
35  * Added destroy flag
36  *
37  * Revision 1.20  2006/07/01 03:47:50  njh
38  * Don't loop if binhex runs out of memory
39  *
40  * Revision 1.19  2006/05/19 11:02:12  njh
41  * Just include mbox.h
42  *
43  * Revision 1.18  2006/05/04 10:37:03  nigelhorne
44  * Speed up scanning of clean files
45  *
46  * Revision 1.17  2006/05/03 09:36:40  nigelhorne
47  * Pass full ctx into the mbox code
48  *
49  * Revision 1.16  2006/04/09 19:59:28  kojm
50  * update GPL headers with new address for FSF
51  *
52  * Revision 1.15  2005/03/10 08:50:49  nigelhorne
53  * Tidy
54  *
55  * Revision 1.14  2005/01/19 05:31:55  nigelhorne
56  * Added textIterate
57  *
58  * Revision 1.13  2004/12/08 19:03:41  nigelhorne
59  * Fix compilation error on Solaris
60  *
61  * Revision 1.12  2004/12/04 16:03:55  nigelhorne
62  * Text/plain now handled as no encoding
63  *
64  * Revision 1.11  2004/11/27 21:54:26  nigelhorne
65  * Tidy
66  *
67  * Revision 1.10  2004/08/22 10:34:24  nigelhorne
68  * Use fileblob
69  *
70  * Revision 1.9  2004/08/21 11:57:57  nigelhorne
71  * Use line.[ch]
72  *
73  * Revision 1.8  2004/07/20 14:35:29  nigelhorne
74  * Some MYDOOM.I were getting through
75  *
76  * Revision 1.7  2004/06/22 04:08:02  nigelhorne
77  * Optimise empty lines
78  *
79  * Revision 1.6  2004/05/05 09:37:52  nigelhorne
80  * Removed textClean - not needed in clamAV
81  *
82  * Revision 1.5  2004/03/25 22:40:46  nigelhorne
83  * Removed even more calls to realloc and some duplicated code
84  *
85  * Revision 1.4  2004/02/26 13:26:34  nigelhorne
86  * Handle spaces at the end of uuencoded lines
87  *
88  */
89 
90 #if HAVE_CONFIG_H
91 #include "clamav-config.h"
92 #endif
93 
94 #include <stdlib.h>
95 #ifdef C_DARWIN
96 #include <sys/types.h>
97 #include <sys/malloc.h>
98 #else
99 #ifdef HAVE_MALLOC_H /* tk: FreeBSD-CURRENT doesn't support malloc.h */
100 #ifndef C_BSD        /* BSD now uses stdlib.h */
101 #include <malloc.h>
102 #endif
103 #endif
104 #endif
105 #include <string.h>
106 #include <ctype.h>
107 #include <assert.h>
108 #include <stdio.h>
109 
110 #include "clamav.h"
111 #include "others.h"
112 
113 #include "mbox.h"
114 
115 static text *textCopy(const text *t_head);
116 static text *textAdd(text *t_head, const text *t);
117 static void addToFileblob(const line_t *line, void *arg);
118 static void getLength(const line_t *line, void *arg);
119 static void addToBlob(const line_t *line, void *arg);
120 static void *textIterate(text *t_text, void (*cb)(const line_t *line, void *arg), void *arg, int destroy);
121 
textDestroy(text * t_head)122 void textDestroy(text *t_head)
123 {
124     while (t_head) {
125         text *t_next = t_head->t_next;
126         if (t_head->t_line) {
127             lineUnlink(t_head->t_line);
128             t_head->t_line = NULL;
129         }
130         free(t_head);
131         t_head = t_next;
132     }
133 }
134 
135 /* Clone the current object */
136 static text *
textCopy(const text * t_head)137 textCopy(const text *t_head)
138 {
139     text *first = NULL, *last = NULL;
140 
141     while (t_head) {
142         if (first == NULL)
143             last = first = (text *)cli_malloc(sizeof(text));
144         else {
145             last->t_next = (text *)cli_malloc(sizeof(text));
146             last         = last->t_next;
147         }
148 
149         if (last == NULL) {
150             cli_errmsg("textCopy: Unable to allocate memory to clone object\n");
151             if (first)
152                 textDestroy(first);
153             return NULL;
154         }
155 
156         last->t_next = NULL;
157 
158         if (t_head->t_line)
159             last->t_line = lineLink(t_head->t_line);
160         else
161             last->t_line = NULL;
162 
163         t_head = t_head->t_next;
164     }
165 
166     if (first)
167         last->t_next = NULL;
168 
169     return first;
170 }
171 
172 /* Add a copy of a text to the end of the current object */
173 static text *
textAdd(text * t_head,const text * t)174 textAdd(text *t_head, const text *t)
175 {
176     text *ret;
177     int count;
178 
179     if (t_head == NULL) {
180         if (t == NULL) {
181             cli_errmsg("textAdd fails sanity check\n");
182             return NULL;
183         }
184         return textCopy(t);
185     }
186 
187     if (t == NULL)
188         return t_head;
189 
190     ret = t_head;
191 
192     count = 0;
193     while (t_head->t_next) {
194         count++;
195         t_head = t_head->t_next;
196     }
197 
198     cli_dbgmsg("textAdd: count = %d\n", count);
199 
200     while (t) {
201         t_head->t_next = (text *)cli_malloc(sizeof(text));
202         t_head         = t_head->t_next;
203 
204         assert(t_head != NULL);
205 
206         if (t->t_line)
207             t_head->t_line = lineLink(t->t_line);
208         else
209             t_head->t_line = NULL;
210 
211         t = t->t_next;
212     }
213 
214     t_head->t_next = NULL;
215 
216     return ret;
217 }
218 
219 /*
220  * Add a message's content to the end of the current object
221  */
222 text *
textAddMessage(text * aText,message * aMessage)223 textAddMessage(text *aText, message *aMessage)
224 {
225     assert(aMessage != NULL);
226 
227     if (messageGetEncoding(aMessage) == NOENCODING)
228         return textAdd(aText, messageGetBody(aMessage));
229     else {
230         text *anotherText = messageToText(aMessage);
231 
232         if (aText) {
233             text *newHead = textMove(aText, anotherText);
234             free(anotherText);
235             return newHead;
236         }
237         return anotherText;
238     }
239 }
240 
241 /*
242  * Put the contents of the given text at the end of the current object.
243  * The given text emptied; it can be used again if needed, though be warned that
244  * it will have an empty line at the start.
245  */
246 text *
textMove(text * t_head,text * t)247 textMove(text *t_head, text *t)
248 {
249     text *ret;
250 
251     if (t_head == NULL) {
252         if (t == NULL) {
253             cli_errmsg("textMove fails sanity check\n");
254             return NULL;
255         }
256         t_head = (text *)cli_malloc(sizeof(text));
257         if (t_head == NULL) {
258             cli_errmsg("textMove: Unable to allocate memory for head\n");
259             return NULL;
260         }
261         t_head->t_line = t->t_line;
262         t_head->t_next = t->t_next;
263         t->t_line      = NULL;
264         t->t_next      = NULL;
265         return t_head;
266     }
267 
268     if (t == NULL)
269         return t_head;
270 
271     ret = t_head;
272 
273     while (t_head->t_next)
274         t_head = t_head->t_next;
275 
276     /*
277 	 * Move the first line manually so that the caller is left clean but
278 	 * empty, the rest is moved by a simple pointer reassignment
279 	 */
280     t_head->t_next = (text *)cli_malloc(sizeof(text));
281     if (t_head->t_next == NULL) {
282         cli_errmsg("textMove: Unable to allocate memory for head->next\n");
283         return NULL;
284     }
285     t_head = t_head->t_next;
286 
287     assert(t_head != NULL);
288 
289     if (t->t_line) {
290         t_head->t_line = t->t_line;
291         t->t_line      = NULL;
292     } else
293         t_head->t_line = NULL;
294 
295     t_head->t_next = t->t_next;
296     t->t_next      = NULL;
297 
298     return ret;
299 }
300 
301 /*
302  * Transfer the contents of the text into a blob
303  * The caller must free the returned blob if b is NULL
304  */
305 blob *
textToBlob(text * t,blob * b,int destroy)306 textToBlob(text *t, blob *b, int destroy)
307 {
308     size_t s;
309     blob *bin;
310 
311     if (t == NULL)
312         return NULL;
313 
314     s = 0;
315 
316     (void)textIterate(t, getLength, &s, 0);
317 
318     if (s == 0)
319         return b;
320 
321     /*
322 	 * copy b. If b is NULL and an error occurs we know we need to free
323 	 *	before returning
324 	 */
325     bin = b;
326     if (b == NULL) {
327         b = blobCreate();
328 
329         if (b == NULL)
330             return NULL;
331     }
332 
333     if (blobGrow(b, s) != CL_SUCCESS) {
334         cli_warnmsg("Couldn't grow the blob: we may be low on memory\n");
335 #if 0
336 		if(!destroy) {
337 			if(bin == NULL)
338 				blobDestroy(b);
339 			return NULL;
340 		}
341 		/*
342 		 * We may be able to recover enough memory as we destroy to
343 		 * create the blob
344 		 */
345 #else
346         if (bin == NULL)
347             blobDestroy(b);
348         return NULL;
349 #endif
350     }
351 
352     (void)textIterate(t, addToBlob, b, destroy);
353 
354     if (destroy && t->t_next) {
355         textDestroy(t->t_next);
356         t->t_next = NULL;
357     }
358 
359     blobClose(b);
360 
361     return b;
362 }
363 
364 fileblob *
textToFileblob(text * t,fileblob * fb,int destroy)365 textToFileblob(text *t, fileblob *fb, int destroy)
366 {
367     assert(fb != NULL);
368     assert(t != NULL);
369 
370     if (fb == NULL) {
371         cli_dbgmsg("textToFileBlob, destroy = %d\n", destroy);
372         fb = fileblobCreate();
373 
374         if (fb == NULL)
375             return NULL;
376     } else {
377         cli_dbgmsg("textToFileBlob to %s, destroy = %d\n",
378                    fileblobGetFilename(fb), destroy);
379 
380         fb->ctx = NULL; /* no need to scan */
381     }
382 
383     fb = textIterate(t, addToFileblob, fb, destroy);
384     if (destroy && t->t_next) {
385         textDestroy(t->t_next);
386         t->t_next = NULL;
387     }
388     return fb;
389 }
390 
391 static void
getLength(const line_t * line,void * arg)392 getLength(const line_t *line, void *arg)
393 {
394     size_t *length = (size_t *)arg;
395 
396     if (line)
397         *length += strlen(lineGetData(line)) + 1;
398     else
399         (*length)++;
400 }
401 
402 static void
addToBlob(const line_t * line,void * arg)403 addToBlob(const line_t *line, void *arg)
404 {
405     blob *b = (blob *)arg;
406 
407     if (line) {
408         const char *l = lineGetData(line);
409 
410         blobAddData(b, (const unsigned char *)l, strlen(l));
411     }
412     blobAddData(b, (const unsigned char *)"\n", 1);
413 }
414 
415 static void
addToFileblob(const line_t * line,void * arg)416 addToFileblob(const line_t *line, void *arg)
417 {
418     fileblob *fb = (fileblob *)arg;
419 
420     if (line) {
421         const char *l = lineGetData(line);
422 
423         fileblobAddData(fb, (const unsigned char *)l, strlen(l));
424     }
425     fileblobAddData(fb, (const unsigned char *)"\n", 1);
426 }
427 
428 static void *
textIterate(text * t_text,void (* cb)(const line_t * item,void * arg),void * arg,int destroy)429 textIterate(text *t_text, void (*cb)(const line_t *item, void *arg), void *arg, int destroy)
430 {
431     /*
432 	 * Have two loops rather than one, so that we're not checking the
433 	 * value of "destroy" lots and lots of times
434 	 */
435 #if 0
436 	while(t_text) {
437 		(*cb)(t_text->t_line, arg);
438 
439 		if(destroy && t_text->t_line) {
440 			lineUnlink(t_text->t_line);
441 			t_text->t_line = NULL;
442 		}
443 
444 		t_text = t_text->t_next;
445 	}
446 #else
447     if (destroy)
448         while (t_text) {
449             (*cb)(t_text->t_line, arg);
450 
451             if (t_text->t_line) {
452                 lineUnlink(t_text->t_line);
453                 t_text->t_line = NULL;
454             }
455 
456             t_text = t_text->t_next;
457         }
458     else
459         while (t_text) {
460             (*cb)(t_text->t_line, arg);
461 
462             t_text = t_text->t_next;
463         }
464 #endif
465     return arg;
466 }
467