Browse code

extract and scan RAR archives file-by-file (bb#141)

git-svn: trunk@2615

Tomasz Kojm authored on 2007/01/13 09:01:39
Showing 4 changed files
... ...
@@ -1,3 +1,8 @@
1
+Sat Jan 13 00:59:01 CET 2007 (tk)
2
+---------------------------------
3
+  * libclamav: extract and scan RAR archives file-by-file (bb#141)
4
+	       Patch from Edwin
5
+
1 6
 Fri Jan 12 22:03:53 CET 2007 (acab)
2 7
 -----------------------------------
3 8
   * libclamav/mew: Cleanup. Now fully merged.
... ...
@@ -16,6 +21,7 @@ Fri Jan 12 18:51:33 CET 2007 (acab)
16 16
   * libclamav: add MEW support from Michal Spadlinski <gim913 * gmail.com>
17 17
 	       Part of the Google Summer of Code program
18 18
 
19
+>>>>>>> 1.1692
19 20
 Fri Jan 12 18:35:02 CET 2007 (tk)
20 21
 ---------------------------------
21 22
   * libclamav/phishcheck.c: add img url link-type filtering (patch from Edwin)
... ...
@@ -118,149 +118,177 @@ extern short cli_leavetemps_flag;
118 118
 
119 119
 static int cli_scanfile(const char *filename, cli_ctx *ctx);
120 120
 
121
-static int cli_scanrar(int desc, cli_ctx *ctx, off_t sfx_offset, uint32_t *sfx_check)
121
+static int cli_unrar_scanmetadata(int desc, rar_metadata_t *metadata, cli_ctx *ctx, unsigned int files, uint32_t* sfx_check)
122 122
 {
123
-	int ret = CL_CLEAN;
124
-	unsigned int files = 0;
125
-	rar_metadata_t *metadata, *metadata_tmp;
126
-	struct cli_meta_node *mdata;
127
-	char *dir;
128
-
123
+	int ret = CL_SUCCESS;
124
+	struct cli_meta_node* mdata;
129 125
 
130
-    cli_dbgmsg("in scanrar()\n");
131 126
 
132
-    /* generate the temporary directory */
133
-    dir = cli_gentemp(NULL);
134
-    if(mkdir(dir, 0700)) {
135
-	cli_dbgmsg("RAR: Can't create temporary directory %s\n", dir);
136
-	free(dir);
137
-	return CL_ETMPDIR;
127
+    if(files == 1 && sfx_check) {
128
+	if(*sfx_check == metadata->crc)
129
+	    return CL_BREAK;/* break extract loop */
130
+	else
131
+	    *sfx_check = metadata->crc;
138 132
     }
139 133
 
140
-    if(sfx_offset)
141
-	lseek(desc, sfx_offset, SEEK_SET);
142
-
143
-    metadata = metadata_tmp = cli_unrar(desc, dir, ctx->limits);
144
-
145
-    if(cli_scandir(dir, ctx) == CL_VIRUS) {
146
-	    ret = CL_VIRUS;
147
-    } else while(metadata) {
148
-
149
-	files++;
134
+    cli_dbgmsg("RAR: %s, crc32: 0x%x, encrypted: %d, compressed: %u, normal: %u, method: %d, ratio: %d (max: %d)\n",
135
+	metadata->filename, metadata->crc, metadata->encrypted, metadata->pack_size,
136
+	metadata->unpack_size, metadata->method,
137
+	metadata->pack_size ? ((unsigned int) metadata->unpack_size / (unsigned int) metadata->pack_size) : 0, ctx->limits ? ctx->limits->maxratio : 0);
150 138
 
151
-	if(files == 1 && sfx_check) {
152
-	    if(*sfx_check == metadata->crc)
153
-		break;
154
-	    else
155
-		*sfx_check = metadata->crc;
156
-	}
157
-
158
-	cli_dbgmsg("RAR: %s, crc32: 0x%x, encrypted: %d, compressed: %u, normal: %u, method: %d, ratio: %d (max: %d)\n",
159
-		metadata->filename, metadata->crc, metadata->encrypted, metadata->pack_size,
160
-		metadata->unpack_size, metadata->method,
161
-		metadata->pack_size ? ((unsigned int) metadata->unpack_size / (unsigned int) metadata->pack_size) : 0, ctx->limits ? ctx->limits->maxratio : 0);
139
+    /* Scan metadata */
140
+    mdata = ctx->engine->rar_mlist;
141
+    if(mdata) do {
142
+	if(mdata->encrypted != metadata->encrypted)
143
+	    continue;
162 144
 
163
-	/* Scan metadata */
164
-	mdata = ctx->engine->rar_mlist;
165
-	if(mdata) do {
166
-	    if(mdata->encrypted != metadata->encrypted)
167
-		continue;
145
+	if(mdata->crc32 && (unsigned int) mdata->crc32 != metadata->crc)
146
+	    continue;
168 147
 
169
-	    if(mdata->crc32 && (unsigned int) mdata->crc32 != metadata->crc)
170
-		continue;
148
+	if(mdata->csize > 0 && (unsigned int) mdata->csize != metadata->pack_size)
149
+	    continue;
171 150
 
172
-	    if(mdata->csize > 0 && (unsigned int) mdata->csize != metadata->pack_size)
173
-		continue;
151
+	if(mdata->size >= 0 && (unsigned int) mdata->size != metadata->unpack_size)
152
+	    continue;
174 153
 
175
-	    if(mdata->size >= 0 && (unsigned int) mdata->size != metadata->unpack_size)
176
-		continue;
154
+	if(mdata->method >= 0 && mdata->method != metadata->method)
155
+	    continue;
177 156
 
178
-	    if(mdata->method >= 0 && mdata->method != metadata->method)
179
-		continue;
157
+	if(mdata->fileno && mdata->fileno != files)
158
+	    continue;
180 159
 
181
-	    if(mdata->fileno && mdata->fileno != files)
182
-		continue;
160
+	if(mdata->maxdepth && ctx->arec > mdata->maxdepth)
161
+	    continue;
183 162
 
184
-	    if(mdata->maxdepth && ctx->arec > mdata->maxdepth)
185
-		continue;
163
+	/* TODO add support for regex */
164
+	/*if(mdata->filename && !strstr(zdirent.d_name, mdata->filename))*/
165
+	if(mdata->filename && strcmp((char *) metadata->filename, mdata->filename))
166
+	    continue;
186 167
 
187
-	    /* TODO add support for regex */
188
-	    /*if(mdata->filename && !strstr(zdirent.d_name, mdata->filename))*/
189
-	    if(mdata->filename && strcmp((char *) metadata->filename, mdata->filename))
190
-		continue;
168
+	break; /* matched */
191 169
 
192
-	    break; /* matched */
170
+    } while((mdata = mdata->next));
193 171
 
194
-	} while((mdata = mdata->next));
172
+    if(mdata) {
173
+	*ctx->virname = mdata->virname;
174
+	return CL_VIRUS;	   
175
+    }
195 176
 
196
-	if(mdata) {
197
-	    *ctx->virname = mdata->virname;
198
-	    ret = CL_VIRUS;
199
-	    break;
177
+    if(DETECT_ENCRYPTED && metadata->encrypted) {
178
+	cli_dbgmsg("RAR: Encrypted files found in archive.\n");
179
+	lseek(desc, 0, SEEK_SET);
180
+	ret = cli_scandesc(desc, ctx, 0, 0, 0, NULL);
181
+	if(ret != CL_VIRUS) {
182
+	    *ctx->virname = "Encrypted.RAR";
183
+	    return CL_VIRUS;
200 184
 	}
185
+    }
201 186
 
202
-	if(DETECT_ENCRYPTED && metadata->encrypted) {
203
-	    cli_dbgmsg("RAR: Encrypted files found in archive.\n");
204
-	    lseek(desc, 0, SEEK_SET);
205
-	    ret = cli_scandesc(desc, ctx, 0, 0, 0, NULL);
206
-	    if(ret < 0) {
207
-		break;
208
-	    } else if(ret != CL_VIRUS) {
209
-		*ctx->virname = "Encrypted.RAR";
210
-		ret = CL_VIRUS;
187
+/*
188
+    TROG - TODO: multi-volume files
189
+    if((rarlist->item.Flags & 0x03) != 0) {
190
+	cli_dbgmsg("RAR: Skipping %s (split)\n", rarlist->item.Name);
191
+	rarlist = rarlist->next;
192
+	continue;
193
+    }
194
+*/
195
+    if(ctx->limits) {
196
+	if(ctx->limits->maxratio && metadata->unpack_size && metadata->pack_size) {
197
+	    if((unsigned int) metadata->unpack_size / (unsigned int) metadata->pack_size >= ctx->limits->maxratio) {
198
+		cli_dbgmsg("RAR: Max ratio reached (normal: %u, compressed: %u, max: %ld)\n", metadata->unpack_size, metadata->pack_size, ctx->limits->maxratio);
199
+		*ctx->virname = "Oversized.RAR";
200
+		return CL_VIRUS;
211 201
 	    }
212
-	    break;
213 202
 	}
214 203
 
215
-/*
216
-	TROG - TODO: multi-volume files
217
-	if((rarlist->item.Flags & 0x03) != 0) {
218
-	    cli_dbgmsg("RAR: Skipping %s (split)\n", rarlist->item.Name);
219
-	    rarlist = rarlist->next;
220
-	    continue;
221
-	}
222
-*/
223
-	if(ctx->limits) {
224
-	    if(ctx->limits->maxratio && metadata->unpack_size && metadata->pack_size) {
225
-		if((unsigned int) metadata->unpack_size / (unsigned int) metadata->pack_size >= ctx->limits->maxratio) {
226
-		    cli_dbgmsg("RAR: Max ratio reached (normal: %u, compressed: %u, max: %ld)\n", metadata->unpack_size,
227
-		    		metadata->pack_size, ctx->limits->maxratio);
228
-		    *ctx->virname = "Oversized.RAR";
229
-		    ret = CL_VIRUS;
230
-		    break;
231
-		}
204
+	if(ctx->limits->maxfilesize && (metadata->unpack_size > ctx->limits->maxfilesize)) {
205
+	    cli_dbgmsg("RAR: %s: Size exceeded (%u, max: %lu)\n", metadata->filename, metadata->unpack_size, ctx->limits->maxfilesize);
206
+	    if(BLOCKMAX) {
207
+		*ctx->virname = "RAR.ExceededFileSize";
208
+		return CL_VIRUS;
232 209
 	    }
210
+	    return CL_SUCCESS;
211
+	}
233 212
 
234
-	    if(ctx->limits->maxfilesize && (metadata->unpack_size > (unsigned int) ctx->limits->maxfilesize)) {
235
-		cli_dbgmsg("RAR: %s: Size exceeded (%u, max: %lu)\n", metadata->filename,
236
-					metadata->unpack_size, ctx->limits->maxfilesize);
237
-		if(BLOCKMAX) {
238
-		    *ctx->virname = "RAR.ExceededFileSize";
239
-		    ret = CL_VIRUS;
240
-		    break;
241
-		}
242
-		metadata = metadata->next;
243
-		continue;
213
+	if(ctx->limits->maxfiles && (files > ctx->limits->maxfiles)) {
214
+	    cli_dbgmsg("RAR: Files limit reached (max: %d)\n", ctx->limits->maxfiles);
215
+	    if(BLOCKMAX) {
216
+		*ctx->virname = "RAR.ExceededFilesLimit";
217
+		return CL_VIRUS;
244 218
 	    }
219
+	    return CL_BREAK;
220
+	}
221
+    }
245 222
 
246
-	    if(ctx->limits->maxfiles && (files > ctx->limits->maxfiles)) {
247
-		cli_dbgmsg("RAR: Files limit reached (max: %d)\n", ctx->limits->maxfiles);
248
-		if(BLOCKMAX) {
249
-		    *ctx->virname = "RAR.ExceededFilesLimit";
250
-		    ret = CL_VIRUS;
251
-		    break;
252
-		}
223
+    return ret;
224
+}
225
+
226
+static int cli_scanrar(int desc, cli_ctx *ctx, off_t sfx_offset, uint32_t *sfx_check)
227
+{
228
+	int ret = CL_CLEAN;
229
+	unsigned int files = 0;
230
+	rar_metadata_t *metadata, *metadata_tmp;
231
+	char *dir;
232
+	rar_state_t rar_state;
233
+
234
+
235
+    cli_dbgmsg("in scanrar()\n");
236
+
237
+    /* generate the temporary directory */
238
+    dir = cli_gentemp(NULL);
239
+    if(mkdir(dir, 0700)) {
240
+	cli_dbgmsg("RAR: Can't create temporary directory %s\n", dir);
241
+	free(dir);
242
+	return CL_ETMPDIR;
243
+    }
244
+
245
+    if(sfx_offset)
246
+	lseek(desc, sfx_offset, SEEK_SET);
247
+
248
+    if((ret = cli_unrar_open(desc, dir, &rar_state)) != CL_SUCCESS) {
249
+	if(!cli_leavetemps_flag)
250
+	    cli_rmdirs(dir);
251
+	free(dir);
252
+	cli_dbgmsg("RAR: Error: %s\n", cl_strerror(ret));
253
+	return ret;
254
+    }
255
+
256
+    do {
257
+	int rc;
258
+	rar_state.unpack_data->ofd = -1;
259
+	ret = cli_unrar_extract_next(&rar_state,dir);
260
+	if(rar_state.unpack_data->ofd > 0) {
261
+	    lseek(rar_state.unpack_data->ofd,0,SEEK_SET);
262
+	    rc = cli_magic_scandesc(rar_state.unpack_data->ofd,ctx);
263
+	    close(rar_state.unpack_data->ofd);
264
+	    if(!cli_leavetemps_flag) 
265
+		unlink(rar_state.filename);
266
+	    if(rc == CL_VIRUS ) {
267
+		cli_dbgmsg("RAR: infected with %s\n",*ctx->virname);
268
+		ret = CL_VIRUS;
253 269
 		break;
254 270
 	    }
255 271
 	}
256 272
 
257
-	metadata = metadata->next;
258
-    }
273
+	if(ret == CL_SUCCESS)
274
+	    ret = cli_unrar_scanmetadata(desc,rar_state.metadata, ctx, rar_state.file_count, sfx_check);
275
+
276
+    } while(ret == CL_SUCCESS);
277
+
278
+    if(ret == CL_BREAK)
279
+	ret = CL_CLEAN;
280
+
281
+    cli_unrar_close(&rar_state);
282
+    metadata = metadata_tmp = rar_state.metadata; 
283
+
284
+    if(cli_scandir(rar_state.comment_dir, ctx) == CL_VIRUS)
285
+	ret = CL_VIRUS;
259 286
 
260 287
     if(!cli_leavetemps_flag)
261 288
         cli_rmdirs(dir);
262 289
 
263 290
     free(dir);
291
+
264 292
     metadata = metadata_tmp;
265 293
     while (metadata) {
266 294
     	metadata_tmp = metadata->next;
... ...
@@ -1381,25 +1381,25 @@ static int rar_unpack(int fd, int method, int solid, unpack_data_t *unpack_data)
1381 1381
 	return retval;
1382 1382
 }
1383 1383
 
1384
-rar_metadata_t *cli_unrar(int fd, const char *dirname, const struct cl_limits *limits)
1384
+int cli_unrar_open(int fd, const char *dirname, rar_state_t* state)
1385 1385
 {
1386
-	main_header_t *main_hdr;
1387 1386
 	int ofd, retval;
1388
-	unsigned long file_count=1;
1389
-	file_header_t *file_header;
1390 1387
 	unsigned char filename[1024];
1391 1388
 	unpack_data_t *unpack_data;
1392
-	rar_metadata_t *metadata=NULL, *metadata_tail=NULL, *new_metadata;
1389
+	main_header_t *main_hdr;
1393 1390
 	off_t offset;
1394 1391
 
1395 1392
 	cli_dbgmsg("in cli_unrar\n");
1393
+	if(!state) {
1394
+		return CL_ENULLARG;
1395
+	}
1396 1396
 	if (!is_rar_archive(fd)) {
1397
-		return FALSE;
1397
+		return CL_EFORMAT;
1398 1398
 	}
1399 1399
 	unpack_data = cli_malloc(sizeof(unpack_data_t));
1400 1400
 	if(!unpack_data) {
1401 1401
 	    cli_dbgmsg("unrar: cli_unrar: cli_malloc failed for unpack_data\n");
1402
-	    return FALSE;
1402
+	    return CL_EMEM;
1403 1403
 	}
1404 1404
 	unpack_data->rarvm_data.mem = NULL;
1405 1405
 	unpack_data->old_filter_lengths = NULL;
... ...
@@ -1416,12 +1416,33 @@ rar_metadata_t *cli_unrar(int fd, const char *dirname, const struct cl_limits *l
1416 1416
 		init_filters(unpack_data);
1417 1417
 		unpack_free_data(unpack_data);
1418 1418
 		free(unpack_data);
1419
-		return metadata;
1419
+		return CL_ERAR;
1420 1420
 	}
1421 1421
 	cli_dbgmsg("Head CRC: %.4x\n", main_hdr->head_crc);
1422 1422
 	cli_dbgmsg("Head Type: %.2x\n", main_hdr->head_type);
1423 1423
 	cli_dbgmsg("Flags: %.4x\n", main_hdr->flags);
1424 1424
 	cli_dbgmsg("Head Size: %.4x\n", main_hdr->head_size);
1425
+
1426
+	snprintf(filename,1024,"%s/comments",dirname);
1427
+	if(mkdir(filename,0700)) {
1428
+		cli_dbgmsg("cli_unrar: Unable to create comment temporary directory\n");
1429
+		free(main_hdr);
1430
+		ppm_destructor(&unpack_data->ppm_data);
1431
+		init_filters(unpack_data);
1432
+		unpack_free_data(unpack_data);
1433
+		free(unpack_data);
1434
+		return CL_ETMPDIR;
1435
+	}
1436
+	state->comment_dir = cli_strdup(filename);
1437
+	if(!state->comment_dir) {
1438
+		free(main_hdr);
1439
+		ppm_destructor(&unpack_data->ppm_data);
1440
+		init_filters(unpack_data);
1441
+		unpack_free_data(unpack_data);
1442
+		free(unpack_data);
1443
+		return CL_EMEM;
1444
+	}
1445
+
1425 1446
 	if ((main_hdr->flags & MHD_VOLUME) != 0) {
1426 1447
 		/* Part of a RAR VOLUME - Skip it */
1427 1448
 		cli_dbgmsg("RAR MUTIPART VOLUME - Skippng.\n");
... ...
@@ -1430,7 +1451,8 @@ rar_metadata_t *cli_unrar(int fd, const char *dirname, const struct cl_limits *l
1430 1430
 		init_filters(unpack_data);
1431 1431
 		unpack_free_data(unpack_data);
1432 1432
 		free(unpack_data);
1433
-		return metadata;
1433
+		free(state->comment_dir);
1434
+		return CL_ESUPPORT;
1434 1435
         }
1435 1436
 
1436 1437
 	if (main_hdr->head_size < SIZEOF_NEWMHD) {
... ...
@@ -1439,7 +1461,8 @@ rar_metadata_t *cli_unrar(int fd, const char *dirname, const struct cl_limits *l
1439 1439
 		init_filters(unpack_data);
1440 1440
 		unpack_free_data(unpack_data);
1441 1441
 		free(unpack_data);
1442
-		return metadata;
1442
+		free(state->comment_dir);
1443
+		return CL_EFORMAT;
1443 1444
 	}
1444 1445
 	if (main_hdr->flags & MHD_COMMENT) {
1445 1446
 		comment_header_t *comment_header;
... ...
@@ -1453,11 +1476,18 @@ rar_metadata_t *cli_unrar(int fd, const char *dirname, const struct cl_limits *l
1453 1453
 			cli_dbgmsg("UnPack Size: 0x%.4x\n", comment_header->unpack_size);
1454 1454
 			cli_dbgmsg("UnPack Version: 0x%.2x\n", comment_header->unpack_ver);
1455 1455
 			cli_dbgmsg("Pack Method: 0x%.2x\n", comment_header->method);
1456
-			snprintf(filename, 1024, "%s/main.cmt", dirname);
1456
+			snprintf(filename, 1024, "%s/main.cmt", state->comment_dir);
1457 1457
 			ofd = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0600);
1458 1458
 			if (ofd < 0) {
1459 1459
 				free(comment_header);
1460 1460
 				cli_dbgmsg("ERROR: Failed to open output file\n");
1461
+				free(main_hdr);
1462
+				ppm_destructor(&unpack_data->ppm_data);
1463
+				init_filters(unpack_data);
1464
+				unpack_free_data(unpack_data);
1465
+				free(unpack_data);
1466
+				free(state->comment_dir);
1467
+				return CL_EIO;
1461 1468
 			} else {
1462 1469
 				if (comment_header->method == 0x30) {
1463 1470
 					cli_dbgmsg("Copying stored comment (not packed)\n");
... ...
@@ -1482,17 +1512,35 @@ rar_metadata_t *cli_unrar(int fd, const char *dirname, const struct cl_limits *l
1482 1482
 			init_filters(unpack_data);
1483 1483
 			unpack_free_data(unpack_data);
1484 1484
 			free(unpack_data);
1485
-			return metadata;
1485
+			free(state->comment_dir);
1486
+			return CL_EFORMAT; /* truncated? */
1486 1487
 		}
1487 1488
 	}
1488
-	for (;;) {
1489
-		file_header=read_block(fd, FILE_HEAD);
1489
+
1490
+	state->unpack_data = unpack_data;
1491
+	state->main_hdr = main_hdr;
1492
+	state->metadata_tail = state->metadata = NULL;
1493
+	state->file_count = 1;
1494
+	state->offset = offset;
1495
+	state->fd = fd;
1496
+
1497
+	return CL_SUCCESS;
1498
+}
1499
+
1500
+int cli_unrar_extract_next(rar_state_t* state,const char* dirname)
1501
+{
1502
+	int retval;
1503
+	unsigned char filename[1024];
1504
+	int ofd;
1505
+
1506
+	rar_metadata_t *new_metadata;
1507
+	file_header_t *file_header = read_block(state->fd, FILE_HEAD);
1490 1508
 		if (!file_header) {
1491
-			break;
1509
+		return CL_BREAK;/* end of archive */
1492 1510
 		}
1493 1511
 		new_metadata = cli_malloc(sizeof(rar_metadata_t));
1494 1512
 		if (!new_metadata) {
1495
-			break;
1513
+		return CL_EMEM;
1496 1514
 		}
1497 1515
 		new_metadata->pack_size = file_header->pack_size;
1498 1516
 		new_metadata->unpack_size = file_header->unpack_size;
... ...
@@ -1501,32 +1549,17 @@ rar_metadata_t *cli_unrar(int fd, const char *dirname, const struct cl_limits *l
1501 1501
 		new_metadata->filename = strdup(file_header->filename);
1502 1502
 		new_metadata->next = NULL;
1503 1503
 		new_metadata->encrypted = FALSE;
1504
-		if (metadata_tail == NULL) {
1505
-			metadata_tail = metadata = new_metadata;
1504
+	if (state->metadata_tail == NULL) {
1505
+		state->metadata_tail = state->metadata = new_metadata;
1506 1506
 		} else {
1507
-			metadata_tail->next = new_metadata;
1508
-			metadata_tail = new_metadata;
1509
-		}
1510
-		if (limits) {
1511
-			if (limits->maxratio && file_header->unpack_size && file_header->pack_size) {
1512
-				if(file_header->unpack_size / file_header->pack_size >= limits->maxratio) {
1513
-					free(file_header->filename);
1514
-					free(file_header);
1515
-					break;
1516
-				}
1517
-			}
1518
-
1519
-			if (limits->maxfilesize && (file_header->unpack_size > (unsigned int) limits->maxfilesize)) {
1520
-				free(file_header->filename);
1521
-				free(file_header);
1522
-				break;
1523
-			}
1507
+		state->metadata_tail->next = new_metadata;
1508
+		state->metadata_tail = new_metadata;
1524 1509
 		}
1525 1510
 		if (file_header->flags & LHD_COMMENT) {
1526 1511
 			comment_header_t *comment_header;
1527 1512
 
1528 1513
 			cli_dbgmsg("File comment present\n");
1529
-			comment_header = read_header(fd, COMM_HEAD);
1514
+		comment_header = read_header(state->fd, COMM_HEAD);
1530 1515
 			if (comment_header) {
1531 1516
 				cli_dbgmsg("Comment type: 0x%.2x\n", comment_header->head_type);
1532 1517
 				cli_dbgmsg("Head size: 0x%.4x\n", comment_header->head_size);
... ...
@@ -1538,63 +1571,62 @@ rar_metadata_t *cli_unrar(int fd, const char *dirname, const struct cl_limits *l
1538 1538
 						(comment_header->method > 0x30)) {
1539 1539
 					cli_dbgmsg("Can't process file comment - skipping\n");
1540 1540
 				} else {
1541
-					snprintf(filename, 1024, "%s/%lu.cmt", dirname, file_count);
1541
+				snprintf(filename, 1024, "%s/%lu.cmt", state->comment_dir, state->file_count);
1542 1542
 					ofd = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0600);
1543 1543
 					if (ofd < 0) {
1544 1544
 						free(comment_header);
1545 1545
 						cli_dbgmsg("ERROR: Failed to open output file\n");
1546 1546
 					} else {
1547 1547
                 	                        cli_dbgmsg("Copying file comment (not packed)\n");
1548
-                        	                copy_file_data(fd, ofd, comment_header->unpack_size);
1548
+					copy_file_data(state->fd, ofd, comment_header->unpack_size);
1549 1549
 						close(ofd);
1550 1550
 					}
1551 1551
 				}
1552 1552
 				free(comment_header);
1553 1553
 			}
1554 1554
 		}
1555
-	        if (lseek(fd, file_header->start_offset+file_header->head_size, SEEK_SET) !=
1555
+	if (lseek(state->fd, file_header->start_offset+file_header->head_size, SEEK_SET) !=
1556 1556
 							file_header->start_offset+file_header->head_size) {
1557
-        	        cli_dbgmsg("Seek failed: %ld\n", offset+file_header->head_size);
1557
+		cli_dbgmsg("Seek failed: %ld\n", state->offset+file_header->head_size);
1558 1558
 			free(file_header->filename);
1559 1559
 			free(file_header);
1560
-			break;
1560
+		return CL_ERAR;
1561 1561
         	}
1562 1562
 		if (file_header->flags & LHD_PASSWORD) {
1563 1563
 			cli_dbgmsg("PASSWORDed file: %s\n", file_header->filename);
1564
-			metadata_tail->encrypted = TRUE;
1564
+		state->metadata_tail->encrypted = TRUE;
1565 1565
 		} else /*if (file_header->unpack_size)*/ {
1566
-			snprintf(filename, 1024, "%s/%lu.ura", dirname, file_count);
1567
-			ofd = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0600);
1566
+		snprintf(state->filename, 1024, "%s/%lu.ura", dirname, state->file_count);
1567
+		ofd = open(state->filename, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, 0600);
1568 1568
 			if (ofd < 0) {
1569 1569
 				free(file_header->filename);
1570 1570
 				free(file_header);
1571 1571
 				cli_dbgmsg("ERROR: Failed to open output file\n");
1572
-				continue;
1572
+			return CL_EOPEN;
1573 1573
 			}
1574
-			unpack_data->ofd = ofd;
1574
+		state->unpack_data->ofd = ofd;
1575 1575
 			if (file_header->method == 0x30) {
1576 1576
 				cli_dbgmsg("Copying stored file (not packed)\n");
1577
-				copy_file_data(fd, ofd, file_header->pack_size);
1577
+			copy_file_data(state->fd, ofd, file_header->pack_size);
1578 1578
 			} else {
1579
-				unpack_data->dest_unp_size = file_header->unpack_size;
1580
-				unpack_data->pack_size = file_header->pack_size;
1579
+			state->unpack_data->dest_unp_size = file_header->unpack_size;
1580
+			state->unpack_data->pack_size = file_header->pack_size;
1581 1581
 				if (file_header->unpack_ver <= 15) {
1582
-					retval = rar_unpack(fd, 15, (file_count>1) &&
1583
-						((main_hdr->flags&MHD_SOLID)!=0), unpack_data);
1582
+				retval = rar_unpack(state->fd, 15, (state->file_count>1) &&
1583
+						((state->main_hdr->flags&MHD_SOLID)!=0), state->unpack_data);
1584 1584
 				} else {
1585
-					if ((file_count == 1) && (file_header->flags & LHD_SOLID)) {
1585
+				if ((state->file_count == 1) && (file_header->flags & LHD_SOLID)) {
1586 1586
 						cli_warnmsg("RAR: First file can't be SOLID.\n");
1587
-						close(ofd);
1588
-						break;
1587
+					return CL_ERAR;
1589 1588
 					} else {
1590
-						retval = rar_unpack(fd, file_header->unpack_ver,
1591
-							file_header->flags & LHD_SOLID,	unpack_data);
1589
+					retval = rar_unpack(state->fd, file_header->unpack_ver,
1590
+							file_header->flags & LHD_SOLID,	state->unpack_data);
1592 1591
 					}
1593 1592
 				}
1594 1593
 				cli_dbgmsg("Expected File CRC: 0x%x\n", file_header->file_crc);
1595
-				cli_dbgmsg("Computed File CRC: 0x%x\n", unpack_data->unp_crc^0xffffffff);
1596
-				if (unpack_data->unp_crc != 0xffffffff) {
1597
-					if (file_header->file_crc != (unpack_data->unp_crc^0xffffffff)) {
1594
+			cli_dbgmsg("Computed File CRC: 0x%x\n", state->unpack_data->unp_crc^0xffffffff);
1595
+			if (state->unpack_data->unp_crc != 0xffffffff) {
1596
+				if (file_header->file_crc != (state->unpack_data->unp_crc^0xffffffff)) {
1598 1597
 						cli_warnmsg("RAR CRC error. Please report the bug at http://bugs.clamav.net/\n");
1599 1598
 					}
1600 1599
 				}
... ...
@@ -1602,34 +1634,31 @@ rar_metadata_t *cli_unrar(int fd, const char *dirname, const struct cl_limits *l
1602 1602
 					cli_dbgmsg("Corrupt file detected\n");
1603 1603
 					if (file_header->flags & LHD_SOLID) {
1604 1604
 						cli_dbgmsg("SOLID archive, can't continue\n");
1605
-						close(ofd);
1606
-						break;
1605
+					return CL_ERAR;
1607 1606
 					}
1608 1607
 				}
1609 1608
 			}
1610 1609
 		
1611
-			close(ofd);
1612 1610
 		}
1613
-		if (lseek(fd, file_header->next_offset, SEEK_SET) != file_header->next_offset) {
1611
+	if (lseek(state->fd, file_header->next_offset, SEEK_SET) != file_header->next_offset) {
1614 1612
 			cli_dbgmsg("ERROR: seek failed: %ld\n", file_header->next_offset);
1615 1613
 			free(file_header->filename);
1616 1614
 			free(file_header);
1617
-			free(main_hdr);
1618
-			ppm_destructor(&unpack_data->ppm_data);
1619
-			init_filters(unpack_data);
1620
-			unpack_free_data(unpack_data);
1621
-			free(unpack_data);
1622
-			return metadata;
1615
+			return CL_ERAR;
1623 1616
 		}
1624 1617
 		free(file_header->filename);
1625 1618
 		free(file_header);
1626
-		unpack_free_data(unpack_data);
1627
-		file_count++;
1628
-	}
1629
-	ppm_destructor(&unpack_data->ppm_data);
1630
-	free(main_hdr);
1631
-	init_filters(unpack_data);
1632
-	unpack_free_data(unpack_data);
1633
-	free(unpack_data);
1634
-	return metadata;
1619
+	unpack_free_data(state->unpack_data);
1620
+	state->file_count++;
1621
+	return CL_SUCCESS;
1622
+	}
1623
+
1624
+void cli_unrar_close(rar_state_t* state)
1625
+{
1626
+	ppm_destructor(&state->unpack_data->ppm_data);
1627
+	free(state->main_hdr);
1628
+	init_filters(state->unpack_data);
1629
+	unpack_free_data(state->unpack_data);
1630
+	free(state->unpack_data);
1631
+	free(state->comment_dir);
1635 1632
 }
... ...
@@ -56,6 +56,7 @@ typedef struct rar_metadata_tag
56 56
 	struct rar_metadata_tag *next;
57 57
 } rar_metadata_t;
58 58
 
59
+
59 60
 #define SIZEOF_MARKHEAD 7
60 61
 #define SIZEOF_NEWMHD 13
61 62
 #define SIZEOF_NEWLHD 32
... ...
@@ -291,6 +292,18 @@ typedef struct unpack_data_tag
291 291
 	unsigned int ntopl[256], ntoplb[256], ntoplc[256];
292 292
 } unpack_data_t;
293 293
 
294
+typedef struct rar_state_tag {
295
+	rar_metadata_t *metadata;
296
+	rar_metadata_t *metadata_tail;
297
+	unpack_data_t *unpack_data;
298
+	main_header_t *main_hdr;
299
+	const char* comment_dir;
300
+	unsigned long file_count;
301
+	off_t offset;
302
+	int fd;
303
+	char  filename[1024];
304
+} rar_state_t;
305
+
294 306
 typedef enum
295 307
 {
296 308
 	ALL_HEAD=0,
... ...
@@ -312,7 +325,10 @@ enum BLOCK_TYPES
312 312
 	BLOCK_PPM
313 313
 };
314 314
 
315
-rar_metadata_t *cli_unrar(int fd, const char *dirname, const struct cl_limits *limits);
315
+
316
+int cli_unrar_extract_next(rar_state_t* state,const char* dirname);
317
+int cli_unrar_open(int fd, const char *dirname, rar_state_t* state);
318
+void cli_unrar_close(rar_state_t* state);
316 319
 unsigned int rar_get_char(int fd, unpack_data_t *unpack_data);
317 320
 void addbits(unpack_data_t *unpack_data, int bits);
318 321
 unsigned int getbits(unpack_data_t *unpack_data);