Browse code

add initial allscan/allmatch mode to libclamav, clamd, clamdscan, and clamscan with unit tests

Steve Morgan authored on 2012/10/19 06:12:58
Showing 38 changed files
... ...
@@ -107,6 +107,7 @@ int scan_callback(STATBUF *sb, char *filename, const char *msg, enum cli_ftw_rea
107 107
 {
108 108
     struct scan_cb_data *scandata = data->data;
109 109
     const char *virname;
110
+    const char **virpp = &virname;
110 111
     int ret;
111 112
     int type = scandata->type;
112 113
     struct cb_context context;
... ...
@@ -227,11 +228,18 @@ int scan_callback(STATBUF *sb, char *filename, const char *msg, enum cli_ftw_rea
227 227
     thrmgr_setactivetask(filename, NULL);
228 228
     context.filename = filename;
229 229
     context.virsize = 0;
230
-    ret = cl_scanfile_callback(filename, &virname, &scandata->scanned, scandata->engine, scandata->options, &context);
230
+    ret = cl_scanfile_callback(filename, virpp, &scandata->scanned, scandata->engine, scandata->options, &context);
231 231
     thrmgr_setactivetask(NULL, NULL);
232 232
 
233
+    if (scandata->options & CL_SCAN_ALLMATCHES) {
234
+	virpp = (const char **)*virpp; /* temp hack for scanall mode until api augmentation */
235
+	virname = virpp[0];
236
+    }
237
+
233 238
     if (thrmgr_group_need_terminate(scandata->conn->group)) {
234 239
 	free(filename);
240
+	if (ret == CL_VIRUS && scandata->options & CL_SCAN_ALLMATCHES)
241
+	    free((void *)virpp);
235 242
 	logg("*Client disconnected while scanjob was active\n");
236 243
 	return ret == CL_ETIMEOUT ? ret : CL_BREAK;
237 244
     }
... ...
@@ -240,8 +248,19 @@ int scan_callback(STATBUF *sb, char *filename, const char *msg, enum cli_ftw_rea
240 240
 	scandata->infected++;
241 241
 	if (conn_reply_virus(scandata->conn, filename, virname) == -1) {
242 242
 	    free(filename);
243
+	    if (scandata->options & CL_SCAN_ALLMATCHES)
244
+		free((void *)virpp);
243 245
 	    return CL_ETIMEOUT;
244 246
 	}
247
+	if (scandata->options & CL_SCAN_ALLMATCHES && virpp[1] != NULL) {
248
+	    int i = 1;
249
+	    while (NULL != virpp[i])
250
+		if (conn_reply_virus(scandata->conn, filename, virpp[i++]) == -1) {
251
+		    free(filename);
252
+		    free((void *)virpp);
253
+		    return CL_ETIMEOUT;
254
+		}
255
+	}
245 256
 	if(context.virsize)
246 257
 	    detstats_add(virname, filename, context.virsize, context.virhash);
247 258
 	if(context.virsize && optget(scandata->opts, "ExtendedDetectionInfo")->enabled)
... ...
@@ -249,6 +268,11 @@ int scan_callback(STATBUF *sb, char *filename, const char *msg, enum cli_ftw_rea
249 249
 	else
250 250
 	    logg("~%s: %s FOUND\n", filename, virname);
251 251
 	virusaction(filename, virname, scandata->opts);
252
+	if (scandata->options & CL_SCAN_ALLMATCHES && virpp[1] != NULL) {
253
+	    int i = 1;
254
+	    while (NULL != virpp[i])
255
+                logg("~%s: %s FOUND\n", filename, virpp[i++]);
256
+	}
252 257
     } else if (ret != CL_CLEAN) {
253 258
 	scandata->errors++;
254 259
 	if (conn_reply(scandata->conn, filename, cl_strerror(ret), "ERROR") == -1) {
... ...
@@ -261,6 +285,8 @@ int scan_callback(STATBUF *sb, char *filename, const char *msg, enum cli_ftw_rea
261 261
     }
262 262
 
263 263
     free(filename);
264
+    if (ret == CL_VIRUS && scandata->options & CL_SCAN_ALLMATCHES)
265
+	free((void *)virpp);
264 266
     if(ret == CL_EMEM) /* stop scanning */
265 267
 	return ret;
266 268
 
... ...
@@ -94,7 +94,8 @@ static struct {
94 94
     {CMD16, sizeof(CMD16)-1,	COMMAND_IDSESSION,  0,	0, 1},
95 95
     {CMD17, sizeof(CMD17)-1,	COMMAND_INSTREAM,   0,	0, 1},
96 96
     {CMD19, sizeof(CMD19)-1,	COMMAND_DETSTATSCLEAR,	0, 1, 1},
97
-    {CMD20, sizeof(CMD20)-1,	COMMAND_DETSTATS,   0, 1, 1}
97
+    {CMD20, sizeof(CMD20)-1,	COMMAND_DETSTATS,   0, 1, 1},
98
+    {CMD21, sizeof(CMD21)-1,	COMMAND_ALLMATCHSCAN,  1, 0, 1}
98 99
 };
99 100
 
100 101
 enum commands parse_command(const char *cmd, const char **argument, int oldstyle)
... ...
@@ -314,129 +315,135 @@ int command(client_conn_t *conn, int *virus)
314 314
 	    }
315 315
 	    return ret;
316 316
 #else
317
-	    conn_reply_error(conn, "FILDES support not compiled in.");
318
-	    close(conn->scanfd);
319
-	    return 0;
320
-#endif
321
-	case COMMAND_STATS:
322
-	    thrmgr_setactivetask(NULL, "STATS");
323
-	    if (conn->group)
324
-		mdprintf(desc, "%u: ", conn->id);
325
-	    thrmgr_printstats(desc, conn->term);
326
-	    return 0;
327
-	case COMMAND_STREAM:
328
-	    thrmgr_setactivetask(NULL, "STREAM");
329
-	    ret = scanstream(desc, NULL, engine, options, opts, conn->term);
330
-	    if (ret == CL_VIRUS)
331
-		*virus = 1;
332
-	    if (ret == CL_EMEM) {
333
-		if(optget(opts, "ExitOnOOM")->enabled)
334
-		    return -1;
335
-		else
336
-		    return 1;
337
-	    }
338
-	    return 0;
339
-	case COMMAND_INSTREAMSCAN:
340
-	    thrmgr_setactivetask(NULL, "INSTREAM");
341
-	    ret = scanfd(conn, NULL, engine, options, opts, desc, 1);
342
-	    if (ret == CL_VIRUS) {
343
-		*virus = 1;
344
-		ret = 0;
345
-	    } else if (ret == CL_EMEM) {
346
-		if(optget(opts, "ExitOnOOM")->enabled)
347
-		    ret = -1;
348
-		else
349
- 		    ret = 1;
350
-	    } else if (ret == CL_ETIMEOUT) {
351
-		thrmgr_group_terminate(conn->group);
352
-		ret = 1;
353
-	    } else
354
-		ret = 0;
355
-	    if (ftruncate(conn->scanfd, 0) == -1) {
356
-		/* not serious, we're going to close it and unlink it anyway */
357
-		logg("*ftruncate failed: %d\n", errno);
358
-	    }
359
-	    close(conn->scanfd);
360
-	    conn->scanfd = -1;
361
-	    cli_unlink(conn->filename);
362
-	    return ret;
363
-	default:
364
-	    logg("!Invalid command distpached: %d\n", conn->cmdtype);
365
-	    return 1;
366
-    }
367
-
368
-    scandata.type = type;
369
-    maxdirrec = optget(opts, "MaxDirectoryRecursion")->numarg;
370
-    if (optget(opts, "FollowDirectorySymlinks")->enabled)
371
-	flags |= CLI_FTW_FOLLOW_DIR_SYMLINK;
372
-    if (optget(opts, "FollowFileSymlinks")->enabled)
373
-	flags |= CLI_FTW_FOLLOW_FILE_SYMLINK;
374
-
375
-    if(!optget(opts, "CrossFilesystems")->enabled)
376
-	if(STAT(conn->filename, &sb) == 0)
377
-	    scandata.dev = sb.st_dev;
378
-
379
-    ret = cli_ftw(conn->filename, flags,  maxdirrec ? maxdirrec : INT_MAX, scan_callback, &data, scan_pathchk);
380
-    if (ret == CL_EMEM) {
381
-	if(optget(opts, "ExitOnOOM")->enabled)
382
-	    return -1;
383
-	else
384
-	    return 1;
385
-    }
386
-    if (scandata.group && type == TYPE_MULTISCAN) {
387
-	thrmgr_group_waitforall(group, &ok, &error, &total);
388
-	pthread_mutex_lock(&conn->thrpool->pool_mutex);
389
-	conn->thrpool->thr_multiscan--;
390
-	pthread_mutex_unlock(&conn->thrpool->pool_mutex);
391
-    } else {
392
-	error = scandata.errors;
393
-	total = scandata.total;
394
-	ok = total - error - scandata.infected;
395
-    }
396
-
397
-    if (ok + error == total && (error != total)) {
398
-	if (conn_reply_single(conn, conn->filename, "OK") == -1)
399
-	    ret = CL_ETIMEOUT;
400
-    }
401
-    *virus = total - (ok + error);
402
-
403
-    if (ret == CL_ETIMEOUT)
404
-	thrmgr_group_terminate(conn->group);
405
-    return error;
406
-}
407
-
408
-static int dispatch_command(client_conn_t *conn, enum commands cmd, const char *argument)
409
-{
410
-    int ret = 0;
411
-    int bulk;
412
-    client_conn_t *dup_conn = (client_conn_t *) malloc(sizeof(struct client_conn_tag));
413
-
414
-    if(!dup_conn) {
415
-	logg("!Can't allocate memory for client_conn\n");
416
-	return -1;
417
-    }
418
-    memcpy(dup_conn, conn, sizeof(*conn));
419
-    dup_conn->cmdtype = cmd;
420
-    if(cl_engine_addref(dup_conn->engine)) {
421
-	logg("!cl_engine_addref() failed\n");
422
-	free(dup_conn);
423
-	return -1;
424
-    }
425
-    dup_conn->scanfd = -1;
426
-    bulk = 1;
427
-    switch (cmd) {
428
-	case COMMAND_FILDES:
429
-	    if (conn->scanfd == -1) {
430
-		conn_reply_error(dup_conn, "No file descriptor received.");
431
-		ret = 1;
432
-	    }
433
-	    dup_conn->scanfd = conn->scanfd;
434
-	    /* consume FD */
435
-	    conn->scanfd = -1;
436
-	    break;
437
-	case COMMAND_SCAN:
438
-	case COMMAND_CONTSCAN:
439
-	case COMMAND_MULTISCAN:
317
+	     conn_reply_error(conn, "FILDES support not compiled in.");
318
+	     close(conn->scanfd);
319
+	     return 0;
320
+ #endif
321
+	 case COMMAND_STATS:
322
+	     thrmgr_setactivetask(NULL, "STATS");
323
+	     if (conn->group)
324
+		 mdprintf(desc, "%u: ", conn->id);
325
+	     thrmgr_printstats(desc, conn->term);
326
+	     return 0;
327
+	 case COMMAND_STREAM:
328
+	     thrmgr_setactivetask(NULL, "STREAM");
329
+	     ret = scanstream(desc, NULL, engine, options, opts, conn->term);
330
+	     if (ret == CL_VIRUS)
331
+		 *virus = 1;
332
+	     if (ret == CL_EMEM) {
333
+		 if(optget(opts, "ExitOnOOM")->enabled)
334
+		     return -1;
335
+		 else
336
+		     return 1;
337
+	     }
338
+	     return 0;
339
+	 case COMMAND_INSTREAMSCAN:
340
+	     thrmgr_setactivetask(NULL, "INSTREAM");
341
+	     ret = scanfd(conn, NULL, engine, options, opts, desc, 1);
342
+	     if (ret == CL_VIRUS) {
343
+		 *virus = 1;
344
+		 ret = 0;
345
+	     } else if (ret == CL_EMEM) {
346
+		 if(optget(opts, "ExitOnOOM")->enabled)
347
+		     ret = -1;
348
+		 else
349
+		     ret = 1;
350
+	     } else if (ret == CL_ETIMEOUT) {
351
+		 thrmgr_group_terminate(conn->group);
352
+		 ret = 1;
353
+	     } else
354
+		 ret = 0;
355
+	     if (ftruncate(conn->scanfd, 0) == -1) {
356
+		 /* not serious, we're going to close it and unlink it anyway */
357
+		 logg("*ftruncate failed: %d\n", errno);
358
+	     }
359
+	     close(conn->scanfd);
360
+	     conn->scanfd = -1;
361
+	     cli_unlink(conn->filename);
362
+	     return ret;
363
+	 case COMMAND_ALLMATCHSCAN:
364
+	     thrmgr_setactivetask(NULL, "ALLMATCHSCAN");
365
+	     scandata.options |= CL_SCAN_ALLMATCHES;
366
+	     type = TYPE_SCAN;
367
+	     break;
368
+	 default:
369
+	     logg("!Invalid command distpached: %d\n", conn->cmdtype);
370
+	     return 1;
371
+     }
372
+
373
+     scandata.type = type;
374
+     maxdirrec = optget(opts, "MaxDirectoryRecursion")->numarg;
375
+     if (optget(opts, "FollowDirectorySymlinks")->enabled)
376
+	 flags |= CLI_FTW_FOLLOW_DIR_SYMLINK;
377
+     if (optget(opts, "FollowFileSymlinks")->enabled)
378
+	 flags |= CLI_FTW_FOLLOW_FILE_SYMLINK;
379
+
380
+     if(!optget(opts, "CrossFilesystems")->enabled)
381
+	 if(STAT(conn->filename, &sb) == 0)
382
+	     scandata.dev = sb.st_dev;
383
+
384
+     ret = cli_ftw(conn->filename, flags,  maxdirrec ? maxdirrec : INT_MAX, scan_callback, &data, scan_pathchk);
385
+     if (ret == CL_EMEM) {
386
+	 if(optget(opts, "ExitOnOOM")->enabled)
387
+	     return -1;
388
+	 else
389
+	     return 1;
390
+     }
391
+     if (scandata.group && type == TYPE_MULTISCAN) {
392
+	 thrmgr_group_waitforall(group, &ok, &error, &total);
393
+	 pthread_mutex_lock(&conn->thrpool->pool_mutex);
394
+	 conn->thrpool->thr_multiscan--;
395
+	 pthread_mutex_unlock(&conn->thrpool->pool_mutex);
396
+     } else {
397
+	 error = scandata.errors;
398
+	 total = scandata.total;
399
+	 ok = total - error - scandata.infected;
400
+     }
401
+
402
+     if (ok + error == total && (error != total)) {
403
+	 if (conn_reply_single(conn, conn->filename, "OK") == -1)
404
+	     ret = CL_ETIMEOUT;
405
+     }
406
+     *virus = total - (ok + error);
407
+
408
+     if (ret == CL_ETIMEOUT)
409
+	 thrmgr_group_terminate(conn->group);
410
+     return error;
411
+ }
412
+
413
+ static int dispatch_command(client_conn_t *conn, enum commands cmd, const char *argument)
414
+ {
415
+     int ret = 0;
416
+     int bulk;
417
+     client_conn_t *dup_conn = (client_conn_t *) malloc(sizeof(struct client_conn_tag));
418
+
419
+     if(!dup_conn) {
420
+	 logg("!Can't allocate memory for client_conn\n");
421
+	 return -1;
422
+     }
423
+     memcpy(dup_conn, conn, sizeof(*conn));
424
+     dup_conn->cmdtype = cmd;
425
+     if(cl_engine_addref(dup_conn->engine)) {
426
+	 logg("!cl_engine_addref() failed\n");
427
+	 free(dup_conn);
428
+	 return -1;
429
+     }
430
+     dup_conn->scanfd = -1;
431
+     bulk = 1;
432
+     switch (cmd) {
433
+	 case COMMAND_FILDES:
434
+	     if (conn->scanfd == -1) {
435
+		 conn_reply_error(dup_conn, "No file descriptor received.");
436
+		 ret = 1;
437
+	     }
438
+	     dup_conn->scanfd = conn->scanfd;
439
+	     /* consume FD */
440
+	     conn->scanfd = -1;
441
+	     break;
442
+	 case COMMAND_SCAN:
443
+	 case COMMAND_CONTSCAN:
444
+	 case COMMAND_MULTISCAN:
445
+	 case COMMAND_ALLMATCHSCAN:
440 446
 	    dup_conn->filename = cli_strdup_to_utf8(argument);
441 447
 	    if (!dup_conn->filename) {
442 448
 		logg("!Failed to allocate memory for filename\n");
... ...
@@ -608,6 +615,7 @@ int execute_or_dispatch_command(client_conn_t *conn, enum commands cmd, const ch
608 608
 	case COMMAND_FILDES:
609 609
 	case COMMAND_SCAN:
610 610
 	case COMMAND_INSTREAMSCAN:
611
+	case COMMAND_ALLMATCHSCAN:
611 612
 	    return dispatch_command(conn, cmd, argument);
612 613
 	case COMMAND_IDSESSION:
613 614
 	    conn->group = thrmgr_group_new();
... ...
@@ -43,6 +43,8 @@
43 43
 #define CMD19 "DETSTATSCLEAR"
44 44
 #define CMD20 "DETSTATS"
45 45
 
46
+#define CMD21 "ALLMATCHSCAN"
47
+
46 48
 #include "libclamav/clamav.h"
47 49
 #include "shared/optparser.h"
48 50
 #include "server.h"
... ...
@@ -70,7 +72,8 @@ enum commands {
70 70
     COMMAND_DETSTATS,
71 71
     /* internal commands */
72 72
     COMMAND_MULTISCANFILE,
73
-    COMMAND_INSTREAMSCAN
73
+    COMMAND_INSTREAMSCAN,
74
+    COMMAND_ALLMATCHSCAN
74 75
 };
75 76
 
76 77
 typedef struct client_conn_tag {
... ...
@@ -236,7 +236,9 @@ int client(const struct optstruct *opts, int *infected, int *err)
236 236
     if(remote || scandash) {
237 237
 	scantype = STREAM;
238 238
 	session = optget(opts, "multiscan")->enabled;
239
-    } else if(optget(opts, "multiscan")->enabled) scantype = MULTI;
239
+    } 
240
+    else if(optget(opts, "multiscan")->enabled) scantype = MULTI;
241
+    else if(optget(opts, "allmatch")->enabled) scantype = ALLMATCH;
240 242
     else scantype = CONT;
241 243
 
242 244
     maxrec = optget(clamdopts, "MaxDirectoryRecursion")->numarg;
... ...
@@ -27,7 +27,9 @@ enum {
27 27
     CONT,
28 28
     MULTI,
29 29
     STREAM,
30
-    FILDES
30
+    FILDES,
31
+    ALLMATCH,
32
+    MAX_SCANTYPE = ALLMATCH
31 33
 };
32 34
 
33 35
 int client(const struct optstruct *opts, int *infected, int *err);
... ...
@@ -58,7 +58,7 @@ extern unsigned long int maxstream;
58 58
 int printinfected;
59 59
 extern struct optstruct *clamdopts;
60 60
 
61
-static const char *scancmd[] = { "CONTSCAN", "MULTISCAN" };
61
+static const char *scancmd[] = { "CONTSCAN", "MULTISCAN", "INSTREAM", "FILDES", "ALLMATCHSCAN" };
62 62
 
63 63
 /* Connects to clamd 
64 64
  * Returns a FD or -1 on error */
... ...
@@ -203,6 +203,7 @@ int dsresult(int sockd, int scantype, const char *filename, int *printok, int *e
203 203
     switch(scantype) {
204 204
     case MULTI:
205 205
     case CONT:
206
+    case ALLMATCH:
206 207
     if (!filename) {
207 208
 	logg("Filename cannot be NULL for MULTISCAN or CONTSCAN.\n");
208 209
 	return -1;
... ...
@@ -254,7 +255,13 @@ int dsresult(int sockd, int scantype, const char *filename, int *printok, int *e
254 254
 		colon = strrchr(bol, ':');
255 255
 	    }
256 256
 	    if(!colon) {
257
-		logg("Failed to parse reply\n");
257
+		char * unkco = "UNKNOWN COMMAND";
258
+		if (!strncmp(bol, unkco, sizeof(unkco) - 1))
259
+		    logg("clamd replied \"UNKNOWN COMMAND\". Command was %s\n", 
260
+			 (scantype < 0 || scantype > MAX_SCANTYPE) ? "unidentified" :
261
+			                                             scancmd[scantype]);
262
+		else
263
+		    logg("Failed to parse reply: \"%s\"\n", bol);
258 264
 		return -1;
259 265
 	    } else if(!memcmp(eol - 7, " FOUND", 6)) {
260 266
 		*(eol - 7) = 0;
... ...
@@ -217,6 +217,7 @@ static void scanfile(const char *filename, struct cl_engine *engine, const struc
217 217
 	unsigned i;
218 218
 	const struct optstruct *opt;
219 219
 	const char *virname;
220
+	const char **virpp = &virname;
220 221
 	STATBUF sb;
221 222
 	struct metachain chain;
222 223
 
... ...
@@ -291,7 +292,7 @@ static void scanfile(const char *filename, struct cl_engine *engine, const struc
291 291
     }
292 292
 
293 293
 
294
-    if((ret = cl_scandesc_callback(fd, &virname, &info.blocks, engine, options, &chain)) == CL_VIRUS) {
294
+    if((ret = cl_scandesc_callback(fd, virpp, &info.blocks, engine, options, &chain)) == CL_VIRUS) {
295 295
 	if(optget(opts, "archive-verbose")->enabled) {
296 296
 	    if (chain.n > 1) {
297 297
 		char str[128];
... ...
@@ -300,7 +301,16 @@ static void scanfile(const char *filename, struct cl_engine *engine, const struc
300 300
 	    } else if (chain.lastvir)
301 301
 		logg("~%s!(%d): %s FOUND\n", filename, chain.lastvir-1, virname);
302 302
 	}
303
-	logg("~%s: %s FOUND\n", filename, virname);
303
+	if (options & CL_SCAN_ALLMATCHES) {
304
+	    int i = 0;
305
+	    virpp = (const char **)*virpp; /* horrible */
306
+	    virname = virpp[0];
307
+	    while (virpp[i])
308
+		logg("~%s: %s FOUND\n", filename, virpp[i++]);
309
+	    free((void *)virpp);
310
+	}
311
+	else
312
+	    logg("~%s: %s FOUND\n", filename, virname);
304 313
 	info.files++;
305 314
 	info.ifiles++;
306 315
 
... ...
@@ -432,6 +442,8 @@ static int scanstdin(const struct cl_engine *engine, const struct optstruct *opt
432 432
 	int ret;
433 433
 	unsigned int fsize = 0;
434 434
 	const char *virname, *tmpdir;
435
+	const char **virpp = &virname;
436
+
435 437
 	char *file, buff[FILEBUFF];
436 438
 	size_t bread;
437 439
 	FILE *fs;
... ...
@@ -474,7 +486,17 @@ static int scanstdin(const struct cl_engine *engine, const struct optstruct *opt
474 474
     info.rblocks += fsize / CL_COUNT_PRECISION;
475 475
 
476 476
     if((ret = cl_scanfile(file, &virname, &info.blocks, engine, options)) == CL_VIRUS) {
477
-	logg("stdin: %s FOUND\n", virname);
477
+        if (options & CL_SCAN_ALLMATCHES) {
478
+            int i = 0;
479
+            virpp = (const char **)*virpp; /* temp hack for scanall mode until api augmentation */
480
+            virname = virpp[0];
481
+            while (virpp[i])
482
+                logg("stdin: %s FOUND\n", virpp[i++]);
483
+            free((void *)virpp);
484
+        }
485
+	else
486
+	    logg("stdin: %s FOUND\n", virname);
487
+
478 488
 	info.ifiles++;
479 489
 
480 490
 	if(bell)
... ...
@@ -710,6 +732,9 @@ int scanmanager(const struct optstruct *opts)
710 710
     }
711 711
 
712 712
     /* set scan options */
713
+    if(optget(opts, "allmatch")->enabled)
714
+	options |= CL_SCAN_ALLMATCHES;
715
+
713 716
     if(optget(opts,"phishing-ssl")->enabled)
714 717
 	options |= CL_SCAN_PHISHING_BLOCKSSL;
715 718
 
... ...
@@ -81,6 +81,7 @@ int cli_7unz (cli_ctx *ctx, size_t offset) {
81 81
     UInt16 utf16buf[UTFBUFSZ], *utf16name = utf16buf;
82 82
     int namelen = UTFBUFSZ, found = CL_CLEAN;
83 83
     Int64 begin_of_archive = offset;
84
+    UInt32 viruses_found = 0;
84 85
 
85 86
     /* Replacement for 
86 87
        FileInStream_CreateVTable(&archiveStream); */
... ...
@@ -150,14 +151,19 @@ int cli_7unz (cli_ctx *ctx, size_t offset) {
150 150
 		encrypted = 1;
151 151
 		if(DETECT_ENCRYPTED) {
152 152
 		    cli_dbgmsg("cli_7unz: Encrypted files found in archive.\n");
153
-		    *ctx->virname = "Heuristics.Encrypted.7Zip";
154
-		    found = CL_VIRUS;
155
-		    break;
153
+		    cli_append_virus(ctx, "Heuristics.Encrypted.7Zip");
154
+		    viruses_found++;
155
+		    if(!SCAN_ALL) {
156
+			found = CL_VIRUS;
157
+			break;
158
+		    }
156 159
 		}
157 160
 	    }
158 161
 	    if(cli_matchmeta(ctx, name, 0, f->Size, encrypted, i, f->CrcDefined ? f->Crc : 0, NULL)) {
159 162
 		found = CL_VIRUS;
160
-		break;
163
+		viruses_found++;
164
+		if (!SCAN_ALL)
165
+		    break;
161 166
 	    }
162 167
 	    if (res != SZ_OK)
163 168
 		cli_dbgmsg("cli_unz: extraction failed with %d\n", res);
... ...
@@ -169,14 +175,16 @@ int cli_7unz (cli_ctx *ctx, size_t offset) {
169 169
 		if(cli_writen(fd, outBuffer + offset, outSizeProcessed) != outSizeProcessed)
170 170
 		    found = CL_EWRITE;
171 171
 		else
172
-		    found = cli_magic_scandesc(fd, ctx);
172
+		    if ((found = cli_magic_scandesc(fd, ctx)) == CL_VIRUS)
173
+			viruses_found++;
173 174
 		close(fd);
174 175
 		if(!ctx->engine->keeptmp && cli_unlink(name))
175 176
 		    found = CL_EUNLINK;
176 177
 
177 178
 		free(name);
178 179
 		if(found != CL_CLEAN)
179
-		    break;
180
+		    if (!(SCAN_ALL && found == CL_VIRUS))
181
+			break;
180 182
 	    }
181 183
 	}
182 184
 	IAlloc_Free(&allocImp, outBuffer);
... ...
@@ -196,5 +204,7 @@ int cli_7unz (cli_ctx *ctx, size_t offset) {
196 196
     else
197 197
 	cli_dbgmsg("cli_7unz: error %d\n", res);
198 198
 
199
+    if (SCAN_ALL && viruses_found)
200
+	return CL_VIRUS;
199 201
     return found;
200 202
 }
... ...
@@ -2594,7 +2594,7 @@ int cli_bytecode_context_setfile(struct cli_bc_ctx *ctx, fmap_t *map)
2594 2594
 
2595 2595
 int cli_bytecode_runlsig(cli_ctx *cctx, struct cli_target_info *tinfo,
2596 2596
 			 const struct cli_all_bc *bcs, unsigned bc_idx,
2597
-			 const char **virname, const uint32_t* lsigcnt,
2597
+			 const uint32_t* lsigcnt,
2598 2598
 			 const uint32_t *lsigsuboff, fmap_t *map)
2599 2599
 {
2600 2600
     int ret;
... ...
@@ -2641,9 +2641,8 @@ int cli_bytecode_runlsig(cli_ctx *cctx, struct cli_target_info *tinfo,
2641 2641
     if (ctx.virname) {
2642 2642
 	int rc;
2643 2643
 	cli_dbgmsg("Bytecode found virus: %s\n", ctx.virname);
2644
-	if (virname)
2645
-	    *virname = ctx.virname;
2646
-	if (!strncmp(*virname, "BC.Heuristics", 13))
2644
+	cli_append_virus(cctx, ctx.virname);
2645
+	if (!strncmp(ctx.virname, "BC.Heuristics", 13))
2647 2646
 	    rc = cli_found_possibly_unwanted(cctx);
2648 2647
 	else
2649 2648
 	    rc = CL_VIRUS;
... ...
@@ -2657,7 +2656,7 @@ int cli_bytecode_runlsig(cli_ctx *cctx, struct cli_target_info *tinfo,
2657 2657
 }
2658 2658
 
2659 2659
 int cli_bytecode_runhook(cli_ctx *cctx, const struct cl_engine *engine, struct cli_bc_ctx *ctx,
2660
-			 unsigned id, fmap_t *map, const char **virname)
2660
+			 unsigned id, fmap_t *map)
2661 2661
 {
2662 2662
     const unsigned *hooks = engine->hooks[id - _BC_START_HOOKS];
2663 2663
     unsigned i, hooks_cnt = engine->hooks_cnt[id - _BC_START_HOOKS];
... ...
@@ -2690,10 +2689,13 @@ int cli_bytecode_runhook(cli_ctx *cctx, const struct cl_engine *engine, struct c
2690 2690
 	}
2691 2691
 	if (ctx->virname) {
2692 2692
 	    cli_dbgmsg("Bytecode found virus: %s\n", ctx->virname);
2693
-	    if (virname)
2694
-		*virname = ctx->virname;
2695
-	    cli_bytecode_context_clear(ctx);
2696
-	    return CL_VIRUS;
2693
+	    cli_append_virus(cctx, ctx->virname);
2694
+	    if (!(cctx->options & CL_SCAN_ALLMATCHES)) {
2695
+		cli_bytecode_context_clear(ctx);
2696
+		return CL_VIRUS;
2697
+	    }
2698
+	    cli_bytecode_context_reset(ctx);
2699
+	    continue;
2697 2700
 	}
2698 2701
 	ret = cli_bytecode_context_getresult_int(ctx);
2699 2702
 	/* TODO: use prefix here */
... ...
@@ -2726,10 +2728,15 @@ int cli_bytecode_runhook(cli_ctx *cctx, const struct cl_engine *engine, struct c
2726 2726
 		}
2727 2727
 		free(tempfile);
2728 2728
 		if (ret != CL_CLEAN) {
2729
-		    if (ret == CL_VIRUS)
2729
+		    if (ret == CL_VIRUS) {
2730 2730
 			cli_dbgmsg("Scanning unpacked file by bytecode %u found a virus\n", bc->id);
2731
-		    cli_bytecode_context_clear(ctx);
2732
-		    return ret;
2731
+			if (cctx->options & CL_SCAN_ALLMATCHES) {
2732
+			    cli_bytecode_context_reset(ctx);
2733
+			    continue;
2734
+			}
2735
+			cli_bytecode_context_clear(ctx);
2736
+		    	return ret;
2737
+		    }
2733 2738
 		}
2734 2739
 		cli_bytecode_context_reset(ctx);
2735 2740
 		continue;
... ...
@@ -118,8 +118,8 @@ void cli_bytecode_describe(const struct cli_bc *bc);
118 118
 struct cli_exe_info;
119 119
 struct cli_ctx_tag;
120 120
 struct cli_target_info;
121
-int cli_bytecode_runlsig(struct cli_ctx_tag *ctx, struct cli_target_info *info, const struct cli_all_bc *bcs, unsigned bc_idx, const char **virname, const uint32_t* lsigcnt, const uint32_t *lsigsuboff, fmap_t *map);
122
-int cli_bytecode_runhook(struct cli_ctx_tag *cctx, const struct cl_engine *engine, struct cli_bc_ctx *ctx, unsigned id, fmap_t *map, const char **virname);
121
+int cli_bytecode_runlsig(struct cli_ctx_tag *ctx, struct cli_target_info *info, const struct cli_all_bc *bcs, unsigned bc_idx, const uint32_t* lsigcnt, const uint32_t *lsigsuboff, fmap_t *map);
122
+int cli_bytecode_runhook(struct cli_ctx_tag *cctx, const struct cl_engine *engine, struct cli_bc_ctx *ctx, unsigned id, fmap_t *map);
123 123
 
124 124
 #ifdef __cplusplus
125 125
 extern "C" {
... ...
@@ -511,8 +511,7 @@ int32_t cli_bcapi_extract_new(struct cli_bc_ctx *ctx, int32_t id)
511 511
 	cctx->recursion--;
512 512
 	cctx->container_type = current;
513 513
 	if (res == CL_VIRUS) {
514
-	    if (cctx->virname)
515
-		ctx->virname = *cctx->virname;
514
+	    ctx->virname = cli_get_last_virus(cctx);
516 515
 	    ctx->found = 1;
517 516
 	}
518 517
     }
... ...
@@ -142,6 +142,7 @@ typedef enum {
142 142
 #define CL_SCAN_PARTIAL_MESSAGE         0x40000
143 143
 #define CL_SCAN_HEURISTIC_PRECEDENCE    0x80000
144 144
 #define CL_SCAN_BLOCKMACROS		0x100000
145
+#define CL_SCAN_ALLMATCHES		0x200000
145 146
 
146 147
 #define CL_SCAN_PERFORMANCE_INFO        0x40000000 /* collect performance timings */
147 148
 #define CL_SCAN_INTERNAL_COLLECT_SHA    0x80000000 /* Enables hash output in sha-collect builds - for internal use only */
... ...
@@ -73,7 +73,7 @@ int cli_scanelf(cli_ctx *ctx)
73 73
 	uint8_t conv = 0, err;
74 74
 	unsigned int format;
75 75
 	fmap_t *map = *ctx->fmap;
76
-
76
+	uint32_t viruses_found = 0;
77 77
 
78 78
     cli_dbgmsg("in cli_scanelf\n");
79 79
 
... ...
@@ -214,8 +214,7 @@ int cli_scanelf(cli_ctx *ctx)
214 214
     if(phnum > 128) {
215 215
 	cli_dbgmsg("ELF: Suspicious number of program headers\n");
216 216
         if(DETECT_BROKEN) {
217
-	    if(ctx->virname)
218
-		*ctx->virname = "Heuristics.Broken.Executable";
217
+	    cli_append_virus(ctx, "Heuristics.Broken.Executable");
219 218
 	    return CL_VIRUS;
220 219
         }
221 220
 	return CL_EFORMAT;
... ...
@@ -227,8 +226,7 @@ int cli_scanelf(cli_ctx *ctx)
227 227
 	if(phentsize != sizeof(struct elf_program_hdr32)) {
228 228
 	    cli_dbgmsg("ELF: phentsize != sizeof(struct elf_program_hdr32)\n");
229 229
 	    if(DETECT_BROKEN) {
230
-		if(ctx->virname)
231
-		    *ctx->virname = "Heuristics.Broken.Executable";
230
+		cli_append_virus(ctx, "Heuristics.Broken.Executable");
232 231
 		return CL_VIRUS;
233 232
 	    }
234 233
 	    return CL_EFORMAT;
... ...
@@ -275,8 +273,7 @@ int cli_scanelf(cli_ctx *ctx)
275 275
 		cli_dbgmsg("ELF: Possibly broken ELF file\n");
276 276
 		free(program_hdr);
277 277
 		if(DETECT_BROKEN) {
278
-		    if(ctx->virname)
279
-			*ctx->virname = "Heuristics.Broken.Executable";
278
+		    cli_append_virus(ctx, "Heuristics.Broken.Executable");
280 279
 		    return CL_VIRUS;
281 280
 		}
282 281
 		return CL_CLEAN;
... ...
@@ -296,8 +293,7 @@ int cli_scanelf(cli_ctx *ctx)
296 296
 	if(err) {
297 297
 	    cli_dbgmsg("ELF: Can't calculate file offset of entry point\n");
298 298
 	    if(DETECT_BROKEN) {
299
-		if(ctx->virname)
300
-		    *ctx->virname = "Heuristics.Broken.Executable";
299
+                cli_append_virus(ctx, "Heuristics.Broken.Executable");
301 300
 		return CL_VIRUS;
302 301
 	    }
303 302
 	    return CL_EFORMAT;
... ...
@@ -319,8 +315,7 @@ int cli_scanelf(cli_ctx *ctx)
319 319
     if(shentsize != sizeof(struct elf_section_hdr32)) {
320 320
 	cli_dbgmsg("ELF: shentsize != sizeof(struct elf_section_hdr32)\n");
321 321
         if(DETECT_BROKEN) {
322
-	    if(ctx->virname)
323
-		*ctx->virname = "Heuristics.Broken.Executable";
322
+	    cli_append_virus(ctx, "Heuristics.Broken.Executable");
324 323
 	    return CL_VIRUS;
325 324
         }
326 325
 	return CL_EFORMAT;
... ...
@@ -369,8 +364,7 @@ int cli_scanelf(cli_ctx *ctx)
369 369
             cli_dbgmsg("ELF: Possibly broken ELF file\n");
370 370
             free(section_hdr);
371 371
             if(DETECT_BROKEN) {
372
-                if(ctx->virname)
373
-                    *ctx->virname = "Heuristics.Broken.Executable";
372
+                cli_append_virus(ctx, "Heuristics.Broken.Executable");
374 373
 		return CL_VIRUS;
375 374
             }
376 375
             return CL_CLEAN;
... ...
@@ -175,8 +175,7 @@ struct macho_fat_arch
175 175
     if(matcher)						    \
176 176
 	return -1;					    \
177 177
     if(DETECT_BROKEN) {					    \
178
-	if(ctx->virname)				    \
179
-	    *ctx->virname = "Heuristics.Broken.Executable"; \
178
+	cli_append_virus(ctx, "Heuristics.Broken.Executable"); \
180 179
 	return CL_VIRUS;				    \
181 180
     }							    \
182 181
     return CL_EFORMAT
... ...
@@ -1157,7 +1157,7 @@ void cli_ac_chkmacro(struct cli_matcher *root, struct cli_ac_data *data, unsigne
1157 1157
 }
1158 1158
 
1159 1159
 
1160
-int cli_ac_scanbuff(const unsigned char *buffer, uint32_t length, const char **virname, void **customdata, struct cli_ac_result **res, const struct cli_matcher *root, struct cli_ac_data *mdata, uint32_t offset, cli_file_t ftype, struct cli_matched_type **ftoffset, unsigned int mode, const cli_ctx *ctx)
1160
+int cli_ac_scanbuff(const unsigned char *buffer, uint32_t length, const char **virname, void **customdata, struct cli_ac_result **res, const struct cli_matcher *root, struct cli_ac_data *mdata, uint32_t offset, cli_file_t ftype, struct cli_matched_type **ftoffset, unsigned int mode, cli_ctx *ctx)
1161 1161
 {
1162 1162
 	struct cli_ac_node *current;
1163 1163
 	struct cli_ac_patt *patt, *pt;
... ...
@@ -1359,11 +1359,18 @@ int cli_ac_scanbuff(const unsigned char *buffer, uint32_t length, const char **v
1359 1359
 					pt = pt->next_same;
1360 1360
 					continue;
1361 1361
 				    } else {
1362
-					if(virname)
1363
-					    *virname = pt->virname;
1362
+					if(virname) {
1363
+					    if (ctx && SCAN_ALL && virname == ctx->virname)
1364
+						cli_append_virus(ctx, (const char *)pt->virname);
1365
+					    else
1366
+						*virname = pt->virname;
1367
+					}
1364 1368
 					if(customdata)
1365 1369
 					    *customdata = pt->customdata;
1366
-					return CL_VIRUS;
1370
+					if (!ctx || !SCAN_ALL)
1371
+					    return CL_VIRUS;
1372
+					pt = pt->next_same;
1373
+					continue;
1367 1374
 				    }
1368 1375
 				}
1369 1376
 			    }
... ...
@@ -1403,11 +1410,18 @@ int cli_ac_scanbuff(const unsigned char *buffer, uint32_t length, const char **v
1403 1403
 				    pt = pt->next_same;
1404 1404
 				    continue;
1405 1405
 				} else {
1406
-				    if(virname)
1407
-					*virname = pt->virname;
1406
+				    if(virname) {
1407
+					if (ctx && SCAN_ALL && virname == ctx->virname)
1408
+					    cli_append_virus(ctx, pt->virname);
1409
+					else
1410
+					    *virname = pt->virname;
1411
+				    }
1408 1412
 				    if(customdata)
1409 1413
 					*customdata = pt->customdata;
1410
-				    return CL_VIRUS;
1414
+				    if (!ctx || !SCAN_ALL)
1415
+					return CL_VIRUS;
1416
+				    pt = pt->next_same;
1417
+				    continue;
1411 1418
 				}
1412 1419
 			    }
1413 1420
 			}
... ...
@@ -93,7 +93,7 @@ int cli_ac_initdata(struct cli_ac_data *data, uint32_t partsigs, uint32_t lsigs,
93 93
 void cli_ac_chkmacro(struct cli_matcher *root, struct cli_ac_data *data, unsigned lsigid1);
94 94
 int cli_ac_chklsig(const char *expr, const char *end, uint32_t *lsigcnt, unsigned int *cnt, uint64_t *ids, unsigned int parse_only);
95 95
 void cli_ac_freedata(struct cli_ac_data *data);
96
-int cli_ac_scanbuff(const unsigned char *buffer, uint32_t length, const char **virname, void **customdata, struct cli_ac_result **res, const struct cli_matcher *root, struct cli_ac_data *mdata, uint32_t offset, cli_file_t ftype, struct cli_matched_type **ftoffset, unsigned int mode, const cli_ctx *ctx);
96
+int cli_ac_scanbuff(const unsigned char *buffer, uint32_t length, const char **virname, void **customdata, struct cli_ac_result **res, const struct cli_matcher *root, struct cli_ac_data *mdata, uint32_t offset, cli_file_t ftype, struct cli_matched_type **ftoffset, unsigned int mode, cli_ctx *ctx);
97 97
 int cli_ac_buildtrie(struct cli_matcher *root);
98 98
 int cli_ac_init(struct cli_matcher *root, uint8_t mindepth, uint8_t maxdepth, uint8_t dconf_prefiltering);
99 99
 int cli_ac_caloff(const struct cli_matcher *root, struct cli_ac_data *data, const struct cli_target_info *info);
... ...
@@ -244,7 +244,7 @@ void cli_bm_free(struct cli_matcher *root)
244 244
     }
245 245
 }
246 246
 
247
-int cli_bm_scanbuff(const unsigned char *buffer, uint32_t length, const char **virname, const struct cli_bm_patt **patt, const struct cli_matcher *root, uint32_t offset, const struct cli_target_info *info, struct cli_bm_off *offdata)
247
+int cli_bm_scanbuff(const unsigned char *buffer, uint32_t length, const char **virname, const struct cli_bm_patt **patt, const struct cli_matcher *root, uint32_t offset, const struct cli_target_info *info, struct cli_bm_off *offdata, uint32_t *viroffset)
248 248
 {
249 249
 	uint32_t i, j, off, off_min, off_max;
250 250
 	uint8_t found, pchain, shift;
... ...
@@ -374,8 +374,11 @@ int cli_bm_scanbuff(const unsigned char *buffer, uint32_t length, const char **v
374 374
 			    continue;
375 375
 			}
376 376
 		    }
377
-		    if(virname)
377
+		    if(virname) {
378 378
 			*virname = p->virname;
379
+			if(viroffset)
380
+			    *viroffset = offset + i + j - BM_MIN_LENGTH + BM_BLOCK_SIZE;
381
+		    }
379 382
 		    if(patt)
380 383
 			*patt = p;
381 384
 		    return CL_VIRUS;
... ...
@@ -47,7 +47,7 @@ int cli_bm_addpatt(struct cli_matcher *root, struct cli_bm_patt *pattern, const
47 47
 int cli_bm_init(struct cli_matcher *root);
48 48
 int cli_bm_initoff(const struct cli_matcher *root, struct cli_bm_off *data, const struct cli_target_info *info);
49 49
 void cli_bm_freeoff(struct cli_bm_off *data);
50
-int cli_bm_scanbuff(const unsigned char *buffer, uint32_t length, const char **virname, const struct cli_bm_patt **patt, const struct cli_matcher *root, uint32_t offset, const struct cli_target_info *info, struct cli_bm_off *offdata);
50
+int cli_bm_scanbuff(const unsigned char *buffer, uint32_t length, const char **virname, const struct cli_bm_patt **patt, const struct cli_matcher *root, uint32_t offset, const struct cli_target_info *info, struct cli_bm_off *offdata, uint32_t *viroffset);
51 51
 void cli_bm_free(struct cli_matcher *root);
52 52
 
53 53
 #endif
... ...
@@ -91,13 +91,16 @@ static inline int matcher_run(const struct cli_matcher *root,
91 91
 			      unsigned int acmode,
92 92
 			      struct cli_ac_result **acres,
93 93
 			      fmap_t *map,
94
-			      struct cli_bm_off *offdata)
94
+			      struct cli_bm_off *offdata,
95
+			      uint32_t *viroffset,
96
+			      cli_ctx *ctx)
95 97
 {
96 98
     int ret;
97 99
     int32_t pos = 0;
98 100
     struct filter_match_info info;
99 101
     uint32_t orig_length, orig_offset;
100 102
     const unsigned char* orig_buffer;
103
+    unsigned int viruses_found = 0;
101 104
 
102 105
     if (root->filter) {
103 106
 	if(filter_search_ext(root->filter, buffer, length, &info) == -1) {
... ...
@@ -128,15 +131,30 @@ static inline int matcher_run(const struct cli_matcher *root,
128 128
 	    /* Don't use prefiltering for BM offset mode, since BM keeps tracks
129 129
 	     * of offsets itself, and doesn't work if we skip chunks of input
130 130
 	     * data */
131
-	    ret = cli_bm_scanbuff(orig_buffer, orig_length, virname, NULL, root, orig_offset, tinfo, offdata);
131
+	    ret = cli_bm_scanbuff(orig_buffer, orig_length, virname, NULL, root, orig_offset, tinfo, offdata, viroffset);
132 132
 	} else {
133
-	    ret = cli_bm_scanbuff(buffer, length, virname, NULL, root, offset, tinfo, offdata);
133
+	    ret = cli_bm_scanbuff(buffer, length, virname, NULL, root, offset, tinfo, offdata, viroffset);
134
+	}
135
+	if (ret == CL_VIRUS) {
136
+	    if (ctx) {
137
+		cli_append_virus(ctx, *virname);
138
+#if 1
139
+		if (SCAN_ALL)
140
+		    viruses_found++;
141
+		else
142
+#endif
143
+		    return ret;
144
+	    }
134 145
 	}
135
-	if (ret == CL_VIRUS)
136
-	    return ret;
137 146
     }
138 147
     PERF_LOG_TRIES(acmode, 0, length);
139 148
     ret = cli_ac_scanbuff(buffer, length, virname, NULL, acres, root, mdata, offset, ftype, ftoffset, acmode, NULL);
149
+#if 1
150
+    if (ctx && ret == CL_VIRUS)
151
+	cli_append_virus(ctx, *virname);
152
+    if (ctx && SCAN_ALL && viruses_found)
153
+	return CL_VIRUS;
154
+#endif
140 155
     return ret;
141 156
 }
142 157
 
... ...
@@ -146,7 +164,7 @@ int cli_scanbuff(const unsigned char *buffer, uint32_t length, uint32_t offset,
146 146
 	unsigned int i;
147 147
 	struct cli_ac_data mdata;
148 148
 	struct cli_matcher *groot, *troot = NULL;
149
-	const char **virname=ctx->virname;
149
+	const char *virname = NULL;
150 150
 	const struct cl_engine *engine=ctx->engine;
151 151
 
152 152
     if(!engine) {
... ...
@@ -170,8 +188,10 @@ int cli_scanbuff(const unsigned char *buffer, uint32_t length, uint32_t offset,
170 170
 	if(!acdata && (ret = cli_ac_initdata(&mdata, troot->ac_partsigs, troot->ac_lsigs, troot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN)))
171 171
 	    return ret;
172 172
 
173
-	ret = matcher_run(troot, buffer, length, virname, acdata ? (acdata[0]): (&mdata), offset, NULL, ftype, NULL, AC_SCAN_VIR, NULL, *ctx->fmap, NULL);
173
+	ret = matcher_run(troot, buffer, length, &virname, acdata ? (acdata[0]): (&mdata), offset, NULL, ftype, NULL, AC_SCAN_VIR, NULL, *ctx->fmap, NULL, NULL, ctx);
174 174
 
175
+	//	if (virname)
176
+	//	    cli_append_virus(ctx, virname);
175 177
 	if(!acdata)
176 178
 	    cli_ac_freedata(&mdata);
177 179
 
... ...
@@ -179,11 +199,15 @@ int cli_scanbuff(const unsigned char *buffer, uint32_t length, uint32_t offset,
179 179
 	    return ret;
180 180
     }
181 181
 
182
+    virname = NULL;
183
+
182 184
     if(!acdata && (ret = cli_ac_initdata(&mdata, groot->ac_partsigs, groot->ac_lsigs, groot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN)))
183 185
 	return ret;
184 186
 
185
-    ret = matcher_run(groot, buffer, length, virname, acdata ? (acdata[1]): (&mdata), offset, NULL, ftype, NULL, AC_SCAN_VIR, NULL, *ctx->fmap, NULL);
187
+    ret = matcher_run(groot, buffer, length, &virname, acdata ? (acdata[1]): (&mdata), offset, NULL, ftype, NULL, AC_SCAN_VIR, NULL, *ctx->fmap, NULL, NULL, ctx);
186 188
 
189
+    //  if (virname)
190
+    //	cli_append_virus(ctx, virname);
187 191
     if(!acdata)
188 192
 	cli_ac_freedata(&mdata);
189 193
 
... ...
@@ -412,11 +436,12 @@ int cli_checkfp(unsigned char *digest, size_t size, cli_ctx *ctx)
412 412
 	for(i = 0; i < 16; i++)
413 413
 	    sprintf(md5 + i * 2, "%02x", digest[i]);
414 414
 	md5[32] = 0;
415
-	cli_dbgmsg("FP SIGNATURE: %s:%u:%s\n", md5, (unsigned int) size, *ctx->virname ? *ctx->virname : "Name");
415
+	cli_dbgmsg("FP SIGNATURE: %s:%u:%s\n", md5, (unsigned int) size,
416
+		   cli_get_last_virus(ctx) ? cli_get_last_virus(ctx) : "Name");
416 417
     }
417 418
 
418
-    if(ctx->virname)
419
-	do_dsig_check = strncmp("W32S.", *ctx->virname, 5);
419
+    if(cli_get_last_virus(ctx))
420
+	do_dsig_check = strncmp("W32S.", cli_get_last_virus(ctx), 5);
420 421
 
421 422
     map = *ctx->fmap;
422 423
     have_sha1 = cli_hm_have_size(ctx->engine->hm_fp, CLI_HASH_SHA1, size) || (cli_hm_have_size(ctx->engine->hm_fp, CLI_HASH_SHA1, 1) && do_dsig_check);
... ...
@@ -487,7 +512,7 @@ int cli_checkfp(unsigned char *digest, size_t size, cli_ctx *ctx)
487 487
 	}
488 488
     }
489 489
     if (ctx->engine->cb_hash)
490
-	ctx->engine->cb_hash(fmap_fd(*ctx->fmap), size, md5, ctx->virname ? *ctx->virname : NULL, ctx->cb_ctx);
490
+	ctx->engine->cb_hash(fmap_fd(*ctx->fmap), size, md5, cli_get_last_virus(ctx), ctx->cb_ctx);
491 491
 
492 492
     return CL_VIRUS;
493 493
 }
... ...
@@ -572,6 +597,7 @@ int cli_lsig_eval(cli_ctx *ctx, struct cli_matcher *root, struct cli_ac_data *ac
572 572
 	unsigned int i, evalcnt;
573 573
 	uint64_t evalids;
574 574
 	fmap_t *map = *ctx->fmap;
575
+	unsigned int viruses_found = 0;
575 576
 
576 577
     for(i = 0; i < root->ac_lsigs; i++) {
577 578
 	evalcnt = 0;
... ...
@@ -598,6 +624,10 @@ int cli_lsig_eval(cli_ctx *ctx, struct cli_matcher *root, struct cli_ac_data *ac
598 598
 		    memcpy(ctx->handlertype_hash, hash, 16);
599 599
 		    if(cli_magic_scandesc_type(ctx, root->ac_lsigtable[i]->tdb.handlertype[0]) == CL_VIRUS) {
600 600
 			ctx->recursion--;
601
+			if (SCAN_ALL) {
602
+			    viruses_found++;
603
+			    continue;
604
+			}
601 605
 			return CL_VIRUS;
602 606
 		    }
603 607
 		    ctx->recursion--;
... ...
@@ -610,25 +640,41 @@ int cli_lsig_eval(cli_ctx *ctx, struct cli_matcher *root, struct cli_ac_data *ac
610 610
 		    continue;
611 611
 		if(matchicon(ctx, &target_info->exeinfo, root->ac_lsigtable[i]->tdb.icongrp1, root->ac_lsigtable[i]->tdb.icongrp2) == CL_VIRUS) {
612 612
 		    if(!root->ac_lsigtable[i]->bc_idx) {
613
-			if(ctx->virname)
614
-			    *ctx->virname = root->ac_lsigtable[i]->virname;
613
+			cli_append_virus(ctx, root->ac_lsigtable[i]->virname);
614
+			if (SCAN_ALL) {
615
+                            viruses_found++;
616
+                            continue;
617
+                        }
615 618
 			return CL_VIRUS;
616
-		    } else if(cli_bytecode_runlsig(ctx, target_info, &ctx->engine->bcs, root->ac_lsigtable[i]->bc_idx, ctx->virname, acdata->lsigcnt[i], acdata->lsigsuboff_first[i], map) == CL_VIRUS) {
619
+		    } else if(cli_bytecode_runlsig(ctx, target_info, &ctx->engine->bcs, root->ac_lsigtable[i]->bc_idx, acdata->lsigcnt[i], acdata->lsigsuboff_first[i], map) == CL_VIRUS) {
620
+			if (SCAN_ALL) {
621
+                            viruses_found++;
622
+                            continue;
623
+                        }
617 624
 			return CL_VIRUS;
618 625
 		    }
619 626
 		}
620 627
 		continue;
621 628
 	    }
622 629
 	    if(!root->ac_lsigtable[i]->bc_idx) {
623
-		if(ctx->virname)
624
-		    *ctx->virname = root->ac_lsigtable[i]->virname;
625
-		return CL_VIRUS;
630
+		cli_append_virus(ctx, root->ac_lsigtable[i]->virname);
631
+		if (SCAN_ALL) {
632
+		    viruses_found++;
633
+		    continue;
634
+		}
635
+ 		return CL_VIRUS;
626 636
 	    }
627
-	    if(cli_bytecode_runlsig(ctx, target_info, &ctx->engine->bcs, root->ac_lsigtable[i]->bc_idx, ctx->virname, acdata->lsigcnt[i], acdata->lsigsuboff_first[i], map) == CL_VIRUS) {
628
-		return CL_VIRUS;
637
+	    if(cli_bytecode_runlsig(ctx, target_info, &ctx->engine->bcs, root->ac_lsigtable[i]->bc_idx, acdata->lsigcnt[i], acdata->lsigsuboff_first[i], map) == CL_VIRUS) {
638
+		if (SCAN_ALL) {
639
+		    viruses_found++;
640
+		    continue;
641
+		}
642
+ 		return CL_VIRUS;
629 643
 	    }
630 644
 	}
631 645
     }
646
+    if (SCAN_ALL && viruses_found)
647
+	return CL_VIRUS;
632 648
     return CL_CLEAN;
633 649
 }
634 650
 
... ...
@@ -648,6 +694,9 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
648 648
 	struct cli_target_info info;
649 649
 	fmap_t *map = *ctx->fmap;
650 650
 	struct cli_matcher *hdb, *fp;
651
+	const char *virname = NULL;
652
+	uint32_t viroffset = 0;
653
+	uint32_t viruses_found = 0;
651 654
 
652 655
     if(!ctx->engine) {
653 656
 	cli_errmsg("cli_scandesc: engine == NULL\n");
... ...
@@ -749,9 +798,15 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
749 749
 	    *ctx->scanned += bytes / CL_COUNT_PRECISION;
750 750
 
751 751
 	if(troot) {
752
-	    ret = matcher_run(troot, buff, bytes, ctx->virname, &tdata, offset, &info, ftype, ftoffset, acmode, acres, map, bm_offmode ? &toff : NULL);
752
+            virname = NULL;
753
+            viroffset = 0;
754
+	    ret = matcher_run(troot, buff, bytes, &virname, &tdata, offset, &info, ftype, ftoffset, acmode, acres, map, bm_offmode ? &toff : NULL, &viroffset, ctx);
753 755
 
754
-	    if(ret == CL_VIRUS || ret == CL_EMEM) {
756
+	    if (virname) {
757
+		//		cli_append_virus(ctx, virname);
758
+		viruses_found++;
759
+	    }
760
+	    if((ret == CL_VIRUS && !SCAN_ALL) || ret == CL_EMEM) {
755 761
 		if(!ftonly)
756 762
 		    cli_ac_freedata(&gdata);
757 763
 		cli_ac_freedata(&tdata);
... ...
@@ -765,9 +820,15 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
765 765
 	}
766 766
 
767 767
 	if(!ftonly) {
768
-	    ret = matcher_run(groot, buff, bytes, ctx->virname, &gdata, offset, &info, ftype, ftoffset, acmode, acres, map, NULL);
768
+	    virname = NULL;
769
+	    viroffset = 0;
770
+	    ret = matcher_run(groot, buff, bytes, &virname, &gdata, offset, &info, ftype, ftoffset, acmode, acres, map, NULL, &viroffset, ctx);
769 771
 
770
-	    if(ret == CL_VIRUS || ret == CL_EMEM) {
772
+            if (virname) {
773
+		//              cli_append_virus(ctx, virname);
774
+		viruses_found++;
775
+	    }
776
+	    if((ret == CL_VIRUS && !SCAN_ALL) || ret == CL_EMEM) {
771 777
 		cli_ac_freedata(&gdata);
772 778
 		if(troot) {
773 779
 		    cli_ac_freedata(&tdata);
... ...
@@ -783,7 +844,7 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
783 783
 		    type = ret;
784 784
 	    }
785 785
 
786
-	    if(hdb) {
786
+	    if(hdb && !SCAN_ALL) {
787 787
 		const void *data = buff + maxpatlen * (offset!=0);
788 788
 		uint32_t data_len = bytes - maxpatlen * (offset!=0);
789 789
 
... ...
@@ -796,12 +857,17 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
796 796
 	    }
797 797
 	}
798 798
 
799
+	if(SCAN_ALL && viroffset) {
800
+	    offset = viroffset;
801
+	    continue;
802
+	}
799 803
 	if(bytes < SCANBUFF) break;
800 804
 	offset += bytes - maxpatlen;
801 805
     }
802 806
 
803 807
     if(!ftonly && hdb) {
804 808
 	enum CLI_HASH_TYPE hashtype;
809
+	unsigned int hvirs = 0, hfps = 0;
805 810
 
806 811
 	if(compute_hash[CLI_HASH_MD5])
807 812
 	    cli_md5_final(digest[CLI_HASH_MD5], &md5ctx);
... ...
@@ -812,31 +878,39 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
812 812
 	if(compute_hash[CLI_HASH_SHA256])
813 813
 	    sha256_final(&sha256ctx, digest[CLI_HASH_SHA256]);
814 814
 
815
+	virname = NULL;
815 816
 	for(hashtype = CLI_HASH_MD5; hashtype < CLI_HASH_AVAIL_TYPES; hashtype++) {
816
-	    if(compute_hash[hashtype] && (ret = cli_hm_scan(digest[hashtype], map->len, ctx->virname, hdb, hashtype)) == CL_VIRUS)
817
-		break;
818
-	}
817
+	    if(compute_hash[hashtype] && (ret = cli_hm_scan(digest[hashtype], map->len, &virname, hdb, hashtype)) == CL_VIRUS) {
819 818
 
820
-	if(ret == CL_VIRUS && fp) {
821
-	    for(hashtype = CLI_HASH_MD5; hashtype < CLI_HASH_AVAIL_TYPES; hashtype++) {
822
-		if(compute_hash[hashtype] && cli_hm_scan(digest[hashtype], map->len, ctx->virname, fp, hashtype) == CL_VIRUS) {
823
-		    ret = CL_CLEAN;
824
-		    break;
819
+		if(fp && cli_hm_scan(digest[hashtype], map->len, NULL, fp, hashtype) == CL_VIRUS) {
820
+		    hfps++;
821
+		    continue;
825 822
 		}
823
+		hvirs++;
824
+		cli_append_virus(ctx, virname);
825
+		virname = NULL;
826
+		if(!SCAN_ALL)
827
+		    break;
826 828
 	    }
827 829
 	}
830
+	if(hvirs > hfps)
831
+	    ret = CL_VIRUS;
832
+	else
833
+	    ret = CL_CLEAN;
828 834
     }
829 835
 
830 836
     if(troot) {
831
-	if(ret != CL_VIRUS)
837
+	if(ret != CL_VIRUS || SCAN_ALL)
832 838
 	    ret = cli_lsig_eval(ctx, troot, &tdata, &info, refhash);
839
+	if (ret == CL_VIRUS)
840
+	    viruses_found++;
833 841
 	cli_ac_freedata(&tdata);
834 842
 	if(bm_offmode)
835 843
 	    cli_bm_freeoff(&toff);
836 844
     }
837 845
 
838 846
     if(groot) {
839
-	if(ret != CL_VIRUS)
847
+	if(ret != CL_VIRUS || SCAN_ALL)
840 848
 	    ret = cli_lsig_eval(ctx, groot, &gdata, &info, refhash);
841 849
 	cli_ac_freedata(&gdata);
842 850
     }
... ...
@@ -845,6 +919,8 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
845 845
 	free(info.exeinfo.section);
846 846
     cli_hashset_destroy(&info.exeinfo.vinfo);
847 847
 
848
+    if (SCAN_ALL && viruses_found)
849
+	return CL_VIRUS;
848 850
     if(ret == CL_VIRUS)
849 851
 	return CL_VIRUS;
850 852
 
... ...
@@ -854,6 +930,7 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
854 854
 int cli_matchmeta(cli_ctx *ctx, const char *fname, size_t fsizec, size_t fsizer, int encrypted, unsigned int filepos, int res1, void *res2)
855 855
 {
856 856
 	const struct cli_cdb *cdb;
857
+	unsigned int viruses_found = 0;
857 858
 
858 859
     cli_dbgmsg("CDBNAME:%s:%lu:%s:%lu:%lu:%d:%u:%u:%p\n",
859 860
 	       cli_ftname(ctx->container_type), fsizec, fname, fsizec, fsizer, encrypted, filepos, res1, res2);
... ...
@@ -861,8 +938,11 @@ int cli_matchmeta(cli_ctx *ctx, const char *fname, size_t fsizec, size_t fsizer,
861 861
     if (ctx->engine && ctx->engine->cb_meta)
862 862
 	if (ctx->engine->cb_meta(cli_ftname(ctx->container_type), fsizec, fname, fsizer, encrypted, filepos, ctx->cb_ctx) == CL_VIRUS) {
863 863
 	    cli_dbgmsg("inner file blacklisted by callback: %s\n", fname);
864
-	    *ctx->virname = "Detected.By.Callback";
865
-	    return CL_VIRUS;
864
+
865
+	    cli_append_virus(ctx, "Detected.By.Callback");
866
+	    viruses_found++;
867
+	    if(!SCAN_ALL)
868
+		return CL_VIRUS;
866 869
 	}
867 870
 
868 871
     if(!ctx->engine || !(cdb = ctx->engine->cdb))
... ...
@@ -895,10 +975,14 @@ int cli_matchmeta(cli_ctx *ctx, const char *fname, size_t fsizec, size_t fsizer,
895 895
 	if(cdb->name.re_magic && (!fname || cli_regexec(&cdb->name, fname, 0, NULL, 0) == REG_NOMATCH))
896 896
 	    continue;
897 897
 
898
-	*ctx->virname = cdb->virname;
899
-	return CL_VIRUS;
898
+	cli_append_virus(ctx, cdb->virname);
899
+	viruses_found++;
900
+	if(!SCAN_ALL)
901
+	    return CL_VIRUS;
900 902
 
901 903
     } while((cdb = cdb->next));
902 904
 
905
+    if (SCAN_ALL && viruses_found)
906
+	return CL_VIRUS;
903 907
     return CL_CLEAN;
904 908
 }
... ...
@@ -544,11 +544,18 @@ cli_parse_mbox(const char *dir, cli_ctx *ctx)
544 544
 		 */
545 545
 		messageDestroy(body);
546 546
 	}
547
-
547
+	
548
+#if 0
548 549
 	if((retcode == CL_CLEAN) && ctx->found_possibly_unwanted && (*ctx->virname == NULL)) {
549
-		*ctx->virname = "Heuristics.Phishing.Email";
550
-		ctx->found_possibly_unwanted = 0;
551
-		retcode = CL_VIRUS;
550
+	    *ctx->virname = "Heuristics.Phishing.Email";
551
+#else
552
+	/* TBD: Breaks unit_test/check1_clamscan.sh and check2_clamd.sh w/SCAN_ALL */
553
+	if((retcode == CL_CLEAN) && ctx->found_possibly_unwanted &&
554
+	   (*ctx->virname == NULL || SCAN_ALL)) {
555
+	    cli_append_virus(ctx, "Heuristics.Phishing.Email");
556
+#endif
557
+	    ctx->found_possibly_unwanted = 0;
558
+	    retcode = CL_VIRUS;
552 559
 	}
553 560
 
554 561
 	cli_dbgmsg("cli_mbox returning %d\n", retcode);
... ...
@@ -770,6 +770,44 @@ int cli_unlink(const char *pathname)
770 770
 	return 0;
771 771
 }
772 772
 
773
+void cli_append_virus(cli_ctx * ctx, const char * virname)
774
+{
775
+    if (!ctx->virname)
776
+	return;
777
+    if (SCAN_ALL) {
778
+	if (ctx->size_viruses == 0) {
779
+	    ctx->size_viruses = 2;
780
+	    if (!(ctx->virname = malloc(ctx->size_viruses * sizeof(char *)))) {
781
+		cli_errmsg("cli_append_virus: fails on malloc() - virus %s virname not appended.\n", virname);
782
+		return;
783
+	    }
784
+	} else if (ctx->num_viruses+1 == ctx->size_viruses) {
785
+	    ctx->size_viruses *= 2;
786
+	    if ((ctx->virname = realloc((void *)ctx->virname, ctx->size_viruses * sizeof (char *))) == NULL) {
787
+		cli_errmsg("cli_append_virus: fails on realloc() - virus %s virname not appended.\n", virname);
788
+		return;
789
+	    }
790
+	}
791
+	ctx->virname[ctx->num_viruses++] = virname;
792
+	ctx->virname[ctx->num_viruses] = NULL;
793
+    }
794
+    else
795
+	*ctx->virname = virname;
796
+}
797
+
798
+const char * cli_get_last_virus(const cli_ctx * ctx)
799
+{
800
+    if (!ctx->virname)
801
+	return NULL;
802
+
803
+    if (SCAN_ALL && ctx->num_viruses) {
804
+	return ctx->virname[ctx->num_viruses-1];
805
+    }
806
+    else
807
+	return *ctx->virname;
808
+}
809
+
810
+
773 811
 #ifdef	C_WINDOWS
774 812
 /*
775 813
  * Windows doesn't allow you to delete a directory while it is still open
... ...
@@ -55,7 +55,7 @@
55 55
  * in re-enabling affected modules.
56 56
  */
57 57
 
58
-#define CL_FLEVEL 72
58
+#define CL_FLEVEL 73
59 59
 #define CL_FLEVEL_DCONF	CL_FLEVEL
60 60
 #define CL_FLEVEL_SIGTOOL CL_FLEVEL
61 61
 
... ...
@@ -111,6 +111,8 @@ typedef struct bitset_tag
111 111
 /* internal clamav context */
112 112
 typedef struct cli_ctx_tag {
113 113
     const char **virname;
114
+    unsigned int num_viruses;         /* manages virname when CL_SCAN_ALLMATCHES == 1 */
115
+    unsigned int size_viruses;        /* manages virname when CL_SCAN_ALLMATCHES == 1 */
114 116
     unsigned long int *scanned;
115 117
     const struct cli_matcher *root;
116 118
     const struct cl_engine *engine;
... ...
@@ -324,6 +326,7 @@ extern int have_rar;
324 324
 #define DETECT_BROKEN	    (ctx->options & CL_SCAN_BLOCKBROKEN)
325 325
 #define BLOCK_MACROS	    (ctx->options & CL_SCAN_BLOCKMACROS)
326 326
 #define SCAN_STRUCTURED	    (ctx->options & CL_SCAN_STRUCTURED)
327
+#define SCAN_ALL            (ctx->options & CL_SCAN_ALLMATCHES)
327 328
 
328 329
 /* based on macros from A. Melnikoff */
329 330
 #define cbswap16(v) (((v & 0xff) << 8) | (((v) >> 8) & 0xff))
... ...
@@ -422,6 +425,9 @@ static inline void cli_writeint32(char *offset, uint32_t value)
422 422
 }
423 423
 #endif
424 424
 
425
+void cli_append_virus(cli_ctx *ctx, const char *virname);
426
+const char *cli_get_last_virus(const cli_ctx *ctx);
427
+
425 428
 /* used by: spin, yc (C) aCaB */
426 429
 #define __SHIFTBITS(a) (sizeof(a)<<3)
427 430
 #define __SHIFTMASK(a) (__SHIFTBITS(a)-1)
... ...
@@ -584,7 +584,7 @@ static int run_pdf_hooks(struct pdf_struct *pdf, enum pdf_phase phase, int fd,
584 584
     cli_bytecode_context_setpdf(bc_ctx, phase, pdf->nobjs, pdf->objs,
585 585
 				&pdf->flags, pdf->size, pdf->startoff);
586 586
     cli_bytecode_context_setctx(bc_ctx, ctx);
587
-    ret = cli_bytecode_runhook(ctx, ctx->engine, bc_ctx, BC_PDF, map, ctx->virname);
587
+    ret = cli_bytecode_runhook(ctx, ctx->engine, bc_ctx, BC_PDF, map);
588 588
     cli_bytecode_context_destroy(bc_ctx);
589 589
     if (fd != -1) {
590 590
 	funmap(map);
... ...
@@ -2074,7 +2074,7 @@ int cli_pdf(const char *dir, cli_ctx *ctx, off_t offset)
2074 2074
 	/* It is encrypted, and a password/key needs to be supplied to decrypt.
2075 2075
 	 * This doesn't trigger for PDFs that are encrypted but don't need
2076 2076
 	 * a password to decrypt */
2077
-	*ctx->virname = "Heuristics.Encrypted.PDF";
2077
+	cli_append_virus(ctx, "Heuristics.Encrypted.PDF");
2078 2078
 	rc = CL_VIRUS;
2079 2079
     }
2080 2080
 
... ...
@@ -2096,7 +2096,7 @@ int cli_pdf(const char *dir, cli_ctx *ctx, off_t offset)
2096 2096
 	if (!rc && (ctx->options & CL_SCAN_ALGORITHMIC)) {
2097 2097
 	    if (pdf.flags & (1 << ESCAPED_COMMON_PDFNAME)) {
2098 2098
 		/* for example /Fl#61te#44#65#63#6f#64#65 instead of /FlateDecode */
2099
-		*ctx->virname = "Heuristics.PDF.ObfuscatedNameObject";
2099
+		cli_append_virus(ctx, "Heuristics.PDF.ObfuscatedNameObject");
2100 2100
 		rc = cli_found_possibly_unwanted(ctx);
2101 2101
 	    }
2102 2102
 	}
... ...
@@ -537,6 +537,8 @@ int cli_scanpe(cli_ctx *ctx)
537 537
 #ifdef HAVE__INTERNAL__SHA_COLLECT
538 538
 	int sha_collect = ctx->sha_collect;
539 539
 #endif
540
+	const char * virname = NULL;
541
+	uint32_t viruses_found = 0;
540 542
 
541 543
     if(!ctx) {
542 544
 	cli_errmsg("cli_scanpe: ctx == NULL\n");
... ...
@@ -557,8 +559,7 @@ int cli_scanpe(cli_ctx *ctx)
557 557
 	cli_dbgmsg("Can't read new header address\n");
558 558
 	/* truncated header? */
559 559
 	if(DETECT_BROKEN_PE) {
560
-	    if(ctx->virname)
561
-		*ctx->virname = "Heuristics.Broken.Executable";
560
+	    cli_append_virus(ctx,"Heuristics.Broken.Executable");
562 561
 	    return CL_VIRUS;
563 562
 	}
564 563
 	return CL_CLEAN;
... ...
@@ -687,8 +688,7 @@ int cli_scanpe(cli_ctx *ctx)
687 687
     nsections = EC16(file_hdr.NumberOfSections);
688 688
     if(nsections < 1 || nsections > 96) {
689 689
 	if(DETECT_BROKEN_PE) {
690
-	    if(ctx->virname)
691
-		*ctx->virname = "Heuristics.Broken.Executable";
690
+	    cli_append_virus(ctx,"Heuristics.Broken.Executable");
692 691
 	    return CL_VIRUS;
693 692
 	}
694 693
 	if(!ctx->corrupted_input) {
... ...
@@ -709,8 +709,7 @@ int cli_scanpe(cli_ctx *ctx)
709 709
     if (EC16(file_hdr.SizeOfOptionalHeader) < sizeof(struct pe_image_optional_hdr32)) {
710 710
         cli_dbgmsg("SizeOfOptionalHeader too small\n");
711 711
 	if(DETECT_BROKEN_PE) {
712
-	    if(ctx->virname)
713
-	        *ctx->virname = "Heuristics.Broken.Executable";
712
+	    cli_append_virus(ctx,"Heuristics.Broken.Executable");
714 713
 	    return CL_VIRUS;
715 714
 	}
716 715
 	return CL_CLEAN;
... ...
@@ -720,8 +719,7 @@ int cli_scanpe(cli_ctx *ctx)
720 720
     if(fmap_readn(map, &optional_hdr32, at, sizeof(struct pe_image_optional_hdr32)) != sizeof(struct pe_image_optional_hdr32)) {
721 721
         cli_dbgmsg("Can't read optional file header\n");
722 722
 	if(DETECT_BROKEN_PE) {
723
-	    if(ctx->virname)
724
-	        *ctx->virname = "Heuristics.Broken.Executable";
723
+	    cli_append_virus(ctx,"Heuristics.Broken.Executable");
725 724
 	    return CL_VIRUS;
726 725
 	}
727 726
 	return CL_CLEAN;
... ...
@@ -734,8 +732,7 @@ int cli_scanpe(cli_ctx *ctx)
734 734
 	    /* FIXME: need to play around a bit more with xp64 */
735 735
 	    cli_dbgmsg("Incorrect SizeOfOptionalHeader for PE32+\n");
736 736
 	    if(DETECT_BROKEN_PE) {
737
-	        if(ctx->virname)
738
-		    *ctx->virname = "Heuristics.Broken.Executable";
737
+		cli_append_virus(ctx,"Heuristics.Broken.Executable");
739 738
 		return CL_VIRUS;
740 739
 	    }
741 740
 	    return CL_CLEAN;
... ...
@@ -777,8 +774,7 @@ int cli_scanpe(cli_ctx *ctx)
777 777
         if(fmap_readn(map, &optional_hdr32 + 1, at, sizeof(struct pe_image_optional_hdr64) - sizeof(struct pe_image_optional_hdr32)) != sizeof(struct pe_image_optional_hdr64) - sizeof(struct pe_image_optional_hdr32)) {
778 778
 	    cli_dbgmsg("Can't read optional file header\n");
779 779
 	    if(DETECT_BROKEN_PE) {
780
-	        if(ctx->virname)
781
-		    *ctx->virname = "Heuristics.Broken.Executable";
780
+		cli_append_virus(ctx,"Heuristics.Broken.Executable");
782 781
 		return CL_VIRUS;
783 782
 	    }
784 783
 	    return CL_CLEAN;
... ...
@@ -858,15 +854,13 @@ int cli_scanpe(cli_ctx *ctx)
858 858
 
859 859
     if (DETECT_BROKEN_PE && !native && (!(pe_plus?EC32(optional_hdr64.SectionAlignment):EC32(optional_hdr32.SectionAlignment)) || (pe_plus?EC32(optional_hdr64.SectionAlignment):EC32(optional_hdr32.SectionAlignment))%0x1000)) {
860 860
         cli_dbgmsg("Bad virtual alignemnt\n");
861
-        if(ctx->virname)
862
-	    *ctx->virname = "Heuristics.Broken.Executable";
861
+	cli_append_virus(ctx,"Heuristics.Broken.Executable");
863 862
 	return CL_VIRUS;
864 863
     }
865 864
 
866 865
     if (DETECT_BROKEN_PE && !native && (!(pe_plus?EC32(optional_hdr64.FileAlignment):EC32(optional_hdr32.FileAlignment)) || (pe_plus?EC32(optional_hdr64.FileAlignment):EC32(optional_hdr32.FileAlignment))%0x200)) {
867 866
         cli_dbgmsg("Bad file alignemnt\n");
868
-	if(ctx->virname)
869
-	    *ctx->virname = "Heuristics.Broken.Executable";
867
+	cli_append_virus(ctx, "Heuristics.Broken.Executable");
870 868
 	return CL_VIRUS;
871 869
     }
872 870
 
... ...
@@ -896,8 +890,7 @@ int cli_scanpe(cli_ctx *ctx)
896 896
 	free(section_hdr);
897 897
 	free(exe_sections);
898 898
 	if(DETECT_BROKEN_PE) {
899
-	    if(ctx->virname)
900
-		*ctx->virname = "Heuristics.Broken.Executable";
899
+	    cli_append_virus(ctx,"Heuristics.Broken.Executable");
901 900
 	    return CL_VIRUS;
902 901
 	}
903 902
 	return CL_CLEAN;
... ...
@@ -964,8 +957,7 @@ int cli_scanpe(cli_ctx *ctx)
964 964
 
965 965
 	if (DETECT_BROKEN_PE && (!valign || (exe_sections[i].urva % valign))) { /* Bad virtual alignment */
966 966
 	    cli_dbgmsg("VirtualAddress is misaligned\n");
967
-	    if(ctx->virname)
968
-	        *ctx->virname = "Heuristics.Broken.Executable";
967
+	    cli_append_virus(ctx, "Heuristics.Broken.Executable");
969 968
 	    free(section_hdr);
970 969
 	    free(exe_sections);
971 970
 	    return CL_VIRUS;
... ...
@@ -977,8 +969,7 @@ int cli_scanpe(cli_ctx *ctx)
977 977
 		free(section_hdr);
978 978
 		free(exe_sections);
979 979
 		if(DETECT_BROKEN_PE) {
980
-		    if(ctx->virname)
981
-		        *ctx->virname = "Heuristics.Broken.Executable";
980
+		    cli_append_virus(ctx, "Heuristics.Broken.Executable");
982 981
 		    return CL_VIRUS;
983 982
 		}
984 983
 		return CL_CLEAN; /* no ninjas to see here! move along! */
... ...
@@ -992,15 +983,19 @@ int cli_scanpe(cli_ctx *ctx)
992 992
 		unsigned char md5_dig[16];
993 993
 		if(cli_hm_have_size(md5_sect, CLI_HASH_MD5, exe_sections[i].rsz) && 
994 994
 		   cli_md5sect(map, &exe_sections[i], md5_dig) &&
995
-		   cli_hm_scan(md5_dig, exe_sections[i].rsz, ctx->virname, md5_sect, CLI_HASH_MD5) == CL_VIRUS) {
995
+		   cli_hm_scan(md5_dig, exe_sections[i].rsz, &virname, md5_sect, CLI_HASH_MD5) == CL_VIRUS) {
996 996
 		    if(cli_hm_scan(md5_dig, fsize, NULL, ctx->engine->hm_fp, CLI_HASH_MD5) != CL_VIRUS) {
997
-			free(section_hdr);
998
-			free(exe_sections);
999
-			return CL_VIRUS;
997
+			if (!SCAN_ALL) {
998
+			    free(section_hdr);
999
+			    free(exe_sections);
1000
+			    return CL_VIRUS;
1001
+			}
1000 1002
 		    }
1003
+		    cli_append_virus(ctx, virname);
1004
+		    viruses_found++;
1001 1005
 		}
1002 1006
 	    }
1003
-
1007
+	    
1004 1008
 	}
1005 1009
 
1006 1010
 	if (exe_sections[i].urva>>31 || exe_sections[i].uvsz>>31 || (exe_sections[i].rsz && exe_sections[i].uraw>>31) || exe_sections[i].ursz>>31) {
... ...
@@ -1008,8 +1003,7 @@ int cli_scanpe(cli_ctx *ctx)
1008 1008
 	    free(section_hdr);
1009 1009
 	    free(exe_sections);
1010 1010
 	    if(DETECT_BROKEN_PE) {
1011
-	        if(ctx->virname)
1012
-		    *ctx->virname = "Heuristics.Broken.Executable";
1011
+		cli_append_virus(ctx, "Heuristics.Broken.Executable");
1013 1012
 		return CL_VIRUS;
1014 1013
 	    }
1015 1014
 	    return CL_CLEAN;
... ...
@@ -1018,8 +1012,7 @@ int cli_scanpe(cli_ctx *ctx)
1018 1018
 	if(!i) {
1019 1019
 	    if (DETECT_BROKEN_PE && exe_sections[i].urva!=hdr_size) { /* Bad first section RVA */
1020 1020
 	        cli_dbgmsg("First section is in the wrong place\n");
1021
-	        if(ctx->virname)
1022
-		    *ctx->virname = "Heuristics.Broken.Executable";
1021
+		cli_append_virus(ctx, "Heuristics.Broken.Executable");
1023 1022
 		free(section_hdr);
1024 1023
 		free(exe_sections);
1025 1024
 		return CL_VIRUS;
... ...
@@ -1029,8 +1022,7 @@ int cli_scanpe(cli_ctx *ctx)
1029 1029
 	} else {
1030 1030
 	    if (DETECT_BROKEN_PE && exe_sections[i].urva - exe_sections[i-1].urva != exe_sections[i-1].vsz) { /* No holes, no overlapping, no virtual disorder */
1031 1031
 	        cli_dbgmsg("Virtually misplaced section (wrong order, overlapping, non contiguous)\n");
1032
-	        if(ctx->virname)
1033
-		    *ctx->virname = "Heuristics.Broken.Executable";
1032
+		cli_append_virus(ctx, "Heuristics.Broken.Executable");
1034 1033
 		free(section_hdr);
1035 1034
 		free(exe_sections);
1036 1035
 		return CL_VIRUS;
... ...
@@ -1051,8 +1043,7 @@ int cli_scanpe(cli_ctx *ctx)
1051 1051
 	cli_dbgmsg("EntryPoint out of file\n");
1052 1052
 	free(exe_sections);
1053 1053
 	if(DETECT_BROKEN_PE) {
1054
-	    if(ctx->virname)
1055
-		*ctx->virname = "Heuristics.Broken.Executable";
1054
+	    cli_append_virus(ctx,"Heuristics.Broken.Executable");
1056 1055
 	    return CL_VIRUS;
1057 1056
 	}
1058 1057
 	return CL_CLEAN;
... ...
@@ -1112,7 +1103,7 @@ int cli_scanpe(cli_ctx *ctx)
1112 1112
     }
1113 1113
     cli_bytecode_context_setpe(bc_ctx, &pedata, exe_sections);
1114 1114
     cli_bytecode_context_setctx(bc_ctx, ctx);
1115
-    ret = cli_bytecode_runhook(ctx, ctx->engine, bc_ctx, BC_PE_ALL, map, ctx->virname);
1115
+    ret = cli_bytecode_runhook(ctx, ctx->engine, bc_ctx, BC_PE_ALL, map);
1116 1116
     switch (ret) {
1117 1117
         case CL_ENULLARG:
1118 1118
             cli_warnmsg("cli_scanpe: NULL argument supplied\n");
... ...
@@ -1132,9 +1123,12 @@ int cli_scanpe(cli_ctx *ctx)
1132 1132
 	if(pt) {
1133 1133
 	    pt += 15;
1134 1134
 	    if((((uint32_t)cli_readint32(pt) ^ (uint32_t)cli_readint32(pt + 4)) == 0x505a4f) && (((uint32_t)cli_readint32(pt + 8) ^ (uint32_t)cli_readint32(pt + 12)) == 0xffffb) && (((uint32_t)cli_readint32(pt + 16) ^ (uint32_t)cli_readint32(pt + 20)) == 0xb8)) {
1135
-	        *ctx->virname = "Heuristics.W32.Parite.B";
1136
-		free(exe_sections);
1137
-		return CL_VIRUS;
1135
+	        cli_append_virus(ctx,"Heuristics.W32.Parite.B");
1136
+		if (!SCAN_ALL) {
1137
+		    free(exe_sections);
1138
+		    return CL_VIRUS;
1139
+		}
1140
+		viruses_found++;
1138 1141
 	    }
1139 1142
 	}
1140 1143
     }
... ...
@@ -1215,9 +1209,11 @@ int cli_scanpe(cli_ctx *ctx)
1215 1215
 		break;
1216 1216
 	    case KZSLOOP:
1217 1217
 		if (op==kzdsize+0x48 && *kzcode==0x75 && kzlen-(int8_t)kzcode[1]-3<=kzinitlen && kzlen-(int8_t)kzcode[1]>=kzxorlen) {
1218
-		    *ctx->virname = "Heuristics.W32.Kriz";
1218
+		    cli_append_virus(ctx,"Heuristics.W32.Kriz");
1219 1219
 		    free(exe_sections);
1220
-		    return CL_VIRUS;
1220
+		    if (!SCAN_ALL)
1221
+			return CL_VIRUS;
1222
+		    viruses_found++;
1221 1223
 		}
1222 1224
 		cli_dbgmsg("kriz: loop out of bounds, corrupted sample?\n");
1223 1225
 		kzstate++;
... ...
@@ -1242,9 +1238,11 @@ int cli_scanpe(cli_ctx *ctx)
1242 1242
 
1243 1243
 	    if((tbuff = fmap_need_off_once(map, exe_sections[nsections - 1].raw + rsize - bw, 4096))) {
1244 1244
 		if(cli_memstr(tbuff, 4091, "\xe8\x2c\x61\x00\x00", 5)) {
1245
-		    *ctx->virname = dam ? "Heuristics.W32.Magistr.A.dam" : "Heuristics.W32.Magistr.A";
1245
+		    cli_append_virus(ctx, dam ? "Heuristics.W32.Magistr.A.dam" : "Heuristics.W32.Magistr.A");
1246 1246
 		    free(exe_sections);
1247
-		    return CL_VIRUS;
1247
+		    if (!SCAN_ALL)
1248
+			return CL_VIRUS;
1249
+		    viruses_found++;
1248 1250
 		}
1249 1251
 	    }
1250 1252
 
... ...
@@ -1254,9 +1252,11 @@ int cli_scanpe(cli_ctx *ctx)
1254 1254
 
1255 1255
 	    if((tbuff = fmap_need_off_once(map, exe_sections[nsections - 1].raw + rsize - bw, 4096))) {
1256 1256
 		if(cli_memstr(tbuff, 4091, "\xe8\x04\x72\x00\x00", 5)) {
1257
-		    *ctx->virname = dam ? "Heuristics.W32.Magistr.B.dam" : "Heuristics.W32.Magistr.B";
1257
+		    cli_append_virus(ctx,dam ? "Heuristics.W32.Magistr.B.dam" : "Heuristics.W32.Magistr.B");
1258 1258
 		    free(exe_sections);
1259
-		    return CL_VIRUS;
1259
+		    if (!SCAN_ALL)
1260
+			return CL_VIRUS;
1261
+		    viruses_found++;
1260 1262
 		} 
1261 1263
 	    }
1262 1264
 	}
... ...
@@ -1302,10 +1302,12 @@ int cli_scanpe(cli_ctx *ctx)
1302 1302
 	for(i=0;i<xsjs;i++) {
1303 1303
 	    if(!(code = fmap_need_off_once(map, jumps[i], 9))) continue;
1304 1304
 	    if((jump=cli_readint32(code))==0x60ec8b55 || (code[4]==0x0ec && ((jump==0x83ec8b55 && code[6]==0x60) || (jump==0x81ec8b55 && !code[7] && !code[8])))) {
1305
-		*ctx->virname = "Heuristics.W32.Polipos.A";
1305
+		cli_append_virus(ctx,"Heuristics.W32.Polipos.A");
1306 1306
 		free(jumps);
1307 1307
 		free(exe_sections);
1308
-		return CL_VIRUS;
1308
+		if (!SCAN_ALL)
1309
+		    return CL_VIRUS;
1310
+		viruses_found++;
1309 1311
 	    }
1310 1312
 	}
1311 1313
 	free(jumps);
... ...
@@ -1324,13 +1326,16 @@ int cli_scanpe(cli_ctx *ctx)
1324 1324
 		    else {
1325 1325
 			    cli_parseres_special(EC32(dirs[2].VirtualAddress), EC32(dirs[2].VirtualAddress), map, exe_sections, nsections, fsize, hdr_size, 0, 0, &m, stats);
1326 1326
 			    if ((ret = cli_detect_swizz(stats)) == CL_VIRUS) {
1327
-				    *ctx->virname = "Heuristics.Trojan.Swizzor.Gen";
1327
+				cli_append_virus(ctx,"Heuristics.Trojan.Swizzor.Gen");
1328 1328
 			    }
1329 1329
 			    free(stats);
1330 1330
 		    }
1331 1331
 		    if (ret != CL_CLEAN) {
1332
+			if (!(ret == CL_VIRUS && SCAN_ALL)) {
1332 1333
 			    free(exe_sections);
1333 1334
 			    return ret;
1335
+			}
1336
+			viruses_found++;
1334 1337
 		    }
1335 1338
 	    }
1336 1339
     }
... ...
@@ -2281,7 +2286,7 @@ int cli_scanpe(cli_ctx *ctx)
2281 2281
     }
2282 2282
     cli_bytecode_context_setpe(bc_ctx, &pedata, exe_sections);
2283 2283
     cli_bytecode_context_setctx(bc_ctx, ctx);
2284
-    ret = cli_bytecode_runhook(ctx, ctx->engine, bc_ctx, BC_PE_UNPACKER, map, ctx->virname);
2284
+    ret = cli_bytecode_runhook(ctx, ctx->engine, bc_ctx, BC_PE_UNPACKER, map);
2285 2285
     switch (ret) {
2286 2286
 	case CL_VIRUS:
2287 2287
 	    free(exe_sections);
... ...
@@ -2299,6 +2304,8 @@ int cli_scanpe(cli_ctx *ctx)
2299 2299
     }
2300 2300
 
2301 2301
     free(exe_sections);
2302
+    if (SCAN_ALL && viruses_found)
2303
+	return CL_VIRUS;
2302 2304
     return CL_CLEAN;
2303 2305
 }
2304 2306
 
... ...
@@ -1513,8 +1513,7 @@ static int parseicon(icon_groupset *set, uint32_t rva, cli_ctx *ctx, struct cli_
1513 1513
 	if(confidence >= positivematch) {
1514 1514
 	    cli_dbgmsg("confidence: %u\n", confidence);
1515 1515
 
1516
-	    if(ctx->virname) 
1517
-		*ctx->virname = matcher->icons[enginesize][x].name;
1516
+	    cli_append_virus(ctx,matcher->icons[enginesize][x].name);
1518 1517
 	    return CL_VIRUS;
1519 1518
 	}
1520 1519
     }
... ...
@@ -735,7 +735,7 @@ int phishingScan(cli_ctx* ctx,tag_arguments_t* hrefs)
735 735
 	if(!pchk || pchk->is_disabled)
736 736
 		return CL_CLEAN;
737 737
 
738
-	if(!ctx->found_possibly_unwanted)
738
+	if(!ctx->found_possibly_unwanted && !SCAN_ALL)
739 739
 		*ctx->virname=NULL;
740 740
 #if 0
741 741
 	FILE *f = fopen("/home/edwin/quarantine/urls","r");
... ...
@@ -810,29 +810,29 @@ int phishingScan(cli_ctx* ctx,tag_arguments_t* hrefs)
810 810
 				case CL_PHISH_CLEAN:
811 811
 					continue;
812 812
 				case CL_PHISH_NUMERIC_IP:
813
-					*ctx->virname="Heuristics.Phishing.Email.Cloaked.NumericIP";
813
+				    cli_append_virus(ctx, "Heuristics.Phishing.Email.Cloaked.NumericIP");
814 814
 					break;
815 815
 				case CL_PHISH_CLOAKED_NULL:
816
-					*ctx->virname="Heuristics.Phishing.Email.Cloaked.Null";/*fakesite%01%00@fake.example.com*/
816
+				    cli_append_virus(ctx, "Heuristics.Phishing.Email.Cloaked.Null");/*fakesite%01%00@fake.example.com*/
817 817
 					break;
818 818
 				case CL_PHISH_SSL_SPOOF:
819
-					*ctx->virname="Heuristics.Phishing.Email.SSL-Spoof";
819
+				    cli_append_virus(ctx, "Heuristics.Phishing.Email.SSL-Spoof");
820 820
 					break;
821 821
 				case CL_PHISH_CLOAKED_UIU:
822
-					*ctx->virname="Heuristics.Phishing.Email.Cloaked.Username";/*http://banksite@fake.example.com*/
822
+				    cli_append_virus(ctx, "Heuristics.Phishing.Email.Cloaked.Username");/*http://banksite@fake.example.com*/
823 823
 					break;
824 824
 				case CL_PHISH_HASH0:
825
-					*ctx->virname="Heuristics.Safebrowsing.Suspected-malware_safebrowsing.clamav.net";
825
+				    cli_append_virus(ctx, "Heuristics.Safebrowsing.Suspected-malware_safebrowsing.clamav.net");
826 826
 					break;
827 827
 				case CL_PHISH_HASH1:
828
-					*ctx->virname="Heuristics.Phishing.URL.Blacklisted";
828
+				    cli_append_virus(ctx, "Heuristics.Phishing.URL.Blacklisted");
829 829
 					break;
830 830
 				case CL_PHISH_HASH2:
831
-					*ctx->virname="Heuristics.Safebrowsing.Suspected-phishing_safebrowsing.clamav.net";
831
+				    cli_append_virus(ctx, "Heuristics.Safebrowsing.Suspected-phishing_safebrowsing.clamav.net");
832 832
 					break;
833 833
 				case CL_PHISH_NOMATCH:
834 834
 				default:
835
-					*ctx->virname="Heuristics.Phishing.Email.SpoofedDomain";
835
+				    cli_append_virus(ctx, "Heuristics.Phishing.Email.SpoofedDomain");
836 836
 					break;
837 837
 			}
838 838
 			return cli_found_possibly_unwanted(ctx);
... ...
@@ -1207,14 +1207,14 @@ static int hash_match(const struct regex_matcher *rlist, const char *host, size_
1207 1207
 	    cli_dbgmsg("Looking up hash %s for %s(%u)%s(%u)\n", h, host, (unsigned)hlen, path, (unsigned)plen);
1208 1208
 #if 0
1209 1209
 	    if (prefix_matched) {
1210
-		if (cli_bm_scanbuff(sha256_dig, 4, &virname, NULL, &rlist->hostkey_prefix,0,NULL,NULL) == CL_VIRUS) {
1210
+		if (cli_bm_scanbuff(sha256_dig, 4, &virname, NULL, &rlist->hostkey_prefix,0,NULL,NULL,NULL) == CL_VIRUS) {
1211 1211
 		    cli_dbgmsg("prefix matched\n");
1212 1212
 		    *prefix_matched = 1;
1213 1213
 		} else
1214 1214
 		    return CL_SUCCESS;
1215 1215
 	    }
1216 1216
 #endif
1217
-	    if (cli_bm_scanbuff(sha256_dig, 32, &virname, NULL, &rlist->sha256_hashes,0,NULL,NULL) == CL_VIRUS) {
1217
+	    if (cli_bm_scanbuff(sha256_dig, 32, &virname, NULL, &rlist->sha256_hashes,0,NULL,NULL,NULL) == CL_VIRUS) {
1218 1218
 		cli_dbgmsg("This hash matched: %s\n", h);
1219 1219
 		switch(*virname) {
1220 1220
 		    case 'W':
... ...
@@ -489,7 +489,7 @@ static int cli_chkign(const struct cli_matcher *ignored, const char *signame, co
489 489
     if(!ignored || !signame || !entry)
490 490
 	return 0;
491 491
 
492
-    if(cli_bm_scanbuff((const unsigned char *) signame, strlen(signame), &md5_expected, NULL, ignored, 0, NULL, NULL) == CL_VIRUS) {
492
+    if(cli_bm_scanbuff((const unsigned char *) signame, strlen(signame), &md5_expected, NULL, ignored, 0, NULL, NULL,NULL) == CL_VIRUS) {
493 493
 	if(md5_expected) {
494 494
 	    cli_md5_init(&md5ctx);
495 495
             cli_md5_update(&md5ctx, entry, strlen(entry));
... ...
@@ -357,7 +357,7 @@ static int add_hash(struct regex_matcher *matcher, char* pattern, const char fl,
357 357
 
358 358
 	if (fl != 'W' && pat->length == 32 &&
359 359
 	    cli_hashset_contains(&matcher->sha256_pfx_set, cli_readint32(pat->pattern)) &&
360
-	    cli_bm_scanbuff(pat->pattern, 32, &vname, NULL, &matcher->sha256_hashes,0,NULL,NULL) == CL_VIRUS) {
360
+	    cli_bm_scanbuff(pat->pattern, 32, &vname, NULL, &matcher->sha256_hashes,0,NULL,NULL,NULL) == CL_VIRUS) {
361 361
 	    if (*vname == 'W') {
362 362
 		/* hash is whitelisted in local.gdb */
363 363
 		cli_dbgmsg("Skipping hash %s\n", pattern);
... ...
@@ -118,7 +118,7 @@ static int cli_scandir(const char *dirname, cli_ctx *ctx)
118 118
 #endif
119 119
 	STATBUF statbuf;
120 120
 	char *fname;
121
-
121
+	unsigned int viruses_found = 0;
122 122
 
123 123
     if((dd = opendir(dirname)) != NULL) {
124 124
 #ifdef HAVE_READDIR_R_3
... ...
@@ -145,29 +145,43 @@ static int cli_scandir(const char *dirname, cli_ctx *ctx)
145 145
 			if(S_ISDIR(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode)) {
146 146
 			    if(cli_scandir(fname, ctx) == CL_VIRUS) {
147 147
 				free(fname);
148
-				closedir(dd);
149
-				return CL_VIRUS;
150
-			    }
148
+
149
+				if (SCAN_ALL) {
150
+				    viruses_found++;
151
+				    continue;
152
+				}
153
+
154
+                                closedir(dd);
155
+                                return CL_VIRUS;
156
+ 			    }
151 157
 			} else {
152 158
 			    if(S_ISREG(statbuf.st_mode)) {
153 159
 				if(cli_scanfile(fname, ctx) == CL_VIRUS) {
154 160
 				    free(fname);
155
-				    closedir(dd);
156
-				    return CL_VIRUS;
157
-				}
161
+
162
+				    if (SCAN_ALL) {
163
+					viruses_found++;
164
+					continue;
165
+				    }
166
+
167
+                                    closedir(dd);
168
+                                    return CL_VIRUS;
169
+ 				}
158 170
 			    }
159 171
 			}
160 172
 		    }
161 173
 		    free(fname);
162 174
 		}
163 175
 	    }
164
-	}
176
+	} 
165 177
     } else {
166 178
 	cli_dbgmsg("cli_scandir: Can't open directory %s.\n", dirname);
167 179
 	return CL_EOPEN;
168 180
     }
169 181
 
170 182
     closedir(dd);
183
+    if (SCAN_ALL && viruses_found)
184
+	return CL_VIRUS;
171 185
     return CL_CLEAN;
172 186
 }
173 187
 
... ...
@@ -194,7 +208,7 @@ static int cli_unrar_scanmetadata(int desc, unrar_metadata_t *metadata, cli_ctx
194 194
 	cli_dbgmsg("RAR: Encrypted files found in archive.\n");
195 195
 	ret = cli_scandesc(desc, ctx, 0, 0, NULL, AC_SCAN_VIR, NULL);
196 196
 	if(ret != CL_VIRUS) {
197
-	    *ctx->virname = "Heuristics.Encrypted.RAR";
197
+	    cli_append_virus(ctx, "Heuristics.Encrypted.RAR");
198 198
 	    return CL_VIRUS;
199 199
 	}
200 200
     }
... ...
@@ -208,7 +222,7 @@ static int cli_scanrar(int desc, cli_ctx *ctx, off_t sfx_offset, uint32_t *sfx_c
208 208
 	unrar_metadata_t *metadata, *metadata_tmp;
209 209
 	char *dir;
210 210
 	unrar_state_t rar_state;
211
-
211
+	unsigned int viruses_found = 0;
212 212
 
213 213
     cli_dbgmsg("in scanrar()\n");
214 214
 
... ...
@@ -236,7 +250,7 @@ static int cli_scanrar(int desc, cli_ctx *ctx, off_t sfx_offset, uint32_t *sfx_c
236 236
 		lseek(desc, 0, SEEK_SET);
237 237
 		ret = cli_scandesc(desc, ctx, 0, 0, NULL, AC_SCAN_VIR, NULL);
238 238
 		if(ret != CL_VIRUS)
239
-		    *ctx->virname = "Heuristics.Encrypted.RAR";
239
+		    cli_append_virus(ctx, "Heuristics.Encrypted.RAR");
240 240
 		return CL_VIRUS;
241 241
 	    }
242 242
 	    return CL_CLEAN;
... ...
@@ -286,12 +300,19 @@ static int cli_scanrar(int desc, cli_ctx *ctx, off_t sfx_offset, uint32_t *sfx_c
286 286
 	    if(!ctx->engine->keeptmp) 
287 287
 		if (cli_unlink(rar_state.filename)) ret = CL_EUNLINK;
288 288
 	    if(rc == CL_VIRUS ) {
289
-		cli_dbgmsg("RAR: infected with %s\n",*ctx->virname);
289
+		cli_dbgmsg("RAR: infected with %s\n", cli_get_last_virus(ctx));
290 290
 		ret = CL_VIRUS;
291
-		break;
291
+		viruses_found++;
292 292
 	    }
293 293
 	}
294 294
 
295
+	if(ret == CL_VIRUS) {
296
+	    if(SCAN_ALL)
297
+		ret = CL_SUCCESS;
298
+	    else
299
+		break;
300
+	}
301
+
295 302
 	if(ret == CL_SUCCESS)
296 303
 	    ret = cli_unrar_scanmetadata(desc,rar_state.metadata_tail, ctx, rar_state.file_count, sfx_check);
297 304
 
... ...
@@ -321,6 +342,8 @@ static int cli_scanrar(int desc, cli_ctx *ctx, off_t sfx_offset, uint32_t *sfx_c
321 321
     }
322 322
     cli_dbgmsg("RAR: Exit code: %d\n", ret);
323 323
 
324
+    if (SCAN_ALL && viruses_found)
325
+	return CL_VIRUS;
324 326
     return ret;
325 327
 }
326 328
 
... ...
@@ -373,7 +396,7 @@ static int cli_scanarj(cli_ctx *ctx, off_t sfx_offset, uint32_t *sfx_check)
373 373
 	    rc = cli_magic_scandesc(metadata.ofd, ctx);
374 374
 	    close(metadata.ofd);
375 375
 	    if (rc == CL_VIRUS) {
376
-		cli_dbgmsg("ARJ: infected with %s\n",*ctx->virname);
376
+		cli_dbgmsg("ARJ: infected with %s\n", cli_get_last_virus(ctx));
377 377
 		ret = CL_VIRUS;
378 378
 		if (metadata.filename) {
379 379
 		    free(metadata.filename);
... ...
@@ -446,7 +469,7 @@ static int cli_scangzip_with_zib_from_the_80s(cli_ctx *ctx, unsigned char *buff)
446 446
     gzclose(gz);
447 447
 
448 448
     if((ret = cli_magic_scandesc(fd, ctx)) == CL_VIRUS) {
449
-	cli_dbgmsg("GZip: Infected with %s\n", *ctx->virname);
449
+	cli_dbgmsg("GZip: Infected with %s\n", cli_get_last_virus(ctx));
450 450
 	close(fd);
451 451
 	if(!ctx->engine->keeptmp) {
452 452
 	    if (cli_unlink(tmpname)) {
... ...
@@ -538,7 +561,7 @@ static int cli_scangzip(cli_ctx *ctx)
538 538
     inflateEnd(&z);	    
539 539
 
540 540
     if((ret = cli_magic_scandesc(fd, ctx)) == CL_VIRUS) {
541
-	cli_dbgmsg("GZip: Infected with %s\n", *ctx->virname);
541
+	cli_dbgmsg("GZip: Infected with %s\n", cli_get_last_virus(ctx));
542 542
 	close(fd);
543 543
 	if(!ctx->engine->keeptmp) {
544 544
 	    if (cli_unlink(tmpname)) {
... ...
@@ -648,7 +671,7 @@ static int cli_scanbzip(cli_ctx *ctx)
648 648
     }
649 649
 
650 650
     if((ret = cli_magic_scandesc(fd, ctx)) == CL_VIRUS ) {
651
-	cli_dbgmsg("Bzip: Infected with %s\n", *ctx->virname);
651
+	cli_dbgmsg("Bzip: Infected with %s\n", cli_get_last_virus(ctx));
652 652
     }
653 653
     close(fd);
654 654
     if(!ctx->engine->keeptmp)
... ...
@@ -700,7 +723,7 @@ static int cli_scanmscab(cli_ctx *ctx, off_t sfx_offset)
700 700
 	struct cab_archive cab;
701 701
 	struct cab_file *file;
702 702
 	unsigned int corrupted_input;
703
-
703
+	unsigned int viruses_found = 0;
704 704
 
705 705
     cli_dbgmsg("in cli_scanmscab()\n");
706 706
 
... ...
@@ -711,8 +734,11 @@ static int cli_scanmscab(cli_ctx *ctx, off_t sfx_offset)
711 711
 	files++;
712 712
 
713 713
 	if(cli_matchmeta(ctx, file->name, 0, file->length, 0, files, 0, NULL) == CL_VIRUS) {
714
-	    ret = CL_VIRUS;
715
-	    break;
714
+	    if (!SCAN_ALL) {
715
+		ret = CL_VIRUS;
716
+		break;
717
+	    }
718
+	    viruses_found++;
716 719
 	}
717 720
 
718 721
 	if(ctx->engine->maxscansize && ctx->scansize >= ctx->engine->maxscansize) {
... ...
@@ -751,11 +777,17 @@ static int cli_scanmscab(cli_ctx *ctx, off_t sfx_offset)
751 751
 	    }
752 752
 	}
753 753
 	free(tempname);
754
-	if(ret == CL_VIRUS)
755
-	    break;
754
+	if(ret == CL_VIRUS) {
755
+	    if (SCAN_ALL)
756
+		viruses_found++;
757
+	    else
758
+		break;
759
+	}
756 760
     }
757 761
 
758 762
     cab_free(&cab);
763
+    if (viruses_found)
764
+	return CL_VIRUS;
759 765
     return ret;
760 766
 }
761 767
 
... ...
@@ -766,6 +798,7 @@ static int vba_scandata(const unsigned char *data, unsigned int len, cli_ctx *ct
766 766
 	struct cli_ac_data gmdata, tmdata;
767 767
 	struct cli_ac_data *mdata[2];
768 768
 	int ret;
769
+	unsigned int viruses_found = 0;
769 770
 
770 771
     if((ret = cli_ac_initdata(&tmdata, troot->ac_partsigs, troot->ac_lsigs, troot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN)))
771 772
 	return ret;
... ...
@@ -779,14 +812,20 @@ static int vba_scandata(const unsigned char *data, unsigned int len, cli_ctx *ct
779 779
 
780 780
     ret = cli_scanbuff(data, len, 0, ctx, CL_TYPE_MSOLE2, mdata);
781 781
 
782
-    if(ret != CL_VIRUS) {
782
+    if(ret != CL_VIRUS || SCAN_ALL) {
783
+	if (SCAN_ALL)
784
+	    viruses_found++;
783 785
 	ret = cli_lsig_eval(ctx, troot, &tmdata, NULL, NULL);
784
-	if(ret != CL_VIRUS)
786
+	if(ret != CL_VIRUS || SCAN_ALL)
787
+	    if (SCAN_ALL)
788
+		viruses_found++;
785 789
 	    ret = cli_lsig_eval(ctx, groot, &gmdata, NULL, NULL);
786 790
     }
787 791
     cli_ac_freedata(&tmdata);
788 792
     cli_ac_freedata(&gmdata);
789 793
 
794
+    if (viruses_found)
795
+	return CL_VIRUS;
790 796
     return ret;
791 797
 }
792 798
 
... ...
@@ -807,6 +846,7 @@ static int cli_vba_scandir(const char *dirname, cli_ctx *ctx, struct uniq *U)
807 807
 	unsigned char *data;
808 808
 	char *hash;
809 809
 	uint32_t hashcnt;
810
+	unsigned int viruses_found = 0;
810 811
 
811 812
 
812 813
     cli_dbgmsg("VBADir: %s\n", dirname);
... ...
@@ -831,9 +871,13 @@ static int cli_vba_scandir(const char *dirname, cli_ctx *ctx, struct uniq *U)
831 831
 		    if(ctx->scanned)
832 832
 			*ctx->scanned += data_len / CL_COUNT_PRECISION;
833 833
 		    if(vba_scandata(data, data_len, ctx) == CL_VIRUS) {
834
-			free(data);
835
-			ret = CL_VIRUS;
836
-			break;
834
+			if (SCAN_ALL) 
835
+			    viruses_found++;
836
+			else {
837
+			    free(data);
838
+			    ret = CL_VIRUS;
839
+			    break;
840
+			}
837 841
 		    }
838 842
 		    free(data);
839 843
 		}
... ...
@@ -845,10 +889,12 @@ static int cli_vba_scandir(const char *dirname, cli_ctx *ctx, struct uniq *U)
845 845
 	free(vba_project->dir);
846 846
 	free(vba_project->offset);
847 847
 	free(vba_project);
848
-	if (ret == CL_VIRUS) break;
848
+	if (ret == CL_VIRUS && !SCAN_ALL)
849
+	    break;
849 850
     }
850 851
 
851
-    if(ret == CL_CLEAN && (hashcnt = uniq_get(U, "powerpoint document", 19, &hash))) {
852
+    if((ret == CL_CLEAN || (ret == CL_VIRUS && SCAN_ALL)) && 
853
+	(hashcnt = uniq_get(U, "powerpoint document", 19, &hash))) {
852 854
 	while(hashcnt--) {
853 855
 	    snprintf(vbaname, 1024, "%s"PATHSEP"%s_%u", dirname, hash, hashcnt);
854 856
 	    vbaname[sizeof(vbaname)-1] = '\0';
... ...
@@ -857,6 +903,7 @@ static int cli_vba_scandir(const char *dirname, cli_ctx *ctx, struct uniq *U)
857 857
 	    if ((fullname = cli_ppt_vba_read(fd, ctx))) {
858 858
 		if(cli_scandir(fullname, ctx) == CL_VIRUS) {
859 859
 		    ret = CL_VIRUS;
860
+		    viruses_found++;
860 861
 		}
861 862
 		if(!ctx->engine->keeptmp)
862 863
 		    cli_rmdirs(fullname);
... ...
@@ -866,7 +913,8 @@ static int cli_vba_scandir(const char *dirname, cli_ctx *ctx, struct uniq *U)
866 866
 	}
867 867
     }
868 868
 
869
-    if (ret == CL_CLEAN && (hashcnt = uniq_get(U, "worddocument", 12, &hash))) {
869
+    if ((ret == CL_CLEAN || (ret == CL_VIRUS && SCAN_ALL)) && 
870
+	(hashcnt = uniq_get(U, "worddocument", 12, &hash))) {
870 871
 	while(hashcnt--) {
871 872
 	    snprintf(vbaname, sizeof(vbaname), "%s"PATHSEP"%s_%u", dirname, hash, hashcnt);
872 873
 	    vbaname[sizeof(vbaname)-1] = '\0';
... ...
@@ -888,9 +936,13 @@ static int cli_vba_scandir(const char *dirname, cli_ctx *ctx, struct uniq *U)
888 888
 			if(ctx->scanned)
889 889
 			    *ctx->scanned += vba_project->length[i] / CL_COUNT_PRECISION;
890 890
 			if(vba_scandata(data, vba_project->length[i], ctx) == CL_VIRUS) {
891
+			    if (SCAN_ALL)
892
+				viruses_found++;
893
+			    else {
891 894
 				free(data);
892 895
 				ret = CL_VIRUS;
893 896
 				break;
897
+			    }
894 898
 			}
895 899
 			free(data);
896 900
 		}
... ...
@@ -904,11 +956,16 @@ static int cli_vba_scandir(const char *dirname, cli_ctx *ctx, struct uniq *U)
904 904
 	    free(vba_project->key);
905 905
 	    free(vba_project->length);
906 906
 	    free(vba_project);
907
-	    if(ret == CL_VIRUS) break;
907
+	    if(ret == CL_VIRUS) {
908
+		if (SCAN_ALL)
909
+		    viruses_found++;
910
+		else
911
+		    break;
912
+	    }
908 913
 	}
909 914
     }
910 915
 
911
-    if(ret != CL_CLEAN)
916
+    if(ret != CL_CLEAN && !(ret == CL_VIRUS && SCAN_ALL))
912 917
     	return ret;
913 918
 
914 919
     /* Check directory for embedded OLE objects */
... ...
@@ -921,7 +978,7 @@ static int cli_vba_scandir(const char *dirname, cli_ctx *ctx, struct uniq *U)
921 921
 	if (fd >= 0) {
922 922
 	    ret = cli_scan_ole10(fd, ctx);
923 923
 	    close(fd);
924
-	    if(ret != CL_CLEAN)
924
+	    if(ret != CL_CLEAN && !(ret == CL_VIRUS && SCAN_ALL))
925 925
 		return ret;
926 926
 	}
927 927
     }
... ...
@@ -954,10 +1011,14 @@ static int cli_vba_scandir(const char *dirname, cli_ctx *ctx, struct uniq *U)
954 954
 		    if(LSTAT(fullname, &statbuf) != -1) {
955 955
 			if(S_ISDIR(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode))
956 956
 			  if (cli_vba_scandir(fullname, ctx, U) == CL_VIRUS) {
957
-			    	ret = CL_VIRUS;
958
-				free(fullname);
959
-				break;
960
-			    }
957
+			      if (SCAN_ALL)
958
+				  viruses_found++;
959
+			      else {
960
+				  ret = CL_VIRUS;
961
+				  free(fullname);
962
+				  break;
963
+			      }
964
+			  }
961 965
 		    }
962 966
 		    free(fullname);
963 967
 		}
... ...
@@ -970,9 +1031,12 @@ static int cli_vba_scandir(const char *dirname, cli_ctx *ctx, struct uniq *U)
970 970
 
971 971
     closedir(dd);
972 972
     if(BLOCK_MACROS && hasmacros) {
973
-	*ctx->virname = "Heuristics.OLE2.ContainsMacros";
973
+	cli_append_virus(ctx, "Heuristics.OLE2.ContainsMacros");
974 974
 	ret = CL_VIRUS;
975
+	viruses_found++;
975 976
     }
977
+    if (SCAN_ALL && viruses_found)
978
+	return CL_VIRUS;
976 979
     return ret;
977 980
 }
978 981
 
... ...
@@ -981,6 +1045,7 @@ static int cli_scanhtml(cli_ctx *ctx)
981 981
 	char *tempname, fullname[1024];
982 982
 	int ret=CL_CLEAN, fd;
983 983
 	fmap_t *map = *ctx->fmap;
984
+	unsigned int viruses_found = 0;
984 985
 
985 986
     cli_dbgmsg("in cli_scanhtml()\n");
986 987
 
... ...
@@ -1008,34 +1073,38 @@ static int cli_scanhtml(cli_ctx *ctx)
1008 1008
     snprintf(fullname, 1024, "%s"PATHSEP"nocomment.html", tempname);
1009 1009
     fd = open(fullname, O_RDONLY|O_BINARY);
1010 1010
     if (fd >= 0) {
1011
-	    ret = cli_scandesc(fd, ctx, CL_TYPE_HTML, 0, NULL, AC_SCAN_VIR, NULL);
1012
-	    close(fd);
1011
+	if ((ret = cli_scandesc(fd, ctx, CL_TYPE_HTML, 0, NULL, AC_SCAN_VIR, NULL)) == CL_VIRUS)
1012
+	    viruses_found++;
1013
+	close(fd);
1013 1014
     }
1014 1015
 
1015
-    if(ret == CL_CLEAN && map->len < 2097152) {
1016
+    if((ret == CL_CLEAN || (ret == CL_VIRUS && SCAN_ALL)) && map->len < 2097152) {
1016 1017
 	    /* limit to 2 MB, we're not interesting in scanning large files in notags form */
1017 1018
 	    /* TODO: don't even create notags if file is over 2 MB */
1018 1019
 	    snprintf(fullname, 1024, "%s"PATHSEP"notags.html", tempname);
1019 1020
 	    fd = open(fullname, O_RDONLY|O_BINARY);
1020 1021
 	    if(fd >= 0) {
1021
-		    ret = cli_scandesc(fd, ctx, CL_TYPE_HTML, 0, NULL, AC_SCAN_VIR, NULL);
1022
-		    close(fd);
1022
+		if ((ret = cli_scandesc(fd, ctx, CL_TYPE_HTML, 0, NULL, AC_SCAN_VIR, NULL)) == CL_VIRUS) 
1023
+		    viruses_found++;
1024
+		close(fd);
1023 1025
 	    }
1024 1026
     }
1025 1027
 
1026
-    if(ret == CL_CLEAN) {
1028
+    if(ret == CL_CLEAN || (ret == CL_VIRUS && SCAN_ALL)) {
1027 1029
 	    snprintf(fullname, 1024, "%s"PATHSEP"javascript", tempname);
1028 1030
 	    fd = open(fullname, O_RDONLY|O_BINARY);
1029 1031
 	    if(fd >= 0) {
1030
-		    ret = cli_scandesc(fd, ctx, CL_TYPE_HTML, 0, NULL, AC_SCAN_VIR, NULL);
1031
-		    if (ret == CL_CLEAN) {
1032
-			    ret = cli_scandesc(fd, ctx, CL_TYPE_TEXT_ASCII, 0, NULL, AC_SCAN_VIR, NULL);
1033
-		    }
1034
-		    close(fd);
1032
+		if ((ret = cli_scandesc(fd, ctx, CL_TYPE_HTML, 0, NULL, AC_SCAN_VIR, NULL)) == CL_VIRUS)
1033
+		    viruses_found++;
1034
+		if (ret == CL_CLEAN || (ret == CL_VIRUS && SCAN_ALL)) {
1035
+		    if ((ret = cli_scandesc(fd, ctx, CL_TYPE_TEXT_ASCII, 0, NULL, AC_SCAN_VIR, NULL)) == CL_VIRUS)
1036
+			viruses_found++;
1037
+		}
1038
+		close(fd);
1035 1039
 	    }
1036 1040
     }
1037 1041
 
1038
-    if (ret == CL_CLEAN) {
1042
+    if (ret == CL_CLEAN || (ret == CL_VIRUS && SCAN_ALL)) {
1039 1043
 	snprintf(fullname, 1024, "%s"PATHSEP"rfc2397", tempname);
1040 1044
 	ret = cli_scandir(fullname, ctx);
1041 1045
     }
... ...
@@ -1044,6 +1113,8 @@ static int cli_scanhtml(cli_ctx *ctx)
1044 1044
         cli_rmdirs(tempname);
1045 1045
 
1046 1046
     free(tempname);
1047
+    if (SCAN_ALL && viruses_found)
1048
+	return CL_VIRUS;
1047 1049
     return ret;
1048 1050
 }
1049 1051
 
... ...
@@ -1061,6 +1132,7 @@ static int cli_scanscript(cli_ctx *ctx)
1061 1061
 	struct cli_ac_data *mdata[2];
1062 1062
 	fmap_t *map = *ctx->fmap;
1063 1063
 	size_t at = 0;
1064
+	unsigned int viruses_found = 0;
1064 1065
 
1065 1066
     if (!ctx || !ctx->engine->root)
1066 1067
         return CL_ENULLARG;
... ...
@@ -1094,7 +1166,7 @@ static int cli_scanscript(cli_ctx *ctx)
1094 1094
 	text_normalize_init(&state, normalized, SCANBUFF + maxpatlen);
1095 1095
 	ret = CL_CLEAN;
1096 1096
 
1097
-	if ((ret = cli_ac_initdata(&tmdata, troot->ac_partsigs, troot->ac_lsigs, troot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN)))
1097
+	if ((ret = cli_ac_initdata(&tmdata, troot->ac_partsigs, troot->ac_lsigs, troot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN))) 
1098 1098
 	    return ret;
1099 1099
 
1100 1100
 	if ((ret = cli_ac_initdata(&gmdata, groot->ac_partsigs, groot->ac_lsigs, groot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN))) {
... ...
@@ -1118,8 +1190,12 @@ static int cli_scanscript(cli_ctx *ctx)
1118 1118
 		}
1119 1119
 		/* when we flush the buffer also scan */
1120 1120
 		if(cli_scanbuff(state.out, state.out_pos, offset, ctx, CL_TYPE_TEXT_ASCII, mdata) == CL_VIRUS) {
1121
-		    ret = CL_VIRUS;
1122
-		    break;
1121
+		    if (SCAN_ALL)
1122
+			viruses_found++;
1123
+		    else {
1124
+			ret = CL_VIRUS;
1125
+			break;
1126
+		    }
1123 1127
 		}
1124 1128
 		if(ctx->scanned)
1125 1129
 		    *ctx->scanned += state.out_pos / CL_COUNT_PRECISION;
... ...
@@ -1140,14 +1216,18 @@ static int cli_scanscript(cli_ctx *ctx)
1140 1140
 		close(ofd);
1141 1141
 	}
1142 1142
 	free(normalized);
1143
-	if(ret != CL_VIRUS) {
1144
-	    ret = cli_lsig_eval(ctx, troot, &tmdata, NULL, NULL);
1145
-	    if(ret != CL_VIRUS)
1146
-		ret = cli_lsig_eval(ctx, groot, &gmdata, NULL, NULL);
1143
+	if(ret != CL_VIRUS || SCAN_ALL)  {
1144
+	    if ((ret = cli_lsig_eval(ctx, troot, &tmdata, NULL, NULL)) == CL_VIRUS)
1145
+		viruses_found++;
1146
+	    if(ret != CL_VIRUS || SCAN_ALL)
1147
+		if ((ret = cli_lsig_eval(ctx, groot, &gmdata, NULL, NULL)) == CL_VIRUS)
1148
+		    viruses_found++;
1147 1149
 	}
1148 1150
 	cli_ac_freedata(&tmdata);
1149 1151
 	cli_ac_freedata(&gmdata);
1150 1152
 
1153
+	if (SCAN_ALL && viruses_found)
1154
+	    return CL_VIRUS;
1151 1155
 	return ret;
1152 1156
 }
1153 1157
 
... ...
@@ -1293,6 +1373,7 @@ static int cli_scanmschm(cli_ctx *ctx)
1293 1293
 	int ret = CL_CLEAN, rc;
1294 1294
 	chm_metadata_t metadata;
1295 1295
 	char *dir;
1296
+	unsigned int viruses_found = 0;
1296 1297
 
1297 1298
     cli_dbgmsg("in cli_scanmschm()\n");
1298 1299
 
... ...
@@ -1325,9 +1406,13 @@ static int cli_scanmschm(cli_ctx *ctx)
1325 1325
 	    rc = cli_magic_scandesc(metadata.ofd, ctx);
1326 1326
 	    close(metadata.ofd);
1327 1327
 	    if (rc == CL_VIRUS) {
1328
-		cli_dbgmsg("CHM: infected with %s\n",*ctx->virname);
1329
-		ret = CL_VIRUS;
1330
-		break;
1328
+		cli_dbgmsg("CHM: infected with %s\n", cli_get_last_virus(ctx));
1329
+		if (SCAN_ALL)
1330
+		    viruses_found++;
1331
+		else {
1332
+		    ret = CL_VIRUS;
1333
+		    break;
1334
+		}
1331 1335
 	    }
1332 1336
 	}
1333 1337
 
... ...
@@ -1344,6 +1429,8 @@ static int cli_scanmschm(cli_ctx *ctx)
1344 1344
     if (ret == CL_BREAK)
1345 1345
 	ret = CL_CLEAN;
1346 1346
 
1347
+    if (SCAN_ALL && viruses_found)
1348
+	return CL_VIRUS;
1347 1349
     return ret;
1348 1350
 }
1349 1351
 
... ...
@@ -1379,7 +1466,7 @@ static int cli_scanriff(cli_ctx *ctx)
1379 1379
 
1380 1380
     if(cli_check_riff_exploit(ctx) == 2) {
1381 1381
 	ret = CL_VIRUS;
1382
-	*ctx->virname = "Heuristics.Exploit.W32.MS05-002";
1382
+	cli_append_virus(ctx, "Heuristics.Exploit.W32.MS05-002");
1383 1383
     }
1384 1384
 
1385 1385
     return ret;
... ...
@@ -1391,7 +1478,7 @@ static int cli_scanjpeg(cli_ctx *ctx)
1391 1391
 
1392 1392
 	if(cli_check_jpeg_exploit(ctx, 0) == 1) {
1393 1393
 	ret = CL_VIRUS;
1394
-	*ctx->virname = "Heuristics.Exploit.W32.MS04-028";
1394
+	cli_append_virus(ctx, "Heuristics.Exploit.W32.MS04-028");
1395 1395
     }
1396 1396
 
1397 1397
     return ret;
... ...
@@ -1446,7 +1533,7 @@ static int cli_scancryptff(cli_ctx *ctx)
1446 1446
     cli_dbgmsg("CryptFF: Scanning decrypted data\n");
1447 1447
 
1448 1448
     if((ret = cli_magic_scandesc(ndesc, ctx)) == CL_VIRUS)
1449
-	cli_dbgmsg("CryptFF: Infected with %s\n", *ctx->virname);
1449
+	cli_dbgmsg("CryptFF: Infected with %s\n", cli_get_last_virus(ctx));
1450 1450
 
1451 1451
     close(ndesc);
1452 1452
 
... ...
@@ -1538,7 +1625,7 @@ static int cli_scanmail(cli_ctx *ctx)
1538 1538
 {
1539 1539
 	char *dir;
1540 1540
 	int ret;
1541
-
1541
+	unsigned int viruses_found = 0;
1542 1542
 
1543 1543
     cli_dbgmsg("Starting cli_scanmail(), recursion = %u\n", ctx->recursion);
1544 1544
 
... ...
@@ -1556,10 +1643,14 @@ static int cli_scanmail(cli_ctx *ctx)
1556 1556
      * Extract the attachments into the temporary directory
1557 1557
      */
1558 1558
     if((ret = cli_mbox(dir, ctx))) {
1559
-	if(!ctx->engine->keeptmp)
1560
-	    cli_rmdirs(dir);
1561
-	free(dir);
1562
-	return ret;
1559
+	if (ret == CL_VIRUS && SCAN_ALL)
1560
+	    viruses_found++;
1561
+	else {
1562
+	    if(!ctx->engine->keeptmp)
1563
+		cli_rmdirs(dir);
1564
+	    free(dir);
1565
+	    return ret;
1566
+	}
1563 1567
     }
1564 1568
 
1565 1569
     ret = cli_scandir(dir, ctx);
... ...
@@ -1568,6 +1659,8 @@ static int cli_scanmail(cli_ctx *ctx)
1568 1568
 	cli_rmdirs(dir);
1569 1569
 
1570 1570
     free(dir);
1571
+    if (SCAN_ALL && viruses_found)
1572
+	return CL_VIRUS;
1571 1573
     return ret;
1572 1574
 }
1573 1575
 
... ...
@@ -1582,7 +1675,7 @@ static int cli_scan_structured(cli_ctx *ctx)
1582 1582
 	size_t pos = 0;
1583 1583
 	int (*ccfunc)(const unsigned char *buffer, int length);
1584 1584
 	int (*ssnfunc)(const unsigned char *buffer, int length);
1585
-
1585
+	unsigned int viruses_found;
1586 1586
 
1587 1587
     if(ctx == NULL)
1588 1588
 	return CL_ENULLARG;
... ...
@@ -1632,16 +1725,24 @@ static int cli_scan_structured(cli_ctx *ctx)
1632 1632
 
1633 1633
     if(cc_count != 0 && cc_count >= ctx->engine->min_cc_count) {
1634 1634
 	cli_dbgmsg("cli_scan_structured: %u credit card numbers detected\n", cc_count);
1635
-	*ctx->virname = "Heuristics.Structured.CreditCardNumber";
1636
-	return CL_VIRUS;
1635
+	cli_append_virus(ctx,"Heuristics.Structured.CreditCardNumber");
1636
+	if (SCAN_ALL)
1637
+	    viruses_found++;
1638
+	else
1639
+	    return CL_VIRUS;
1637 1640
     }
1638 1641
 
1639 1642
     if(ssn_count != 0 && ssn_count >= ctx->engine->min_ssn_count) {
1640 1643
 	cli_dbgmsg("cli_scan_structured: %u social security numbers detected\n", ssn_count);
1641
-	*ctx->virname = "Heuristics.Structured.SSN";
1642
-	return CL_VIRUS;
1644
+	cli_append_virus(ctx,"Heuristics.Structured.SSN");
1645
+	if (SCAN_ALL)
1646
+	    viruses_found++;
1647
+	else
1648
+	    return CL_VIRUS;
1643 1649
     }
1644 1650
 
1651
+    if (SCAN_ALL && viruses_found)
1652
+	return CL_VIRUS;
1645 1653
     return CL_CLEAN;
1646 1654
 }
1647 1655
 
... ...
@@ -1707,7 +1808,7 @@ static int cli_scanembpe(cli_ctx *ctx, off_t offset)
1707 1707
     ret = cli_magic_scandesc(fd, ctx);
1708 1708
     ctx->corrupted_input = corrupted_input;
1709 1709
     if(ret == CL_VIRUS) {
1710
-	cli_dbgmsg("cli_scanembpe: Infected with %s\n", *ctx->virname);
1710
+	cli_dbgmsg("cli_scanembpe: Infected with %s\n", cli_get_last_virus(ctx));
1711 1711
 	close(fd);
1712 1712
 	if(!ctx->engine->keeptmp) {
1713 1713
 	    if (cli_unlink(tmpname)) {
... ...
@@ -1903,7 +2004,7 @@ static int cli_scanraw(cli_ctx *ctx, cli_file_t type, uint8_t typercg, cli_file_
1903 1903
     if(ret >= CL_TYPENO) {
1904 1904
 	perf_nested_start(ctx, PERFT_RAWTYPENO, PERFT_SCAN);
1905 1905
 	ctx->recursion++;
1906
-	if(nret != CL_VIRUS) {
1906
+	if(nret != CL_VIRUS) { //TODO: don't need this test: nret == CL_CLEAN
1907 1907
 	    lastrar = 0xdeadbeef;
1908 1908
 	    fpt = ftoffset;
1909 1909
 	    while(fpt) {
... ...
@@ -2068,7 +2169,7 @@ static int cli_scanraw(cli_ctx *ctx, cli_file_t type, uint8_t typercg, cli_file_
2068 2068
     }
2069 2069
 
2070 2070
     if(ret == CL_VIRUS)
2071
-	cli_dbgmsg("%s found\n", *ctx->virname);
2071
+	cli_dbgmsg("%s found\n", cli_get_last_virus(ctx));
2072 2072
 
2073 2073
     return ret;
2074 2074
 }
... ...
@@ -2090,26 +2191,25 @@ static void emax_reached(cli_ctx *ctx) {
2090 2090
 #define LINESTR2(x) LINESTR(x)
2091 2091
 #define __AT__  " at line "LINESTR2(__LINE__)
2092 2092
 
2093
-#define early_ret_from_magicscan(retcode) \
2094
-    do {\
2095
-	cli_dbgmsg("cli_magic_scandesc: returning %d %s (no post, no cache)\n", retcode, __AT__);\
2096
-	return retcode;                                                                         \
2093
+#define early_ret_from_magicscan(retcode)				\
2094
+    do {								\
2095
+	cli_dbgmsg("cli_magic_scandesc: returning %d %s (no post, no cache)\n", retcode, __AT__); \
2096
+	return retcode;							\
2097 2097
     } while(0)
2098 2098
 
2099
-#define ret_from_magicscan(retcode) \
2100
-    do {											\
2101
-	cli_dbgmsg("cli_magic_scandesc: returning %d %s\n", retcode, __AT__); 			\
2102
-	if(ctx->engine->cb_post_scan) {								\
2103
-	    perf_start(ctx, PERFT_POSTCB);							\
2104
-	    switch(ctx->engine->cb_post_scan(fmap_fd(*ctx->fmap), retcode, retcode == CL_VIRUS && ctx->virname ? *ctx->virname : NULL, ctx->cb_ctx)) {	\
2099
+#define ret_from_magicscan(retcode)					\
2100
+    do {								\
2101
+	cli_dbgmsg("cli_magic_scandesc: returning %d %s\n", retcode, __AT__); \
2102
+	if(ctx->engine->cb_post_scan) {					\
2103
+	    perf_start(ctx, PERFT_POSTCB);				\
2104
+	    switch(ctx->engine->cb_post_scan(fmap_fd(*ctx->fmap), retcode, ret == CL_VIRUS ? cli_get_last_virus(ctx) : NULL, ctx->cb_ctx)) { \
2105 2105
 	    case CL_BREAK:									\
2106 2106
 		cli_dbgmsg("cli_magic_scandesc: file whitelisted by post_scan callback\n"); 	\
2107 2107
 		perf_stop(ctx, PERFT_POSTCB);							\
2108 2108
 		return CL_CLEAN;								\
2109 2109
 	    case CL_VIRUS:									\
2110 2110
 		cli_dbgmsg("cli_magic_scandesc: file blacklisted by post_scan callback\n");	\
2111
-		if(ctx->virname)								\
2112
-		    *ctx->virname = "Detected.By.Callback";					\
2111
+		cli_append_virus(ctx, "Detected.By.Callback");					\
2113 2112
 		perf_stop(ctx, PERFT_POSTCB);							\
2114 2113
 		if (retcode != CL_VIRUS)                                                        \
2115 2114
 		    return cli_checkfp(hash, hashed_size, ctx);                                 \
... ...
@@ -2140,8 +2240,7 @@ static void emax_reached(cli_ctx *ctx) {
2140 2140
 	    ret_from_magicscan(CL_CLEAN);                                                    \
2141 2141
 	case CL_VIRUS:                                                                       \
2142 2142
 	    cli_dbgmsg("cli_magic_scandesc: file blacklisted by "#scanfn" callback\n");                \
2143
-	    if(ctx->virname)                                                                 \
2144
-		*ctx->virname = "Detected.By.Callback";                                      \
2143
+	    cli_append_virus(ctx, "Detected.By.Callback");		                     \
2145 2144
 	    perf_stop(ctx, PERFT_PRECB);                                                     \
2146 2145
 	    ret_from_magicscan(cli_checkfp(hash, hashed_size, ctx));                          \
2147 2146
 	case CL_CLEAN:                                                                       \
... ...
@@ -2165,6 +2264,7 @@ static int magic_scandesc(cli_ctx *ctx, cli_file_t type)
2165 2165
 	bitset_t *old_hook_lsig_matches;
2166 2166
 	const char *filetype;
2167 2167
 	int cache_clean = 0, res;
2168
+	unsigned int viruses_found = 0;
2168 2169
 
2169 2170
     cli_dbgmsg("in magic_scandesc\n");
2170 2171
     if(ctx->engine->maxreclevel && ctx->recursion > ctx->engine->maxreclevel) {
... ...
@@ -2212,7 +2312,7 @@ static int magic_scandesc(cli_ctx *ctx, cli_file_t type)
2212 2212
     old_hook_lsig_matches = ctx->hook_lsig_matches;
2213 2213
     ctx->hook_lsig_matches = NULL;
2214 2214
 
2215
-    if(!ctx->options || (ctx->recursion == ctx->engine->maxreclevel)) { /* raw mode (stdin, etc.) or last level of recursion */
2215
+    if(!(ctx->options&~CL_SCAN_ALLMATCHES) || (ctx->recursion == ctx->engine->maxreclevel)) { /* raw mode (stdin, etc.) or last level of recursion */
2216 2216
 	if(ctx->recursion == ctx->engine->maxreclevel)
2217 2217
 	    cli_dbgmsg("cli_magic_scandesc: Hit recursion limit, only scanning raw file\n");
2218 2218
 	else
... ...
@@ -2221,7 +2321,7 @@ static int magic_scandesc(cli_ctx *ctx, cli_file_t type)
2221 2221
 	CALL_PRESCAN_CB(cb_pre_scan);
2222 2222
 	/* ret_from_magicscan can be used below here*/
2223 2223
 	if((ret = cli_fmap_scandesc(ctx, 0, 0, NULL, AC_SCAN_VIR, NULL, hash)) == CL_VIRUS)
2224
-	    cli_dbgmsg("%s found in descriptor %d\n", *ctx->virname, fmap_fd(*ctx->fmap));
2224
+	    cli_dbgmsg("%s found in descriptor %d\n", cli_get_last_virus(ctx), fmap_fd(*ctx->fmap));
2225 2225
 	else if(ret == CL_CLEAN) {
2226 2226
 	    if(ctx->recursion != ctx->engine->maxreclevel)
2227 2227
 		cache_clean = 1; /* Only cache if limits are not reached */
... ...
@@ -2564,6 +2664,8 @@ static int magic_scandesc(cli_ctx *ctx, cli_file_t type)
2564 2564
 		/* CL_VIRUS = malware found, check FP and report */
2565 2565
 		case CL_VIRUS:
2566 2566
 		    ret = cli_checkfp(hash, hashed_size, ctx);
2567
+		    if (SCAN_ALL)
2568
+			break;
2567 2569
 		    cli_bitset_free(ctx->hook_lsig_matches);
2568 2570
 		    ctx->hook_lsig_matches = old_hook_lsig_matches;
2569 2571
 		    ret_from_magicscan(ret);
... ...
@@ -2596,7 +2698,7 @@ static int magic_scandesc(cli_ctx *ctx, cli_file_t type)
2596 2596
 	case CL_TYPE_TEXT_UTF16LE:
2597 2597
 	case CL_TYPE_TEXT_UTF8:
2598 2598
 	    perf_nested_start(ctx, PERFT_SCRIPT, PERFT_SCAN);
2599
-	    if((DCONF_DOC & DOC_CONF_SCRIPT) && dettype != CL_TYPE_HTML)
2599
+	    if((DCONF_DOC & DOC_CONF_SCRIPT) && dettype != CL_TYPE_HTML && ret != CL_VIRUS)
2600 2600
 	        ret = cli_scanscript(ctx);
2601 2601
 	    if(SCAN_MAIL && (DCONF_MAIL & MAIL_CONF_MBOX) && ret != CL_VIRUS && (ctx->container_type == CL_TYPE_MAIL || dettype == CL_TYPE_MAIL)) {
2602 2602
 		ret = cli_fmap_scandesc(ctx, CL_TYPE_MAIL, 0, NULL, AC_SCAN_VIR, NULL, NULL);
... ...
@@ -2758,6 +2860,9 @@ static int scan_common(int desc, cl_fmap_t *map, const char **virname, unsigned
2758 2758
     ctx.virname = virname;
2759 2759
     ctx.scanned = scanned;
2760 2760
     ctx.options = scanoptions;
2761
+#if 0 /* for development testing only */
2762
+    ctx.options |= CL_SCAN_ALLMATCHES;
2763
+#endif
2761 2764
     ctx.found_possibly_unwanted = 0;
2762 2765
     ctx.container_type = CL_TYPE_ANY;
2763 2766
     ctx.container_size = 0;
... ...
@@ -2777,6 +2882,7 @@ static int scan_common(int desc, cl_fmap_t *map, const char **virname, unsigned
2777 2777
 	char link[32];
2778 2778
 	ssize_t linksz;
2779 2779
 
2780
+
2780 2781
 	snprintf(link, sizeof(link), "/proc/self/fd/%u", desc);
2781 2782
 	link[sizeof(link)-1]='\0';
2782 2783
 	if((linksz=readlink(link, ctx.entry_filename, sizeof(ctx.entry_filename)-1))==-1) {
... ...
@@ -2790,6 +2896,12 @@ static int scan_common(int desc, cl_fmap_t *map, const char **virname, unsigned
2790 2790
     cli_logg_setup(&ctx);
2791 2791
     rc = map ? cli_map_scandesc(map, 0, map->len, &ctx) : cli_magic_scandesc(desc, &ctx);
2792 2792
 
2793
+    if (ctx.options & CL_SCAN_ALLMATCHES) {
2794
+	*virname = (char *)ctx.virname; /* temp hack for scanall mode until api augmentation */
2795
+	if (rc == CL_CLEAN && ctx.num_viruses)
2796
+	    rc = CL_VIRUS;
2797
+    }
2798
+
2793 2799
     cli_bitset_free(ctx.hook_lsig_matches);
2794 2800
     free(ctx.fmap);
2795 2801
     if(rc == CL_CLEAN && ctx.found_possibly_unwanted)
... ...
@@ -2811,24 +2923,24 @@ int cl_scanmap_callback(cl_fmap_t *map, const char **virname, unsigned long int
2811 2811
 
2812 2812
 int cli_found_possibly_unwanted(cli_ctx* ctx)
2813 2813
 {
2814
-	if(ctx->virname) {
2815
-		cli_dbgmsg("found Possibly Unwanted: %s\n",*ctx->virname);
2816
-		if(ctx->options & CL_SCAN_HEURISTIC_PRECEDENCE) {
2817
-			/* we found a heuristic match, don't scan further,
2818
-			 * but consider it a virus. */
2819
-			cli_dbgmsg("cli_found_possibly_unwanted: CL_VIRUS\n");
2820
-			return CL_VIRUS;
2821
-		}
2822
-		/* heuristic scan isn't taking precedence, keep scanning.
2823
-		 * If this is part of an archive, and 
2824
-		 * we find a real malware we report that instead of the 
2825
-		 * heuristic match */
2826
-		ctx->found_possibly_unwanted = 1;
2827
-	} else {
2828
-		cli_warnmsg("cli_found_possibly_unwanted called, but virname is not set\n");
2814
+    if(cli_get_last_virus(ctx)) {
2815
+	cli_dbgmsg("found Possibly Unwanted: %s\n", cli_get_last_virus(ctx));
2816
+	if(ctx->options & CL_SCAN_HEURISTIC_PRECEDENCE) {
2817
+	    /* we found a heuristic match, don't scan further,
2818
+	     * but consider it a virus. */
2819
+	    cli_dbgmsg("cli_found_possibly_unwanted: CL_VIRUS\n");
2820
+	    return CL_VIRUS;
2829 2821
 	}
2830
-	emax_reached(ctx);
2831
-	return CL_CLEAN;
2822
+	/* heuristic scan isn't taking precedence, keep scanning.
2823
+	 * If this is part of an archive, and 
2824
+	 * we find a real malware we report that instead of the 
2825
+	 * heuristic match */
2826
+	ctx->found_possibly_unwanted = 1;
2827
+    } else {
2828
+	cli_warnmsg("cli_found_possibly_unwanted called, but virname is not set\n");
2829
+    }
2830
+    emax_reached(ctx);
2831
+    return CL_CLEAN;
2832 2832
 }
2833 2833
 
2834 2834
 static int cli_scanfile(const char *filename, cli_ctx *ctx)
... ...
@@ -88,7 +88,7 @@ int cli_check_mydoom_log(cli_ctx *ctx)
88 88
     if ((~check) != key)
89 89
 	return CL_CLEAN;
90 90
 
91
-    *ctx->virname = "Heuristics.Worm.Mydoom.M.log";
91
+    cli_append_virus(ctx, "Heuristics.Worm.Mydoom.M.log");
92 92
     return CL_VIRUS;
93 93
 }
94 94
 
... ...
@@ -253,24 +253,24 @@ static int dumpscan(fmap_t *map, unsigned int offset, unsigned int size, const c
253 253
 	    } else if(!memcmp(buff, "\xff\xd9\xff\xd8", 4)) {
254 254
 		cli_dbgmsg("SWF: JPEG image data (erroneous header)\n");
255 255
 		if(version >= 8 && SCAN_ALGO) {
256
-			*ctx->virname = "Heuristics.SWF.SuspectImage.A";
256
+		        cli_append_virus(ctx, "Heuristics.SWF.SuspectImage.A");
257 257
 			ret = CL_VIRUS;
258 258
 		}
259 259
 	    } else if(!memcmp(buff, "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a", 8)) {
260 260
 		cli_dbgmsg("SWF: PNG image data\n");
261 261
 		if(version < 8 && SCAN_ALGO) {
262
-		    *ctx->virname = "Heuristics.SWF.SuspectImage.B";
262
+		    cli_append_virus(ctx, "Heuristics.SWF.SuspectImage.B");
263 263
 		    ret = CL_VIRUS;
264 264
 		}
265 265
 	    } else if(!memcmp(buff, "\x47\x49\x46\x38\x39\x61", 6)) {
266 266
 		cli_dbgmsg("SWF: GIF89a image data\n");
267 267
 		if(version < 8 && SCAN_ALGO) {
268
-		    *ctx->virname = "Heuristics.SWF.SuspectImage.C";
268
+		    cli_append_virus(ctx, "Heuristics.SWF.SuspectImage.C");
269 269
 		    ret = CL_VIRUS;
270 270
 		}
271 271
 	    } else if(SCAN_ALGO) {
272 272
 		cli_warnmsg("SWF: Unknown image data\n");
273
-		*ctx->virname = "Heuristics.SWF.SuspectImage.D";
273
+		cli_append_virus(ctx, "Heuristics.SWF.SuspectImage.D");
274 274
 		ret = CL_VIRUS;
275 275
 	    }
276 276
 	    if(ret == CL_VIRUS) {
... ...
@@ -279,7 +279,7 @@ static int dumpscan(fmap_t *map, unsigned int offset, unsigned int size, const c
279 279
     }
280 280
     ret = cli_map_scandesc(map, offset, size, ctx);
281 281
     if(ctx->img_validate && ret == CL_EPARSE && SCAN_ALGO) {
282
-	*ctx->virname = "Heuristics.SWF.SuspectImage.E";
282
+	cli_append_virus(ctx, "Heuristics.SWF.SuspectImage.E");
283 283
 	return CL_VIRUS;
284 284
     }
285 285
     return ret;
... ...
@@ -130,6 +130,7 @@ cli_untar(const char *dir, unsigned int posix, cli_ctx *ctx)
130 130
 	size_t pos = 0;
131 131
 	size_t currsize = 0;
132 132
         char zero[BLOCKSIZE];
133
+	unsigned int num_viruses = 0; 
133 134
 
134 135
 	cli_dbgmsg("In untar(%s)\n", dir);
135 136
         memset(zero, 0, sizeof(zero));
... ...
@@ -168,8 +169,12 @@ cli_untar(const char *dir, unsigned int posix, cli_ctx *ctx)
168 168
 				close(fout);
169 169
 				if (!ctx->engine->keeptmp)
170 170
 					if (cli_unlink(fullname)) return CL_EUNLINK;
171
-				if (ret==CL_VIRUS)
171
+				if (ret==CL_VIRUS) {
172
+				    if (!SCAN_ALL)
172 173
 					return CL_VIRUS;
174
+				    else
175
+					num_viruses++;
176
+				}
173 177
 				fout = -1;
174 178
 			}
175 179
 
... ...
@@ -285,8 +290,12 @@ cli_untar(const char *dir, unsigned int posix, cli_ctx *ctx)
285 285
 
286 286
 			strncpy(name, block, 100);
287 287
 			name[100] = '\0';
288
-			if(cli_matchmeta(ctx, name, size, size, 0, files, 0, NULL) == CL_VIRUS)
289
-			    return CL_VIRUS;
288
+			if(cli_matchmeta(ctx, name, size, size, 0, files, 0, NULL) == CL_VIRUS) {
289
+			    if (!SCAN_ALL)
290
+				return CL_VIRUS;
291
+			    else
292
+				num_viruses++;
293
+			}
290 294
 
291 295
 			snprintf(fullname, sizeof(fullname)-1, "%s"PATHSEP"tar%02u", dir, files);
292 296
 			fullname[sizeof(fullname)-1] = '\0';
... ...
@@ -349,5 +358,7 @@ cli_untar(const char *dir, unsigned int posix, cli_ctx *ctx)
349 349
 		if (ret==CL_VIRUS)
350 350
 			return CL_VIRUS;
351 351
 	}
352
+	if (num_viruses)
353
+	    return CL_VIRUS;
352 354
 	return CL_CLEAN;
353 355
 }
... ...
@@ -350,7 +350,7 @@ static unsigned int lhdr(fmap_t *map, uint32_t loff,uint32_t zsize, unsigned int
350 350
 
351 351
   if(detect_encrypted && (LH_flags & F_ENCR) && DETECT_ENCRYPTED) {
352 352
     cli_dbgmsg("cli_unzip: Encrypted files found in archive.\n");
353
-    *ctx->virname = "Heuristics.Encrypted.Zip";
353
+    cli_append_virus(ctx, "Heuristics.Encrypted.Zip");
354 354
     *ret = CL_VIRUS;
355 355
     fmap_unneed_off(map, loff, SIZEOF_LH);
356 356
     return 0;
... ...
@@ -81,6 +81,7 @@ const struct clam_option __clam_options[] = {
81 81
     { NULL, "multiscan", 'm', TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMDSCAN, "", "" },
82 82
     { NULL, "fdpass", 0, TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMDSCAN, "", "" },
83 83
     { NULL, "stream", 0, TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMDSCAN, "", "" },
84
+    { NULL, "allmatch", 'z', TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMSCAN | OPT_CLAMDSCAN, "", "" },
84 85
     { NULL, "database", 'd', TYPE_STRING, NULL, -1, DATADIR, FLAG_REQUIRED | FLAG_MULTIPLE, OPT_CLAMSCAN, "", "" }, /* merge it with DatabaseDirectory (and fix conflict with --datadir */
85 86
     { NULL, "recursive", 'r', TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMSCAN, "", "" },
86 87
     { NULL, "follow-dir-symlinks", 0, TYPE_NUMBER, MATCH_NUMBER, 1, NULL, 0, OPT_CLAMSCAN, "", "" },
... ...
@@ -59,8 +59,11 @@ static void runtest(const char *file, uint64_t expected, int fail, int nojit,
59 59
     struct cl_engine *engine;
60 60
     int fdin = -1;
61 61
     char filestr[512];
62
+    char * virname = NULL;
62 63
 
63 64
     memset(&cctx, 0, sizeof(cctx));
65
+    cctx.options |= CL_SCAN_ALLMATCHES;
66
+    cctx.virname = &virname;
64 67
     cctx.engine = engine = cl_engine_new();
65 68
     fail_unless(!!cctx.engine, "cannot create engine");
66 69
     rc = cl_engine_compile(engine);
... ...
@@ -170,7 +170,29 @@ START_TEST (test_cl_scandesc)
170 170
 }
171 171
 END_TEST
172 172
 
173
-/* int cl_scanfile(const char *filename, const char **virname, unsigned long int *scanned, const struct cl_engine *engine, const struct cl_limits *limits, unsigned int options) */
173
+START_TEST (test_cl_scandesc_allscan)
174
+{
175
+    const char *virname = NULL;
176
+    const char ** virpp = &virname;
177
+    char file[256];
178
+    unsigned long size;
179
+    unsigned long int scanned = 0;
180
+    int ret;
181
+
182
+    int fd = get_test_file(_i, file, sizeof(file), &size);
183
+    cli_dbgmsg("scanning (scandesc) %s\n", file);
184
+    ret = cl_scandesc(fd, virpp, &scanned, g_engine, CL_SCAN_ALLMATCHES+CL_SCAN_STDOPT);
185
+    virpp = (const char **)*virpp; /* allscan api hack */
186
+    cli_dbgmsg("scan end (scandesc) %s\n", file);
187
+
188
+    fail_unless_fmt(ret == CL_VIRUS, "cl_scandesc_allscan failed for %s: %s", file, cl_strerror(ret));
189
+    fail_unless_fmt(*virpp && !strcmp(*virpp, "ClamAV-Test-File.UNOFFICIAL"), "virusname: %s", *virpp);
190
+    free((void *)virpp);
191
+    close(fd);
192
+}
193
+END_TEST
194
+
195
+//* int cl_scanfile(const char *filename, const char **virname, unsigned long int *scanned, const struct cl_engine *engine, const struct cl_limits *limits, unsigned int options) */
174 196
 START_TEST (test_cl_scanfile)
175 197
 {
176 198
     const char *virname = NULL;
... ...
@@ -191,6 +213,29 @@ START_TEST (test_cl_scanfile)
191 191
 }
192 192
 END_TEST
193 193
 
194
+START_TEST (test_cl_scanfile_allscan)
195
+{
196
+    const char *virname = NULL;
197
+    const char **virpp = &virname;
198
+    char file[256];
199
+    unsigned long size;
200
+    unsigned long int scanned = 0;
201
+    int ret;
202
+
203
+    int fd = get_test_file(_i, file, sizeof(file), &size);
204
+    close(fd);
205
+
206
+    cli_dbgmsg("scanning (scanfile_allscan) %s\n", file);
207
+    ret = cl_scanfile(file, virpp, &scanned, g_engine, CL_SCAN_ALLMATCHES+CL_SCAN_STDOPT);
208
+    virpp = (const char **)*virpp; /* allscan api hack */
209
+    cli_dbgmsg("scan end (scanfile_allscan) %s\n", file);
210
+
211
+    fail_unless_fmt(ret == CL_VIRUS, "cl_scanfile_allscan failed for %s: %s", file, cl_strerror(ret));
212
+    fail_unless_fmt(*virpp && !strcmp(*virpp, "ClamAV-Test-File.UNOFFICIAL"), "virusname: %s", *virpp);
213
+    free((void *)virpp);
214
+}
215
+END_TEST
216
+
194 217
 START_TEST (test_cl_scanfile_callback)
195 218
 {
196 219
     const char *virname = NULL;
... ...
@@ -207,11 +252,35 @@ START_TEST (test_cl_scanfile_callback)
207 207
     ret = cl_scanfile_callback(file, &virname, &scanned, g_engine, CL_SCAN_STDOPT, NULL);
208 208
     cli_dbgmsg("scan end (scanfile_cb) %s\n", file);
209 209
 
210
-    fail_unless_fmt(ret == CL_VIRUS, "cl_scanfile failed for %s: %s", file, cl_strerror(ret));
210
+    fail_unless_fmt(ret == CL_VIRUS, "cl_scanfile_cb failed for %s: %s", file, cl_strerror(ret));
211 211
     fail_unless_fmt(virname && !strcmp(virname, "ClamAV-Test-File.UNOFFICIAL"), "virusname: %s", virname);
212 212
 }
213 213
 END_TEST
214 214
 
215
+START_TEST (test_cl_scanfile_callback_allscan)
216
+{
217
+    const char *virname = NULL;
218
+    const char **virpp = &virname;
219
+    char file[256];
220
+    unsigned long size;
221
+    unsigned long int scanned = 0;
222
+    int ret;
223
+
224
+    int fd = get_test_file(_i, file, sizeof(file), &size);
225
+    close(fd);
226
+
227
+    cli_dbgmsg("scanning (scanfile_cb_allscan) %s\n", file);
228
+    /* TODO: test callbacks */
229
+    ret = cl_scanfile_callback(file, virpp, &scanned, g_engine, CL_SCAN_ALLMATCHES+CL_SCAN_STDOPT, NULL);
230
+    virpp = (const char **)*virpp; /* allscan api hack */
231
+    cli_dbgmsg("scan end (scanfile_cb_allscan) %s\n", file);
232
+
233
+    fail_unless_fmt(ret == CL_VIRUS, "cl_scanfile_cb_allscan failed for %s: %s", file, cl_strerror(ret));
234
+    fail_unless_fmt(*virpp && !strcmp(*virpp, "ClamAV-Test-File.UNOFFICIAL"), "virusname: %s", *virpp);
235
+    free((void *)virpp);
236
+}
237
+END_TEST
238
+
215 239
 START_TEST (test_cl_scandesc_callback)
216 240
 {
217 241
     const char *virname = NULL;
... ...
@@ -232,6 +301,31 @@ START_TEST (test_cl_scandesc_callback)
232 232
     close(fd);
233 233
 }
234 234
 END_TEST
235
+
236
+START_TEST (test_cl_scandesc_callback_allscan)
237
+{
238
+    const char *virname = NULL;
239
+    const char **virpp = &virname;
240
+    char file[256];
241
+    unsigned long size;
242
+    unsigned long int scanned = 0;
243
+    int ret;
244
+
245
+    int fd = get_test_file(_i, file, sizeof(file), &size);
246
+
247
+    cli_dbgmsg("scanning (scandesc_cb_allscan) %s\n", file);
248
+    /* TODO: test callbacks */
249
+    ret = cl_scandesc_callback(fd, virpp, &scanned, g_engine, CL_SCAN_ALLMATCHES+CL_SCAN_STDOPT, NULL);
250
+    virpp = (const char **)*virpp; /* allscan api hack */
251
+    cli_dbgmsg("scan end (scandesc_cb_allscan) %s\n", file);
252
+
253
+    fail_unless_fmt(ret == CL_VIRUS, "cl_scanfile_allscan failed for %s: %s", file, cl_strerror(ret));
254
+    fail_unless_fmt(*virpp && !strcmp(*virpp, "ClamAV-Test-File.UNOFFICIAL"), "virusname: %s", *virpp);
255
+    free((void *)virpp);
256
+    close(fd);
257
+}
258
+END_TEST
259
+
235 260
 #endif
236 261
 
237 262
 /* int cl_load(const char *path, struct cl_engine **engine, unsigned int *signo, unsigned int options) */
... ...
@@ -367,6 +461,33 @@ START_TEST (test_cl_scanmap_callback_handle)
367 367
 }
368 368
 END_TEST
369 369
 
370
+START_TEST (test_cl_scanmap_callback_handle_allscan)
371
+{
372
+    const char *virname = NULL;
373
+    const char ** virpp = &virname;
374
+    unsigned long int scanned = 0;
375
+    cl_fmap_t *map;
376
+    int ret;
377
+    char file[256];
378
+    unsigned long size;
379
+
380
+    int fd = get_test_file(_i, file, sizeof(file), &size);
381
+    /* intentionally use different way than scanners.c for testing */
382
+    map = cl_fmap_open_handle(&fd, 0, size, pread_cb, 1);
383
+    fail_unless(!!map, "cl_fmap_open_handle");
384
+
385
+    cli_dbgmsg("scanning (handle) allscan %s\n", file);
386
+    ret = cl_scanmap_callback(map, virpp, &scanned, g_engine, CL_SCAN_ALLMATCHES+CL_SCAN_STDOPT, NULL);
387
+    virpp = (const char **)*virpp; /* allscan api hack */
388
+    cli_dbgmsg("scan end (handle) allscan %s\n", file);
389
+
390
+    fail_unless_fmt(ret == CL_VIRUS, "cl_scanmap_callback_allscan failed for %s: %s", file, cl_strerror(ret));
391
+    fail_unless_fmt(*virpp && !strcmp(*virpp, "ClamAV-Test-File.UNOFFICIAL"), "virusname: %s", *virpp);
392
+    free((void *)virpp);
393
+    close(fd);
394
+}
395
+END_TEST
396
+
370 397
 START_TEST (test_cl_scanmap_callback_mem)
371 398
 {
372 399
     const char *virname = NULL;
... ...
@@ -397,6 +518,39 @@ START_TEST (test_cl_scanmap_callback_mem)
397 397
     munmap(mem, size);
398 398
 }
399 399
 END_TEST
400
+
401
+START_TEST (test_cl_scanmap_callback_mem_allscan)
402
+{
403
+    const char *virname = NULL;
404
+    const char **virpp = &virname;
405
+    unsigned long int scanned = 0;
406
+    cl_fmap_t *map;
407
+    int ret;
408
+    void *mem;
409
+    unsigned long size;
410
+    char file[256];
411
+
412
+    int fd = get_test_file(_i, file, sizeof(file), &size);
413
+
414
+    mem = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
415
+    fail_unless(mem != MAP_FAILED, "mmap");
416
+
417
+    /* intentionally use different way than scanners.c for testing */
418
+    map = cl_fmap_open_memory(mem, size);
419
+    fail_unless(!!map, "cl_fmap_open_mem");
420
+
421
+    cli_dbgmsg("scanning (mem) allscan %s\n", file);
422
+    ret = cl_scanmap_callback(map, virpp, &scanned, g_engine, CL_SCAN_ALLMATCHES+CL_SCAN_STDOPT, NULL);
423
+    virpp = (const char **)*virpp; /* allscan api hack */
424
+    cli_dbgmsg("scan end (mem) allscan %s\n", file);
425
+    fail_unless_fmt(ret == CL_VIRUS, "cl_scanmap_callback failed for %s: %s", file, cl_strerror(ret));
426
+    fail_unless_fmt(*virpp && !strcmp(*virpp, "ClamAV-Test-File.UNOFFICIAL"), "virusname: %s for %s", *virpp, file);
427
+    close(fd);
428
+    cl_fmap_close(map);
429
+    free((void *)virpp);
430
+    munmap(mem, size);
431
+}
432
+END_TEST
400 433
 #endif
401 434
 
402 435
 static Suite *test_cl_suite(void)
... ...
@@ -427,11 +581,17 @@ static Suite *test_cl_suite(void)
427 427
     tcase_add_checked_fixture (tc_cl_scan, engine_setup, engine_teardown);
428 428
 #ifdef CHECK_HAVE_LOOPS
429 429
     tcase_add_loop_test(tc_cl_scan, test_cl_scandesc, 0, expected_testfiles);
430
+    tcase_add_loop_test(tc_cl_scan, test_cl_scandesc_allscan, 0, expected_testfiles);
430 431
     tcase_add_loop_test(tc_cl_scan, test_cl_scanfile, 0, expected_testfiles);
432
+    tcase_add_loop_test(tc_cl_scan, test_cl_scanfile_allscan, 0, expected_testfiles);
431 433
     tcase_add_loop_test(tc_cl_scan, test_cl_scandesc_callback, 0, expected_testfiles);
434
+    tcase_add_loop_test(tc_cl_scan, test_cl_scandesc_callback_allscan, 0, expected_testfiles);
432 435
     tcase_add_loop_test(tc_cl_scan, test_cl_scanfile_callback, 0, expected_testfiles);
436
+    tcase_add_loop_test(tc_cl_scan, test_cl_scanfile_callback_allscan, 0, expected_testfiles);
433 437
     tcase_add_loop_test(tc_cl_scan, test_cl_scanmap_callback_handle, 0, expected_testfiles);
438
+    tcase_add_loop_test(tc_cl_scan, test_cl_scanmap_callback_handle_allscan, 0, expected_testfiles);
434 439
     tcase_add_loop_test(tc_cl_scan, test_cl_scanmap_callback_mem, 0, expected_testfiles);
440
+    tcase_add_loop_test(tc_cl_scan, test_cl_scanmap_callback_mem_allscan, 0, expected_testfiles);
435 441
 #endif
436 442
     return s;
437 443
 }
... ...
@@ -145,7 +145,83 @@ START_TEST (test_bm_scanbuff) {
145 145
     ret = cli_parse_add(root, "Sig3", "babedead", 0, 0, "*", 0, NULL, 0);
146 146
     fail_unless(ret == CL_SUCCESS, "cli_parse_add() failed");
147 147
 
148
-    ret = cli_bm_scanbuff((const unsigned char*)"blah\xde\xad\xbe\xef", 12, &virname, NULL, root, 0, NULL, NULL);
148
+    ret = cli_bm_scanbuff((const unsigned char*)"blah\xde\xad\xbe\xef", 12, &virname, NULL, root, 0, NULL, NULL, NULL);
149
+    fail_unless(ret == CL_VIRUS, "cli_bm_scanbuff() failed");
150
+    fail_unless(!strncmp(virname, "Sig2", 4), "Incorrect signature matched in cli_bm_scanbuff()\n");
151
+}
152
+END_TEST
153
+
154
+START_TEST (test_ac_scanbuff_allscan) {
155
+	struct cli_ac_data mdata;
156
+	struct cli_matcher *root;
157
+	unsigned int i;
158
+	int ret;
159
+
160
+    root = ctx.engine->root[0];
161
+    fail_unless(root != NULL, "root == NULL");
162
+    root->ac_only = 1;
163
+
164
+#ifdef USE_MPOOL
165
+    root->mempool = mpool_create();
166
+#endif
167
+    ret = cli_ac_init(root, CLI_DEFAULT_AC_MINDEPTH, CLI_DEFAULT_AC_MAXDEPTH, 1);
168
+    fail_unless(ret == CL_SUCCESS, "cli_ac_init() failed");
169
+
170
+
171
+    for(i = 0; ac_testdata[i].data; i++) {
172
+	ret = cli_parse_add(root, ac_testdata[i].virname, ac_testdata[i].hexsig, 0, 0, "*", 0, NULL, 0);
173
+	fail_unless(ret == CL_SUCCESS, "cli_parse_add() failed");
174
+    }
175
+
176
+    ret = cli_ac_buildtrie(root);
177
+    fail_unless(ret == CL_SUCCESS, "cli_ac_buildtrie() failed");
178
+
179
+    ret = cli_ac_initdata(&mdata, root->ac_partsigs, 0, 0, CLI_DEFAULT_AC_TRACKLEN);
180
+    fail_unless(ret == CL_SUCCESS, "cli_ac_initdata() failed");
181
+
182
+    ctx.options |= CL_SCAN_ALLMATCHES;
183
+    for(i = 0; ac_testdata[i].data; i++) {
184
+	ret = cli_ac_scanbuff((const unsigned char*)ac_testdata[i].data, strlen(ac_testdata[i].data), &virname, NULL, NULL, root, &mdata, 0, 0, NULL, AC_SCAN_VIR, NULL);
185
+	fail_unless_fmt(ret == CL_VIRUS, "cli_ac_scanbuff() failed for %s", ac_testdata[i].virname);
186
+	fail_unless_fmt(!strncmp(virname, ac_testdata[i].virname, strlen(ac_testdata[i].virname)), "Dataset %u matched with %s", i, virname);
187
+
188
+	ret = cli_scanbuff((const unsigned char*)ac_testdata[i].data, strlen(ac_testdata[i].data), 0, &ctx, 0, NULL);
189
+	fail_unless_fmt(ret == CL_VIRUS, "cli_scanbuff() failed for %s", ac_testdata[i].virname);
190
+	fail_unless_fmt(!strncmp(virname, ac_testdata[i].virname, strlen(ac_testdata[i].virname)), "Dataset %u matched with %s", i, virname);
191
+	if (ctx.num_viruses) {
192
+	    free((void *)ctx.virname);
193
+	    ctx.num_viruses = 0;
194
+	    ctx.size_viruses = 0;
195
+	}
196
+     }
197
+
198
+    cli_ac_freedata(&mdata);
199
+}
200
+END_TEST
201
+
202
+START_TEST (test_bm_scanbuff_allscan) {
203
+	struct cli_matcher *root;
204
+	const char *virname = NULL;
205
+	int ret;
206
+
207
+
208
+    root = ctx.engine->root[0];
209
+    fail_unless(root != NULL, "root == NULL");
210
+
211
+#ifdef USE_MPOOL
212
+    root->mempool = mpool_create();
213
+#endif
214
+    ret = cli_bm_init(root);
215
+    fail_unless(ret == CL_SUCCESS, "cli_bm_init() failed");
216
+
217
+    ret = cli_parse_add(root, "Sig1", "deadbabe", 0, 0, "*", 0, NULL, 0);
218
+    fail_unless(ret == CL_SUCCESS, "cli_parse_add() failed");
219
+    ret = cli_parse_add(root, "Sig2", "deadbeef", 0, 0, "*", 0, NULL, 0);
220
+    fail_unless(ret == CL_SUCCESS, "cli_parse_add() failed");
221
+    ret = cli_parse_add(root, "Sig3", "babedead", 0, 0, "*", 0, NULL, 0);
222
+    fail_unless(ret == CL_SUCCESS, "cli_parse_add() failed");
223
+
224
+    ret = cli_bm_scanbuff((const unsigned char*)"blah\xde\xad\xbe\xef", 12, &virname, NULL, root, 0, NULL, NULL, NULL);
149 225
     fail_unless(ret == CL_VIRUS, "cli_bm_scanbuff() failed");
150 226
     fail_unless(!strncmp(virname, "Sig2", 4), "Incorrect signature matched in cli_bm_scanbuff()\n");
151 227
 }
... ...
@@ -160,6 +236,8 @@ Suite *test_matchers_suite(void)
160 160
     tcase_add_checked_fixture (tc_matchers, setup, teardown);
161 161
     tcase_add_test(tc_matchers, test_ac_scanbuff);
162 162
     tcase_add_test(tc_matchers, test_bm_scanbuff);
163
+    tcase_add_test(tc_matchers, test_ac_scanbuff_allscan);
164
+    tcase_add_test(tc_matchers, test_bm_scanbuff_allscan);
163 165
     return s;
164 166
 }
165 167
 
... ...
@@ -425,12 +425,85 @@ static void do_phishing_test(const struct rtest *rtest)
425 425
 	}
426 426
 }
427 427
 
428
+static void do_phishing_test_allscan(const struct rtest *rtest)
429
+{
430
+	char *realurl;
431
+	cli_ctx ctx;
432
+	const char *virname = NULL;
433
+	tag_arguments_t hrefs;
434
+	int rc;
435
+
436
+	memset(&ctx, 0, sizeof(ctx));
437
+
438
+	realurl = cli_strdup(rtest->realurl);
439
+	fail_unless(!!realurl, "cli_strdup");
440
+
441
+	hrefs.count = 1;
442
+	hrefs.value = cli_malloc(sizeof(*hrefs.value));
443
+	fail_unless(!!hrefs.value, "cli_malloc");
444
+	hrefs.value[0] = (unsigned char*)realurl;
445
+	hrefs.contents = cli_malloc(sizeof(*hrefs.contents));
446
+	fail_unless(!!hrefs.contents, "cli_malloc");
447
+	hrefs.tag = cli_malloc(sizeof(*hrefs.tag));
448
+	fail_unless(!!hrefs.tag, "cli_malloc");
449
+	hrefs.tag[0] = (unsigned char*)cli_strdup("href");
450
+	hrefs.contents[0] = (unsigned char*)cli_strdup(rtest->displayurl);
451
+
452
+	ctx.engine = engine;
453
+	ctx.virname = &virname;
454
+	ctx.options |= CL_SCAN_ALLMATCHES;
455
+
456
+	rc = phishingScan(&ctx, &hrefs);
457
+
458
+	html_tag_arg_free(&hrefs);
459
+	fail_unless(rc == CL_CLEAN,"phishingScan");
460
+	switch(rtest->result) {
461
+		case 0:
462
+			fail_unless_fmt(ctx.found_possibly_unwanted,
463
+					"this should be phishing, realURL: %s, displayURL: %s",
464
+					rtest->realurl, rtest->displayurl);
465
+			break;
466
+		case 1:
467
+			fail_unless_fmt(!ctx.found_possibly_unwanted,
468
+					"this should be whitelisted, realURL: %s, displayURL: %s",
469
+					rtest->realurl, rtest->displayurl);
470
+			break;
471
+		case 2:
472
+			fail_unless_fmt(!ctx.found_possibly_unwanted,
473
+					"this should be clean, realURL: %s, displayURL: %s",
474
+					rtest->realurl, rtest->displayurl);
475
+			break;
476
+		case 3:
477
+			if(!loaded_2)
478
+				fail_unless_fmt(!ctx.found_possibly_unwanted,
479
+					"this should be clean, realURL: %s, displayURL: %s",
480
+					rtest->realurl, rtest->displayurl);
481
+			else {
482
+				fail_unless_fmt(ctx.found_possibly_unwanted,
483
+					"this should be blacklisted, realURL: %s, displayURL: %s",
484
+					rtest->realurl, rtest->displayurl);
485
+				if (*ctx.virname)
486
+				    fail_unless_fmt(!strstr((const char*)*ctx.virname,"Blacklisted"),
487
+						    "should be blacklisted, but is: %s\n", ctx.virname);
488
+			}
489
+			break;
490
+	}
491
+	if (ctx.num_viruses)
492
+	    free((void *)ctx.virname);
493
+}
494
+
428 495
 #ifdef CHECK_HAVE_LOOPS
429 496
 START_TEST (phishingScan_test)
430 497
 {
431 498
 	do_phishing_test(&rtests[_i]);
432 499
 }
433 500
 END_TEST
501
+
502
+START_TEST (phishingScan_test_allscan)
503
+{
504
+	do_phishing_test_allscan(&rtests[_i]);
505
+}
506
+END_TEST
434 507
 #endif
435 508
 
436 509
 #ifdef CHECK_HAVE_LOOPS
... ...
@@ -515,6 +588,27 @@ START_TEST(phishing_fake_test)
515 515
 }
516 516
 END_TEST
517 517
 
518
+START_TEST(phishing_fake_test_allscan)
519
+{
520
+	char buf[4096];
521
+	FILE *f = fdopen(open_testfile("input/daily.pdb"),"r");
522
+	fail_unless(!!f,"fopen daily.pdb");
523
+	while(fgets(buf, sizeof(buf), f)) {
524
+		struct rtest rtest;
525
+		const char *pdb = strchr(buf,':');
526
+		fail_unless(!!pdb, "missing : in pdb");
527
+		rtest.realurl = pdb;
528
+		rtest.displayurl = pdb;
529
+		rtest.result = 2;
530
+		do_phishing_test_allscan(&rtest);
531
+		rtest.realurl = "http://fake.example.com";
532
+		rtest.result = 0;
533
+		do_phishing_test_allscan(&rtest);
534
+	}
535
+	fclose(f);
536
+}
537
+END_TEST
538
+
518 539
 Suite *test_regex_suite(void)
519 540
 {
520 541
 	Suite *s = suite_create("regex");
... ...
@@ -539,16 +633,20 @@ Suite *test_regex_suite(void)
539 539
 	tcase_add_unchecked_fixture(tc_phish, psetup, pteardown);
540 540
 #ifdef CHECK_HAVE_LOOPS
541 541
 	tcase_add_loop_test(tc_phish, phishingScan_test, 0, sizeof(rtests)/sizeof(rtests[0]));
542
+	tcase_add_loop_test(tc_phish, phishingScan_test_allscan, 0, sizeof(rtests)/sizeof(rtests[0]));
542 543
 #endif
543 544
 	tcase_add_test(tc_phish, phishing_fake_test);
545
+	tcase_add_test(tc_phish, phishing_fake_test_allscan);
544 546
 
545 547
 	tc_phish2 = tcase_create("phishingScan with 2 dbs");
546 548
 	suite_add_tcase(s, tc_phish2);
547 549
 	tcase_add_unchecked_fixture(tc_phish2, psetup2, pteardown);
548 550
 #ifdef CHECK_HAVE_LOOPS
549 551
 	tcase_add_loop_test(tc_phish2, phishingScan_test, 0, sizeof(rtests)/sizeof(rtests[0]));
552
+	tcase_add_loop_test(tc_phish2, phishingScan_test_allscan, 0, sizeof(rtests)/sizeof(rtests[0]));
550 553
 #endif
551 554
 	tcase_add_test(tc_phish2, phishing_fake_test);
555
+	tcase_add_test(tc_phish2, phishing_fake_test_allscan);
552 556
 #ifdef CHECK_HAVE_LOOPS
553 557
 	tcase_add_loop_test(tc_phish, test_url_canon, 0, sizeof(uc)/sizeof(uc[0]));
554 558
 #endif