#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#include <mspack.h>
#include <system.h>
#include <cab.h>
static struct mscabd_cabinet * cabd_open(
struct mscab_decompressor *base, char *filename);
static struct mscabd_cabinet * cabd_dopen(
struct mscab_decompressor *base, int desc);
static void cabd_close(
struct mscab_decompressor *base, struct mscabd_cabinet *origcab);
static int cabd_read_headers(
struct mspack_system *sys, struct mspack_file *fh,
struct mscabd_cabinet_p *cab, off_t offset, int quiet);
static char *cabd_read_string(
struct mspack_system *sys, struct mspack_file *fh,
struct mscabd_cabinet_p *cab, int *error);
static struct mscabd_cabinet *cabd_search(
struct mscab_decompressor *base, char *filename);
static struct mscabd_cabinet *cabd_dsearch(
struct mscab_decompressor *base, int desc);
static int cabd_find(
struct mscab_decompressor_p *this, unsigned char *buf,
struct mspack_file *fh, char *filename, int desc, off_t flen,
unsigned int *firstlen, struct mscabd_cabinet_p **firstcab);
static int cabd_prepend(
struct mscab_decompressor *base, struct mscabd_cabinet *cab,
struct mscabd_cabinet *prevcab);
static int cabd_append(
struct mscab_decompressor *base, struct mscabd_cabinet *cab,
struct mscabd_cabinet *nextcab);
static int cabd_merge(
struct mscab_decompressor *base, struct mscabd_cabinet *lcab,
struct mscabd_cabinet *rcab);
static int cabd_extract(
struct mscab_decompressor *base, struct mscabd_file *file, char *filename);
static int cabd_init_decomp(
struct mscab_decompressor_p *this, unsigned int ct);
static void cabd_free_decomp(
struct mscab_decompressor_p *this);
static int cabd_sys_read(
struct mspack_file *file, void *buffer, int bytes);
static int cabd_sys_write(
struct mspack_file *file, void *buffer, int bytes);
static int cabd_sys_read_block(
struct mspack_system *sys, struct mscabd_decompress_state *d, int *out,
int ignore_cksum);
static unsigned int cabd_checksum(
unsigned char *data, unsigned int bytes, unsigned int cksum);
static struct noned_state *noned_init(
struct mspack_system *sys, struct mspack_file *in, struct mspack_file *out,
int bufsize);
static int noned_decompress(
struct noned_state *s, off_t bytes);
static void noned_free(
struct noned_state *state);
static int cabd_param(
struct mscab_decompressor *base, int param, int value);
static int cabd_error(
struct mscab_decompressor *base);
struct mscab_decompressor *
mspack_create_cab_decompressor(struct mspack_system *sys)
{
struct mscab_decompressor_p *this = NULL;
if (!sys) sys = mspack_default_system;
if (!mspack_valid_system(sys)) return NULL;
if ((this = sys->alloc(sys, sizeof(struct mscab_decompressor_p)))) {
this->base.open = &cabd_open;
this->base.dopen = &cabd_dopen;
this->base.close = &cabd_close;
this->base.search = &cabd_search;
this->base.dsearch = &cabd_dsearch;
this->base.extract = &cabd_extract;
this->base.prepend = &cabd_prepend;
this->base.append = &cabd_append;
this->base.set_param = &cabd_param;
this->base.last_error = &cabd_error;
this->system = sys;
this->d = NULL;
this->error = MSPACK_ERR_OK;
this->param[MSCABD_PARAM_SEARCHBUF] = 32768;
this->param[MSCABD_PARAM_FIXMSZIP] = 0;
this->param[MSCABD_PARAM_DECOMPBUF] = 4096;
}
return (struct mscab_decompressor *) this;
}
void mspack_destroy_cab_decompressor(struct mscab_decompressor *base) {
struct mscab_decompressor_p *this = (struct mscab_decompressor_p *) base;
if (this) {
struct mspack_system *sys = this->system;
cabd_free_decomp(this);
if (this->d) {
if (this->d->infh) sys->close(this->d->infh);
sys->free(this->d);
}
sys->free(this);
}
}
static struct mscabd_cabinet *cabd_open(struct mscab_decompressor *base,
char *filename)
{
struct mscab_decompressor_p *this = (struct mscab_decompressor_p *) base;
struct mscabd_cabinet_p *cab = NULL;
struct mspack_system *sys;
struct mspack_file *fh;
int error;
if (!base) return NULL;
sys = this->system;
if ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) {
if ((cab = sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) {
cab->base.filename = filename;
error = cabd_read_headers(sys, fh, cab, (off_t) 0, 0);
if (error) {
cabd_close(base, (struct mscabd_cabinet *) cab);
cab = NULL;
}
this->error = error;
}
else {
this->error = MSPACK_ERR_NOMEMORY;
}
sys->close(fh);
}
else {
this->error = MSPACK_ERR_OPEN;
}
return (struct mscabd_cabinet *) cab;
}
static struct mscabd_cabinet *cabd_dopen(struct mscab_decompressor *base,
int desc)
{
struct mscab_decompressor_p *this = (struct mscab_decompressor_p *) base;
struct mscabd_cabinet_p *cab = NULL;
struct mspack_system *sys;
struct mspack_file *fh;
int error;
if (!base) return NULL;
sys = this->system;
if ((fh = sys->dopen(sys, desc, MSPACK_SYS_OPEN_READ))) {
if ((cab = sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) {
cab->base.filename = "descriptor";
cab->base.desc = dup(desc);
error = cabd_read_headers(sys, fh, cab, (off_t) 0, 0);
if (error) {
cabd_close(base, (struct mscabd_cabinet *) cab);
cab = NULL;
}
this->error = error;
}
else {
this->error = MSPACK_ERR_NOMEMORY;
}
sys->close(fh);
}
else {
this->error = MSPACK_ERR_OPEN;
}
return (struct mscabd_cabinet *) cab;
}
static void cabd_close(struct mscab_decompressor *base,
struct mscabd_cabinet *origcab)
{
struct mscab_decompressor_p *this = (struct mscab_decompressor_p *) base;
struct mscabd_folder_data *dat, *ndat;
struct mscabd_cabinet *cab, *ncab;
struct mscabd_folder *fol, *nfol;
struct mscabd_file *fi, *nfi;
struct mspack_system *sys;
if(origcab->desc)
close(origcab->desc);
if (!base) return;
sys = this->system;
this->error = MSPACK_ERR_OK;
while (origcab) {
for (fi = origcab->files; fi; fi = nfi) {
nfi = fi->next;
sys->free(fi->filename);
sys->free(fi);
}
for (fol = origcab->folders; fol; fol = nfol) {
nfol = fol->next;
if (this->d && (this->d->folder == (struct mscabd_folder_p *) fol)) {
if (this->d->infh) sys->close(this->d->infh);
cabd_free_decomp(this);
sys->free(this->d);
this->d = NULL;
}
for (dat = ((struct mscabd_folder_p *)fol)->data.next; dat; dat = ndat) {
ndat = dat->next;
sys->free(dat);
}
sys->free(fol);
}
for (cab = origcab; cab; cab = ncab) {
ncab = cab->prevcab;
sys->free(cab->prevname);
sys->free(cab->nextname);
sys->free(cab->previnfo);
sys->free(cab->nextinfo);
if (cab != origcab) sys->free(cab);
}
for (cab = origcab->nextcab; cab; cab = ncab) {
ncab = cab->nextcab;
sys->free(cab->prevname);
sys->free(cab->nextname);
sys->free(cab->previnfo);
sys->free(cab->nextinfo);
sys->free(cab);
}
cab = origcab->next;
sys->free(origcab);
origcab = cab;
}
}
static int cabd_read_headers(struct mspack_system *sys,
struct mspack_file *fh,
struct mscabd_cabinet_p *cab,
off_t offset, int quiet)
{
int num_folders, num_files, folder_resv, i, x;
struct mscabd_folder_p *fol, *linkfol = NULL;
struct mscabd_file *file, *linkfile = NULL;
unsigned char buf[64];
cab->base.next = NULL;
cab->base.files = NULL;
cab->base.folders = NULL;
cab->base.prevcab = cab->base.nextcab = NULL;
cab->base.prevname = cab->base.nextname = NULL;
cab->base.previnfo = cab->base.nextinfo = NULL;
cab->base.base_offset = offset;
if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) {
return MSPACK_ERR_SEEK;
}
if (sys->read(fh, &buf[0], cfhead_SIZEOF) != cfhead_SIZEOF) {
return MSPACK_ERR_READ;
}
if (EndGetI32(&buf[cfhead_Signature]) != 0x4643534D) {
return MSPACK_ERR_SIGNATURE;
}
cab->base.length = EndGetI32(&buf[cfhead_CabinetSize]);
cab->base.set_id = EndGetI16(&buf[cfhead_SetID]);
cab->base.set_index = EndGetI16(&buf[cfhead_CabinetIndex]);
num_folders = EndGetI16(&buf[cfhead_NumFolders]);
if (num_folders == 0) {
if (!quiet) sys->message(fh, "no folders in cabinet.");
return MSPACK_ERR_DATAFORMAT;
}
num_files = EndGetI16(&buf[cfhead_NumFiles]);
if (num_files == 0) {
if (!quiet) sys->message(fh, "no files in cabinet.");
return MSPACK_ERR_DATAFORMAT;
}
if ((buf[cfhead_MajorVersion] != 1) && (buf[cfhead_MinorVersion] != 3)) {
if (!quiet) sys->message(fh, "WARNING; cabinet version is not 1.3");
}
cab->base.flags = EndGetI16(&buf[cfhead_Flags]);
if (cab->base.flags & cfheadRESERVE_PRESENT) {
if (sys->read(fh, &buf[0], cfheadext_SIZEOF) != cfheadext_SIZEOF) {
return MSPACK_ERR_READ;
}
cab->base.header_resv = EndGetI16(&buf[cfheadext_HeaderReserved]);
folder_resv = buf[cfheadext_FolderReserved];
cab->block_resv = buf[cfheadext_DataReserved];
if (cab->base.header_resv > 60000) {
if (!quiet) sys->message(fh, "WARNING; reserved header > 60000.");
}
if (cab->base.header_resv) {
if (sys->seek(fh, (off_t) cab->base.header_resv, MSPACK_SYS_SEEK_CUR)) {
return MSPACK_ERR_SEEK;
}
}
}
else {
cab->base.header_resv = 0;
folder_resv = 0;
cab->block_resv = 0;
}
if (cab->base.flags & cfheadPREV_CABINET) {
cab->base.prevname = cabd_read_string(sys, fh, cab, &x); if (x) return x;
cab->base.previnfo = cabd_read_string(sys, fh, cab, &x); if (x) return x;
}
if (cab->base.flags & cfheadNEXT_CABINET) {
cab->base.nextname = cabd_read_string(sys, fh, cab, &x); if (x) return x;
cab->base.nextinfo = cabd_read_string(sys, fh, cab, &x); if (x) return x;
}
for (i = 0; i < num_folders; i++) {
if (sys->read(fh, &buf[0], cffold_SIZEOF) != cffold_SIZEOF) {
return MSPACK_ERR_READ;
}
if (folder_resv) {
if (sys->seek(fh, (off_t) folder_resv, MSPACK_SYS_SEEK_CUR)) {
return MSPACK_ERR_SEEK;
}
}
if (!(fol = sys->alloc(sys, sizeof(struct mscabd_folder_p)))) {
return MSPACK_ERR_NOMEMORY;
}
fol->base.next = NULL;
fol->base.comp_type = EndGetI16(&buf[cffold_CompType]);
fol->base.num_blocks = EndGetI16(&buf[cffold_NumBlocks]);
fol->data.next = NULL;
fol->data.cab = (struct mscabd_cabinet_p *) cab;
fol->data.offset = offset + (off_t)
( (unsigned int) EndGetI32(&buf[cffold_DataOffset]) );
fol->merge_prev = NULL;
fol->merge_next = NULL;
if (!linkfol) cab->base.folders = (struct mscabd_folder *) fol;
else linkfol->base.next = (struct mscabd_folder *) fol;
linkfol = fol;
}
for (i = 0; i < num_files; i++) {
if (sys->read(fh, &buf[0], cffile_SIZEOF) != cffile_SIZEOF) {
return MSPACK_ERR_READ;
}
if (!(file = sys->alloc(sys, sizeof(struct mscabd_file)))) {
return MSPACK_ERR_NOMEMORY;
}
file->next = NULL;
file->length = EndGetI32(&buf[cffile_UncompressedSize]);
file->attribs = EndGetI16(&buf[cffile_Attribs]);
file->offset = EndGetI32(&buf[cffile_FolderOffset]);
x = EndGetI16(&buf[cffile_FolderIndex]);
if (x < cffileCONTINUED_FROM_PREV) {
struct mscabd_folder *ifol = cab->base.folders;
while (x--) if (ifol) ifol = ifol->next;
file->folder = ifol;
if (!ifol) {
sys->free(file);
D(("invalid folder index"))
return MSPACK_ERR_DATAFORMAT;
}
}
else {
if ((x == cffileCONTINUED_TO_NEXT) ||
(x == cffileCONTINUED_PREV_AND_NEXT))
{
struct mscabd_folder *ifol = cab->base.folders;
while (ifol->next) ifol = ifol->next;
file->folder = ifol;
fol = (struct mscabd_folder_p *) ifol;
if (!fol->merge_next) fol->merge_next = file;
}
if ((x == cffileCONTINUED_FROM_PREV) ||
(x == cffileCONTINUED_PREV_AND_NEXT))
{
file->folder = cab->base.folders;
fol = (struct mscabd_folder_p *) file->folder;
if (!fol->merge_prev) fol->merge_prev = file;
}
}
x = EndGetI16(&buf[cffile_Time]);
file->time_h = x >> 11;
file->time_m = (x >> 5) & 0x3F;
file->time_s = (x << 1) & 0x3E;
x = EndGetI16(&buf[cffile_Date]);
file->date_d = x & 0x1F;
file->date_m = (x >> 5) & 0xF;
file->date_y = (x >> 9) + 1980;
file->filename = cabd_read_string(sys, fh, cab, &x);
if (x) {
sys->free(file);
return x;
}
if (!linkfile) cab->base.files = file;
else linkfile->next = file;
linkfile = file;
}
return MSPACK_ERR_OK;
}
static char *cabd_read_string(struct mspack_system *sys,
struct mspack_file *fh,
struct mscabd_cabinet_p *cab, int *error)
{
off_t base = sys->tell(fh);
char buf[256], *str;
unsigned int len, i, ok;
len = sys->read(fh, &buf[0], 256);
for (i = 0, ok = 0; i < len; i++) if (!buf[i]) { ok = 1; break; }
if (!ok) {
*error = MSPACK_ERR_DATAFORMAT;
return NULL;
}
len = i + 1;
if (sys->seek(fh, base + (off_t)len, MSPACK_SYS_SEEK_START)) {
*error = MSPACK_ERR_SEEK;
return NULL;
}
if (!(str = sys->alloc(sys, len))) {
*error = MSPACK_ERR_NOMEMORY;
return NULL;
}
sys->copy(&buf[0], str, len);
*error = MSPACK_ERR_OK;
return str;
}
static struct mscabd_cabinet *cabd_search(struct mscab_decompressor *base,
char *filename)
{
struct mscab_decompressor_p *this = (struct mscab_decompressor_p *) base;
struct mscabd_cabinet_p *cab = NULL;
struct mspack_system *sys;
unsigned char *search_buf;
struct mspack_file *fh;
unsigned int firstlen = 0;
off_t filelen;
if (!base) return NULL;
sys = this->system;
search_buf = sys->alloc(sys, (size_t) this->param[MSCABD_PARAM_SEARCHBUF]);
if (!search_buf) {
this->error = MSPACK_ERR_NOMEMORY;
return NULL;
}
if ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) {
if (!(this->error = mspack_sys_filelen(sys, fh, &filelen))) {
this->error = cabd_find(this, search_buf, fh, filename, 0,
filelen, &firstlen, &cab);
}
if (firstlen && (firstlen != filelen) &&
(!cab || (cab->base.base_offset == 0)))
{
if (firstlen < filelen) {
sys->message(fh, "WARNING; possible %u extra bytes at end of file.",
(unsigned int) (filelen - firstlen));
}
else {
sys->message(fh, "WARNING; file possibly truncated by %u bytes.",
(unsigned int) (firstlen - filelen));
}
}
sys->close(fh);
}
else {
this->error = MSPACK_ERR_OPEN;
}
sys->free(search_buf);
return (struct mscabd_cabinet *) cab;
}
static struct mscabd_cabinet *cabd_dsearch(struct mscab_decompressor *base,
int desc)
{
struct mscab_decompressor_p *this = (struct mscab_decompressor_p *) base;
struct mscabd_cabinet_p *cab = NULL;
struct mspack_system *sys;
unsigned char *search_buf;
struct mspack_file *fh;
unsigned int firstlen = 0;
off_t filelen;
char *filename = "descriptor";
if (!base) return NULL;
sys = this->system;
search_buf = sys->alloc(sys, (size_t) this->param[MSCABD_PARAM_SEARCHBUF]);
if (!search_buf) {
this->error = MSPACK_ERR_NOMEMORY;
return NULL;
}
if ((fh = sys->dopen(sys, desc, MSPACK_SYS_OPEN_READ))) {
if (!(this->error = mspack_sys_filelen(sys, fh, &filelen))) {
this->error = cabd_find(this, search_buf, fh, filename, desc,
filelen, &firstlen, &cab);
}
if (firstlen && (firstlen != filelen) &&
(!cab || (cab->base.base_offset == 0)))
{
if (firstlen < filelen) {
sys->message(fh, "WARNING; possible %u extra bytes at end of file.",
(unsigned int) (filelen - firstlen));
}
else {
sys->message(fh, "WARNING; file possibly truncated by %u bytes.",
(unsigned int) (firstlen - filelen));
}
}
sys->close(fh);
}
else {
this->error = MSPACK_ERR_OPEN;
}
sys->free(search_buf);
return (struct mscabd_cabinet *) cab;
}
static int cabd_find(struct mscab_decompressor_p *this, unsigned char *buf,
struct mspack_file *fh, char *filename, int desc, off_t flen,
unsigned int *firstlen,
struct mscabd_cabinet_p **firstcab)
{
struct mscabd_cabinet_p *cab, *link = NULL;
off_t caboff, offset, foffset=0, cablen=0;
struct mspack_system *sys = this->system;
unsigned char *p, *pend, state = 0;
int false_cabs = 0, length;
for (offset = 0; offset < flen; offset += length) {
length = flen - offset;
if (length > this->param[MSCABD_PARAM_SEARCHBUF]) {
length = this->param[MSCABD_PARAM_SEARCHBUF];
}
if (sys->read(fh, &buf[0], length) != length) {
return MSPACK_ERR_READ;
}
if ((offset == 0) && (EndGetI32(&buf[0]) == 0x28635349)) {
sys->message(fh, "WARNING; found InstallShield header. "
"This is probably an InstallShield file. "
"Use UNSHIELD (http://synce.sf.net) to unpack it.");
}
for (p = &buf[0], pend = &buf[length]; p < pend; ) {
switch (state) {
case 0:
while (p < pend && *p != 0x4D) p++;
if (p++ < pend) state = 1;
break;
case 1: state = (*p++ == 0x53) ? 2 : 0; break;
case 2: state = (*p++ == 0x43) ? 3 : 0; break;
case 3: state = (*p++ == 0x46) ? 4 : 0; break;
case 8: cablen = *p++; state++; break;
case 9: cablen |= *p++ << 8; state++; break;
case 10: cablen |= *p++ << 16; state++; break;
case 11: cablen |= *p++ << 24; state++; break;
case 16: foffset = *p++; state++; break;
case 17: foffset |= *p++ << 8; state++; break;
case 18: foffset |= *p++ << 16; state++; break;
case 19: foffset |= *p++ << 24;
caboff = offset + (p - &buf[0]) - 20;
offset = caboff + 4;
if (caboff == 0) *firstlen = cablen;
if ((foffset < cablen) &&
((caboff + foffset) < (flen + 32)) &&
((caboff + cablen) < (flen + 32)) )
{
if (!(cab = sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) {
return MSPACK_ERR_NOMEMORY;
}
cab->base.filename = filename;
cab->base.desc = dup(desc);
if (cabd_read_headers(sys, fh, cab, caboff, 1)) {
cabd_close((struct mscab_decompressor *) this,
(struct mscabd_cabinet *) cab);
false_cabs++;
}
else {
offset = caboff + cablen;
if (!link) *firstcab = cab;
else link->base.next = (struct mscabd_cabinet *) cab;
link = cab;
}
}
if (offset >= flen) return MSPACK_ERR_OK;
if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START))
return MSPACK_ERR_SEEK;
length = 0;
p = pend;
state = 0;
break;
default:
p++, state++;
}
}
}
if (false_cabs) {
D(("%d false cabinets found", false_cabs))
}
return MSPACK_ERR_OK;
}
static int cabd_prepend(struct mscab_decompressor *base,
struct mscabd_cabinet *cab,
struct mscabd_cabinet *prevcab)
{
return cabd_merge(base, prevcab, cab);
}
static int cabd_append(struct mscab_decompressor *base,
struct mscabd_cabinet *cab,
struct mscabd_cabinet *nextcab)
{
return cabd_merge(base, cab, nextcab);
}
static int cabd_merge(struct mscab_decompressor *base,
struct mscabd_cabinet *lcab,
struct mscabd_cabinet *rcab)
{
struct mscab_decompressor_p *this = (struct mscab_decompressor_p *) base;
struct mscabd_folder_data *data, *ndata;
struct mscabd_folder_p *lfol, *rfol;
struct mscabd_file *fi, *rfi, *lfi;
struct mscabd_cabinet *cab;
struct mspack_system *sys;
if (!this) return MSPACK_ERR_ARGS;
sys = this->system;
if (!lcab || !rcab || (lcab == rcab)) {
D(("lcab NULL, rcab NULL or lcab = rcab"))
return this->error = MSPACK_ERR_ARGS;
}
if (lcab->nextcab || rcab->prevcab) {
D(("cabs already joined"))
return this->error = MSPACK_ERR_ARGS;
}
for (cab = lcab->prevcab; cab; cab = cab->prevcab) {
if (cab == rcab) {D(("circular!")) return this->error = MSPACK_ERR_ARGS;}
}
for (cab = rcab->nextcab; cab; cab = cab->nextcab) {
if (cab == lcab) {D(("circular!")) return this->error = MSPACK_ERR_ARGS;}
}
if (lcab->set_id != rcab->set_id) {
sys->message(NULL, "WARNING; merged cabinets with differing Set IDs.");
}
if (lcab->set_index > rcab->set_index) {
sys->message(NULL, "WARNING; merged cabinets with odd order.");
}
lfol = (struct mscabd_folder_p *) lcab->folders;
rfol = (struct mscabd_folder_p *) rcab->folders;
while (lfol->base.next) lfol = (struct mscabd_folder_p *) lfol->base.next;
if (!lfol->merge_next && !rfol->merge_prev) {
lcab->nextcab = rcab;
rcab->prevcab = lcab;
lfol->base.next = (struct mscabd_folder *) rfol;
fi = lcab->files;
while (fi->next) fi = fi->next;
fi->next = rcab->files;
}
else {
if (!lfol->merge_next) {
D(("rcab has merge files, lcab doesn't"))
return this->error = MSPACK_ERR_DATAFORMAT;
}
if (!rfol->merge_prev) {
D(("lcab has merge files, rcab doesn't"))
return this->error = MSPACK_ERR_DATAFORMAT;
}
if (lfol->base.comp_type != rfol->base.comp_type) {
D(("compression type mismatch"))
return this->error = MSPACK_ERR_DATAFORMAT;
}
lfi = lfol->merge_next;
rfi = rfol->merge_prev;
while (lfi) {
if (!rfi || (lfi->offset != rfi->offset)) {
D(("folder merge mismatch"))
return this->error = MSPACK_ERR_DATAFORMAT;
}
lfi = lfi->next;
rfi = rfi->next;
}
if (!(data = sys->alloc(sys, sizeof(struct mscabd_folder_data)))) {
return this->error = MSPACK_ERR_NOMEMORY;
}
lcab->nextcab = rcab;
rcab->prevcab = lcab;
ndata = &lfol->data;
while (ndata->next) ndata = ndata->next;
ndata->next = data;
*data = rfol->data;
rfol->data.next = NULL;
lfol->base.num_blocks += rfol->base.num_blocks - 1;
if ((rfol->merge_next == NULL) ||
(rfol->merge_next->folder != (struct mscabd_folder *) rfol))
{
lfol->merge_next = rfol->merge_next;
}
while (lfol->base.next) lfol = (struct mscabd_folder_p *) lfol->base.next;
lfol->base.next = rfol->base.next;
sys->free(rfol);
fi = lcab->files;
while (fi->next) fi = fi->next;
fi->next = rcab->files;
lfi = NULL;
for (fi = lcab->files; fi ; fi = rfi) {
rfi = fi->next;
if (fi->folder == (struct mscabd_folder *) rfol) {
if (lfi) lfi->next = rfi; else lcab->files = rfi;
sys->free(fi->filename);
sys->free(fi);
}
else lfi = fi;
}
}
for (cab = lcab->prevcab; cab; cab = cab->prevcab) {
cab->files = lcab->files;
cab->folders = lcab->folders;
}
for (cab = lcab->nextcab; cab; cab = cab->nextcab) {
cab->files = lcab->files;
cab->folders = lcab->folders;
}
return this->error = MSPACK_ERR_OK;
}
static int cabd_extract(struct mscab_decompressor *base,
struct mscabd_file *file, char *filename)
{
struct mscab_decompressor_p *this = (struct mscab_decompressor_p *) base;
struct mscabd_folder_p *fol;
struct mspack_system *sys;
struct mspack_file *fh;
if (!this) return MSPACK_ERR_ARGS;
if (!file) return this->error = MSPACK_ERR_ARGS;
sys = this->system;
fol = (struct mscabd_folder_p *) file->folder;
if ((!fol) || (fol->merge_prev) ||
(((file->offset + file->length) / CAB_BLOCKMAX) > fol->base.num_blocks))
{
sys->message(NULL, "ERROR; file \"%s\" cannot be extracted, "
"cabinet set is incomplete.", file->filename);
return this->error = MSPACK_ERR_DATAFORMAT;
}
if (!this->d) {
this->d = sys->alloc(sys, sizeof(struct mscabd_decompress_state));
if (!this->d) return this->error = MSPACK_ERR_NOMEMORY;
this->d->folder = NULL;
this->d->data = NULL;
this->d->sys = *sys;
this->d->sys.read = &cabd_sys_read;
this->d->sys.write = &cabd_sys_write;
this->d->state = NULL;
this->d->infh = NULL;
this->d->incab = NULL;
}
if ((this->d->folder != fol) || (this->d->offset > file->offset)) {
if (!this->d->infh || (fol->data.cab != this->d->incab)) {
if (this->d->infh) sys->close(this->d->infh);
this->d->incab = fol->data.cab;
if(fol->data.cab->base.desc) {
this->d->infh = sys->dopen(sys, fol->data.cab->base.desc,
MSPACK_SYS_OPEN_READ);
} else {
this->d->infh = sys->open(sys, fol->data.cab->base.filename,
MSPACK_SYS_OPEN_READ);
}
if (!this->d->infh) return this->error = MSPACK_ERR_OPEN;
}
if (sys->seek(this->d->infh, fol->data.offset, MSPACK_SYS_SEEK_START)) {
return this->error = MSPACK_ERR_SEEK;
}
if (cabd_init_decomp(this, (unsigned int) fol->base.comp_type)) {
return this->error;
}
this->d->folder = fol;
this->d->data = &fol->data;
this->d->offset = 0;
this->d->block = 0;
this->d->i_ptr = this->d->i_end = &this->d->input[0];
}
if (!(fh = sys->open(sys, filename, MSPACK_SYS_OPEN_WRITE))) {
return this->error = MSPACK_ERR_OPEN;
}
this->error = MSPACK_ERR_OK;
if (file->length) {
off_t bytes;
int error;
this->d->outfh = NULL;
if ((bytes = file->offset - this->d->offset)) {
error = this->d->decompress(this->d->state, bytes);
if (error != MSPACK_ERR_READ) this->error = error;
}
if (!this->error) {
this->d->outfh = fh;
error = this->d->decompress(this->d->state, (off_t) file->length);
if (error != MSPACK_ERR_READ) this->error = error;
}
}
sys->close(fh);
this->d->outfh = NULL;
return this->error;
}
static int cabd_init_decomp(struct mscab_decompressor_p *this, unsigned int ct)
{
struct mspack_file *fh = (struct mspack_file *) this;
if (!this || !this->d) {
return this->error = MSPACK_ERR_ARGS;
}
cabd_free_decomp(this);
this->d->comp_type = ct;
switch (ct & cffoldCOMPTYPE_MASK) {
case cffoldCOMPTYPE_NONE:
this->d->decompress = (int (*)(void *, off_t)) &noned_decompress;
this->d->state = noned_init(&this->d->sys, fh, fh,
this->param[MSCABD_PARAM_DECOMPBUF]);
break;
case cffoldCOMPTYPE_MSZIP:
this->d->decompress = (int (*)(void *, off_t)) &mszipd_decompress;
this->d->state = mszipd_init(&this->d->sys, fh, fh,
this->param[MSCABD_PARAM_DECOMPBUF],
this->param[MSCABD_PARAM_FIXMSZIP]);
break;
case cffoldCOMPTYPE_QUANTUM:
this->d->decompress = (int (*)(void *, off_t)) &qtmd_decompress;
this->d->state = qtmd_init(&this->d->sys, fh, fh, (int) (ct >> 8) & 0x1f,
this->param[MSCABD_PARAM_DECOMPBUF]);
break;
case cffoldCOMPTYPE_LZX:
this->d->decompress = (int (*)(void *, off_t)) &lzxd_decompress;
this->d->state = lzxd_init(&this->d->sys, fh, fh, (int) (ct >> 8) & 0x1f, 0,
this->param[MSCABD_PARAM_DECOMPBUF], (off_t) 0);
break;
default:
return this->error = MSPACK_ERR_DATAFORMAT;
}
return this->error = (this->d->state) ? MSPACK_ERR_OK : MSPACK_ERR_NOMEMORY;
}
static void cabd_free_decomp(struct mscab_decompressor_p *this) {
if (!this || !this->d || !this->d->folder || !this->d->state) return;
switch (this->d->comp_type & cffoldCOMPTYPE_MASK) {
case cffoldCOMPTYPE_NONE: noned_free(this->d->state); break;
case cffoldCOMPTYPE_MSZIP: mszipd_free(this->d->state); break;
case cffoldCOMPTYPE_QUANTUM: qtmd_free(this->d->state); break;
case cffoldCOMPTYPE_LZX: lzxd_free(this->d->state); break;
}
this->d->decompress = NULL;
this->d->state = NULL;
}
static int cabd_sys_read(struct mspack_file *file, void *buffer, int bytes) {
struct mscab_decompressor_p *this = (struct mscab_decompressor_p *) file;
unsigned char *buf = (unsigned char *) buffer;
struct mspack_system *sys = this->system;
int avail, todo, outlen, ignore_cksum;
ignore_cksum = this->param[MSCABD_PARAM_FIXMSZIP] &&
((this->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_MSZIP);
todo = bytes;
while (todo > 0) {
avail = this->d->i_end - this->d->i_ptr;
if (avail) {
if (avail > todo) avail = todo;
sys->copy(this->d->i_ptr, buf, (size_t) avail);
this->d->i_ptr += avail;
buf += avail;
todo -= avail;
}
else {
if (this->d->block++ >= this->d->folder->base.num_blocks) {
this->error = MSPACK_ERR_DATAFORMAT;
break;
}
this->error = cabd_sys_read_block(sys, this->d, &outlen, ignore_cksum);
if (this->error) return -1;
if ((this->d->comp_type & cffoldCOMPTYPE_MASK)==cffoldCOMPTYPE_QUANTUM) {
*this->d->i_end++ = 0xFF;
}
if (this->d->block >= this->d->folder->base.num_blocks) {
if ((this->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_LZX) {
lzxd_set_output_length(this->d->state, (off_t)
((this->d->block-1) * CAB_BLOCKMAX + outlen));
}
}
else {
if (outlen != CAB_BLOCKMAX) {
this->system->message(this->d->infh,
"WARNING; non-maximal data block");
}
}
}
}
return bytes - todo;
}
static int cabd_sys_write(struct mspack_file *file, void *buffer, int bytes) {
struct mscab_decompressor_p *this = (struct mscab_decompressor_p *) file;
this->d->offset += bytes;
if (this->d->outfh) {
return this->system->write(this->d->outfh, buffer, bytes);
}
return bytes;
}
static int cabd_sys_read_block(struct mspack_system *sys,
struct mscabd_decompress_state *d,
int *out, int ignore_cksum)
{
unsigned char hdr[cfdata_SIZEOF];
unsigned int cksum;
int len;
d->i_ptr = d->i_end = &d->input[0];
do {
if (sys->read(d->infh, &hdr[0], cfdata_SIZEOF) != cfdata_SIZEOF) {
return MSPACK_ERR_READ;
}
if (d->data->cab->block_resv &&
sys->seek(d->infh, (off_t) d->data->cab->block_resv,
MSPACK_SYS_SEEK_CUR))
{
return MSPACK_ERR_SEEK;
}
len = EndGetI16(&hdr[cfdata_CompressedSize]);
if (((d->i_end - d->i_ptr) + len) > CAB_INPUTMAX) {
D(("block size > CAB_INPUTMAX (%d + %d)", d->i_end - d->i_ptr, len))
return MSPACK_ERR_DATAFORMAT;
}
if (EndGetI16(&hdr[cfdata_UncompressedSize]) > CAB_BLOCKMAX) {
D(("block size > CAB_BLOCKMAX"))
return MSPACK_ERR_DATAFORMAT;
}
if (sys->read(d->infh, d->i_end, len) != len) {
return MSPACK_ERR_READ;
}
if ((cksum = EndGetI32(&hdr[cfdata_CheckSum]))) {
unsigned int sum2 = cabd_checksum(d->i_end, (unsigned int) len, 0);
if (cabd_checksum(&hdr[4], 4, sum2) != cksum) {
if (!ignore_cksum) return MSPACK_ERR_CHECKSUM;
sys->message(d->infh, "WARNING; bad block checksum found");
}
}
d->i_end += len;
if ((*out = EndGetI16(&hdr[cfdata_UncompressedSize]))) {
return MSPACK_ERR_OK;
}
sys->close(d->infh);
d->infh = NULL;
if (!(d->data = d->data->next)) {
D(("ran out of splits in cabinet set"))
return MSPACK_ERR_DATAFORMAT;
}
d->incab = d->data->cab;
if (!(d->infh = sys->open(sys, d->incab->base.filename,
MSPACK_SYS_OPEN_READ)))
{
return MSPACK_ERR_OPEN;
}
if (sys->seek(d->infh, d->data->offset, MSPACK_SYS_SEEK_START)) {
return MSPACK_ERR_SEEK;
}
} while (1);
return MSPACK_ERR_OK;
}
static unsigned int cabd_checksum(unsigned char *data, unsigned int bytes,
unsigned int cksum)
{
unsigned int len, ul = 0;
for (len = bytes >> 2; len--; data += 4) {
cksum ^= ((data[0]) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24));
}
switch (bytes & 3) {
case 3: ul |= *data++ << 16;
case 2: ul |= *data++ << 8;
case 1: ul |= *data;
}
cksum ^= ul;
return cksum;
}
struct noned_state {
struct mspack_system *sys;
struct mspack_file *i;
struct mspack_file *o;
unsigned char *buf;
int bufsize;
};
static struct noned_state *noned_init(struct mspack_system *sys,
struct mspack_file *in,
struct mspack_file *out,
int bufsize)
{
struct noned_state *state = sys->alloc(sys, sizeof(struct noned_state));
unsigned char *buf = sys->alloc(sys, (size_t) bufsize);
if (state && buf) {
state->sys = sys;
state->i = in;
state->o = out;
state->buf = buf;
state->bufsize = bufsize;
}
else {
sys->free(buf);
sys->free(state);
state = NULL;
}
return state;
}
static int noned_decompress(struct noned_state *s, off_t bytes) {
int run;
while (bytes > 0) {
run = (bytes > s->bufsize) ? s->bufsize : (int) bytes;
if (s->sys->read(s->i, &s->buf[0], run) != run) return MSPACK_ERR_READ;
if (s->sys->write(s->o, &s->buf[0], run) != run) return MSPACK_ERR_WRITE;
bytes -= run;
}
return MSPACK_ERR_OK;
}
static void noned_free(struct noned_state *state) {
struct mspack_system *sys;
if (state) {
sys = state->sys;
sys->free(state->buf);
sys->free(state);
}
}
static int cabd_param(struct mscab_decompressor *base, int param, int value) {
struct mscab_decompressor_p *this = (struct mscab_decompressor_p *) base;
if (!this) return MSPACK_ERR_ARGS;
switch (param) {
case MSCABD_PARAM_SEARCHBUF:
if (value < 4) return MSPACK_ERR_ARGS;
this->param[MSCABD_PARAM_SEARCHBUF] = value;
break;
case MSCABD_PARAM_FIXMSZIP:
this->param[MSCABD_PARAM_FIXMSZIP] = value;
break;
case MSCABD_PARAM_DECOMPBUF:
if (value < 4) return MSPACK_ERR_ARGS;
this->param[MSCABD_PARAM_DECOMPBUF] = value;
break;
default:
return MSPACK_ERR_ARGS;
}
return MSPACK_ERR_OK;
}
static int cabd_error(struct mscab_decompressor *base) {
struct mscab_decompressor_p *this = (struct mscab_decompressor_p *) base;
return (this) ? this->error : MSPACK_ERR_ARGS;
}