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