/** * @file ole.c * @author Alex Ott, Victor B Wagner * @date Wed Jun 11 12:33:01 2003 * Version: $Id: ole.c,v 1.2 2006/02/25 15:28:14 vitus Exp $ * Copyright: Victor B Wagner, 1996-2003 Alex Ott, 2003 * * @brief Parsing structure of MS Office compound document * * This file is part of catdoc project * and distributed under GNU Public License * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "catdoc.h" #define min(a,b) ((a) < (b) ? (a) : (b)) const static unsigned char ole_sign[]={0xD0,0xCF,0x11,0xE0,0xA1,0xB1,0x1A,0xE1,0}; /** * Initializes ole structure * * @param f (FILE *) compound document file, positioned at bufSize * byte. Might be pipe or socket * @param buffer (void *) bytes already read from f * @param bufSize number of bytes already read from f should be less * than 512 * * @return */ FILE* ole_init(FILE *f, void *buffer, size_t bufSize, struct ole_params_t *ole_params) { unsigned char oleBuf[BBD_BLOCK_SIZE]; unsigned char *tmpBuf; FILE *newfile; int ret=0, i; long int sbdMaxLen, sbdCurrent, propMaxLen, propCurrent, mblock, msat_size; oleEntry *tEntry; long int sectorSize; long int shortSectorSize; long int bbdNumBlocks; /* deleting old data (if it was allocated) */ ole_finish(ole_params); if (fseek(f,0,SEEK_SET) == -1) { if (errno == ESPIPE) { /* We got non-seekable file, create temp file */ if ((newfile=tmpfile()) == NULL) { return NULL; } if (bufSize > 0) { ret=fwrite(buffer, 1, bufSize, newfile); if (ret != bufSize) { return NULL; } } while (!feof(f)) { ret=fread(oleBuf,1,BBD_BLOCK_SIZE,f); fwrite(oleBuf, 1, ret, newfile); } fseek(newfile,0,SEEK_SET); } else { return NULL; } } else { newfile=f; } fseek(newfile,0,SEEK_END); ole_params->fileLength=ftell(newfile); fseek(newfile,0,SEEK_SET); ret=fread(oleBuf,1,BBD_BLOCK_SIZE,newfile); if (ret != BBD_BLOCK_SIZE) { return NULL; } if (strncmp(oleBuf,ole_sign,8) != 0) { return NULL; } ole_params->sectorSize = 1<sectorSize == 0) { return NULL; } sectorSize = ole_params->sectorSize; ole_params->shortSectorSize = 1<shortSectorSize; if (shortSectorSize > sectorSize) { return NULL; } /* Read BBD into memory */ ole_params->bbdNumBlocks = getulong(oleBuf,0x2c); bbdNumBlocks = ole_params->bbdNumBlocks; if ((ole_params->BBD=malloc(bbdNumBlocks*sectorSize)) == NULL) { return NULL; } if ((tmpBuf=malloc(MSAT_ORIG_SIZE)) == NULL) { return NULL; } memcpy(tmpBuf,oleBuf+0x4c,MSAT_ORIG_SIZE); mblock=getlong(oleBuf,0x44); msat_size=getlong(oleBuf,0x48); /* fprintf(stderr, "msat_size=%ld\n", msat_size); */ i=0; while ((mblock >= 0) && (i < msat_size)) { unsigned char *newbuf; /* fprintf(stderr, "i=%d mblock=%ld\n", i, mblock); */ if ((newbuf=realloc(tmpBuf, sectorSize*(i+1)+MSAT_ORIG_SIZE)) != NULL) { tmpBuf=newbuf; } else { free(tmpBuf); ole_finish(ole_params); return NULL; } fseek(newfile, 512+mblock*sectorSize, SEEK_SET); if (fread(tmpBuf+MSAT_ORIG_SIZE+(sectorSize-4)*i, 1, sectorSize, newfile) != sectorSize) { ole_finish(ole_params); return NULL; } i++; mblock=getlong(tmpBuf, MSAT_ORIG_SIZE+(sectorSize-4)*i); } /* fprintf(stderr, "bbdNumBlocks=%ld\n", bbdNumBlocks); */ for (i=0; i< bbdNumBlocks; i++) { long int bbdSector=getlong(tmpBuf,4*i); if (bbdSector >= ole_params->fileLength/sectorSize || bbdSector < 0) { errno = EINVAL; ole_finish(ole_params); return NULL; } fseek(newfile, 512+bbdSector*sectorSize, SEEK_SET); if (fread(ole_params->BBD+i*sectorSize, 1, sectorSize, newfile) != sectorSize) { free(tmpBuf); ole_finish(ole_params); return NULL; } } free(tmpBuf); /* Read SBD into memory */ ole_params->sbdLen=0; sbdMaxLen=10; sbdCurrent = ole_params->sbdStart = getlong(oleBuf,0x3c); if (ole_params->sbdStart > 0) { if ((ole_params->SBD=malloc(sectorSize*sbdMaxLen)) == NULL) { ole_finish(ole_params); return NULL; } while (1) { fseek(newfile, 512+sbdCurrent*sectorSize, SEEK_SET); fread(ole_params->SBD+ole_params->sbdLen*sectorSize, 1, sectorSize, newfile); ole_params->sbdLen++; if (ole_params->sbdLen >= sbdMaxLen) { unsigned char *newSBD; sbdMaxLen+=5; if ((newSBD=realloc(ole_params->SBD, sectorSize*sbdMaxLen)) != NULL) { ole_params->SBD=newSBD; } else { ole_finish(ole_params); return NULL; } } if (sbdCurrent < 0 || sbdCurrent * 4 >= bbdNumBlocks * sectorSize) { break; } sbdCurrent = getlong(ole_params->BBD, sbdCurrent*4); if (sbdCurrent < 0 || sbdCurrent >= ole_params->fileLength/sectorSize) break; } ole_params->sbdNumber = (ole_params->sbdLen*sectorSize)/shortSectorSize; } else { ole_params->SBD=NULL; } /* Read property catalog into memory */ ole_params->propLen = 0; propMaxLen = 5; propCurrent = ole_params->propStart = getlong(oleBuf,0x30); if (ole_params->propStart >= 0) { if ((ole_params->properties=malloc(propMaxLen*sectorSize)) == NULL) { ole_finish(ole_params); return NULL; } while (1) { /* fprintf(stderr, "propCurrent=%ld\n",propCurrent); */ fseek(newfile, 512+propCurrent*sectorSize, SEEK_SET); fread(ole_params->properties+ole_params->propLen*sectorSize, 1, sectorSize, newfile); (ole_params->propLen)++; if (ole_params->propLen >= propMaxLen) { unsigned char *newProp; propMaxLen+=5; if ((newProp=realloc(ole_params->properties, propMaxLen*sectorSize)) != NULL) ole_params->properties=newProp; else { ole_finish(ole_params); return NULL; } } propCurrent = getlong(ole_params->BBD, propCurrent*4); if (propCurrent < 0 || propCurrent >= ole_params->fileLength/sectorSize) { break; } } ole_params->propNumber = (ole_params->propLen*sectorSize)/PROP_BLOCK_SIZE; ole_params->propCurNumber = 0; } else { ole_finish(ole_params); ole_params->properties = NULL; return NULL; } /* Find Root Entry */ while ((tEntry=(oleEntry*)ole_readdir(newfile, ole_params)) != NULL) { if (tEntry->type == oleRootDir) { ole_params->rootEntry=tEntry; break; } ole_close((FILE*)tEntry); } ole_params->propCurNumber = 0; fseek(newfile, 0, SEEK_SET); if (!ole_params->rootEntry) { errno = EINVAL; ole_finish(ole_params); return NULL; } return newfile; } /** * * * @param oleBuf * * @return */ int rightOleType(unsigned char *oleBuf) { return (oleBuf[0x42] == 1 || oleBuf[0x42] == 2 || oleBuf[0x42] == 3 || oleBuf[0x42] == 5); } /** * * * @param oleBuf * * @return */ oleType getOleType(unsigned char *oleBuf) { return (oleType)((unsigned char)oleBuf[0x42]); } /** * Reads next directory entry from file * * @param name buffer for name converted to us-ascii should be at least 33 chars long * @param size size of file * * @return 0 if everything is ok -1 on error */ FILE *ole_readdir(FILE *f, struct ole_params_t *ole_params) { int i, nLen; unsigned char *oleBuf; oleEntry *e=NULL; long int chainMaxLen, chainCurrent; if (ole_params->properties == NULL || ole_params->propCurNumber >= ole_params->propNumber || f == NULL) return NULL; oleBuf=ole_params->properties + ole_params->propCurNumber*PROP_BLOCK_SIZE; if (!rightOleType(oleBuf)) return NULL; if ((e = (oleEntry*)malloc(sizeof(oleEntry))) == NULL) { return NULL; } e->dirPos=oleBuf; e->type=getOleType(oleBuf); e->file=f; e->startBlock=getlong(oleBuf,0x74); e->blocks=NULL; nLen=getshort(oleBuf,0x40); for (i=0; i < nLen/2 && i < OLENAMELENGHT; i++) e->name[i]=(char)oleBuf[i*2]; e->name[i]='\0'; (ole_params->propCurNumber)++; e->length=getulong(oleBuf,0x78); /* Read sector chain for object */ chainMaxLen = 25; e->numOfBlocks = 0; chainCurrent = e->startBlock; e->isBigBlock = (e->length >= 0x1000) || !strcmp(e->name, "Root Entry"); /* fprintf(stderr, "e->name=%s e->length=%ld\n", e->name, e->length); */ /* fprintf(stderr, "e->startBlock=%ld BBD=%p\n", e->startBlock, BBD); */ if (e->startBlock >= 0 && e->length >= 0 && (e->startBlock <= ole_params->fileLength/(e->isBigBlock ? ole_params->sectorSize : ole_params->shortSectorSize))) { if ((e->blocks=malloc(chainMaxLen*sizeof(long int))) == NULL) { return NULL; } while (1) { /* fprintf(stderr, "chainCurrent=%ld\n", chainCurrent); */ e->blocks[e->numOfBlocks++] = chainCurrent; if (e->numOfBlocks >= chainMaxLen) { long int *newChain; chainMaxLen+=25; if ((newChain=realloc(e->blocks, chainMaxLen*sizeof(long int))) != NULL) e->blocks=newChain; else { free(e->blocks); e->blocks=NULL; return NULL; } } if (e->isBigBlock) { chainCurrent = getlong(ole_params->BBD, chainCurrent*4); } else if (ole_params->SBD != NULL) { chainCurrent = getlong(ole_params->SBD, chainCurrent*4); } else { chainCurrent=-1; } if (chainCurrent <= 0 || chainCurrent >= (e->isBigBlock ? ((ole_params->bbdNumBlocks*ole_params->sectorSize)/4) : ((ole_params->sbdNumber*ole_params->shortSectorSize)/4)) || (e->numOfBlocks > e->length/(e->isBigBlock ? ole_params->sectorSize : ole_params->shortSectorSize))) { /* fprintf(stderr, "chain End=%ld\n", chainCurrent); */ break; } } } if (e->length > (e->isBigBlock ? ole_params->sectorSize : ole_params->shortSectorSize)*e->numOfBlocks) e->length = (e->isBigBlock ? ole_params->sectorSize : ole_params->shortSectorSize)*e->numOfBlocks; /* fprintf(stderr, "READDIR: e->name=%s e->numOfBlocks=%ld length=%ld\n", */ /* e->name, e->numOfBlocks, e->length); */ return (FILE*)e; } /** * Open stream, which correspond to directory entry last read by * ole_readdir * * * @return opaque pointer to pass to ole_read, casted to (FILE *) */ int ole_open(FILE *stream) { oleEntry *e=(oleEntry *)stream; if (e->type != oleStream) return -2; e->ole_offset=0; e->file_offset= ftell(e->file); return 0; } /** * * * @param e * @param blk * * @return */ long int calcFileBlockOffset(oleEntry *e, long int blk, struct ole_params_t *ole_params) { long int res; if (e->isBigBlock) { res=512+e->blocks[blk]*ole_params->sectorSize; } else { long int sbdPerSector=(ole_params->sectorSize)/(ole_params->shortSectorSize); long int sbdSecNum=e->blocks[blk]/sbdPerSector; long int sbdSecMod=e->blocks[blk]%sbdPerSector; res=512 + ole_params->rootEntry->blocks[sbdSecNum]*ole_params->sectorSize + sbdSecMod*ole_params->shortSectorSize; } return res; } /** * Reads block from open ole stream interface-compatible with fread * * @param ptr pointer to buffer for read to * @param size size of block * @param nmemb size in blocks * @param stream pointer to FILE* structure * * @return number of readed blocks */ size_t ole_read(void *ptr, size_t size, size_t nmemb, FILE *stream, struct ole_params_t *ole_params) { oleEntry *e = (oleEntry*)stream; long int llen = size*nmemb, rread=0, i; long int blockNumber, modBlock, toReadBlocks, toReadBytes, bytesInBlock; long int ssize; /**< Size of block */ long int newoffset; unsigned char *cptr = ptr; if (e->ole_offset+llen > e->length) llen= e->length - e->ole_offset; ssize = (e->isBigBlock ? ole_params->sectorSize : ole_params->shortSectorSize); blockNumber=e->ole_offset/ssize; /* fprintf(stderr, "blockNumber=%ld e->numOfBlocks=%ld llen=%ld\n", */ /* blockNumber, e->numOfBlocks, llen); */ if (blockNumber >= e->numOfBlocks || llen <=0) return 0; modBlock=e->ole_offset%ssize; bytesInBlock = ssize - modBlock; if (bytesInBlock < llen) { toReadBlocks = (llen-bytesInBlock)/ssize; toReadBytes = (llen-bytesInBlock)%ssize; } else { toReadBlocks = toReadBytes = 0; } /* fprintf(stderr, "llen=%ld toReadBlocks=%ld toReadBytes=%ld bytesInBlock=%ld blockNumber=%ld modBlock=%ld\n", */ /* llen, toReadBlocks, toReadBytes, bytesInBlock, blockNumber, modBlock); */ newoffset = calcFileBlockOffset(e,blockNumber, ole_params)+modBlock; if (e->file_offset != newoffset) { fseek(e->file, e->file_offset=newoffset, SEEK_SET); } rread=fread(ptr, 1, min(llen,bytesInBlock), e->file); e->file_offset += rread; for (i=0; ifile_offset); fseek(e->file, e->file_offset=newoffset, SEEK_SET); readbytes=fread(cptr+rread, 1, min(llen-rread, ssize), e->file); rread +=readbytes; e->file_offset +=readbytes; } if (toReadBytes > 0) { int readbytes; blockNumber++; newoffset = calcFileBlockOffset(e,blockNumber, ole_params); fseek(e->file, e->file_offset=newoffset, SEEK_SET); readbytes=fread(cptr+rread, 1, toReadBytes,e->file); rread +=readbytes; e->file_offset +=readbytes; } /* fprintf(stderr, "ole_offset=%ld rread=%ld llen=%ld\n", e->ole_offset, rread, llen);*/ e->ole_offset+=rread; return rread; } /** * * * @param stream * * @return */ int ole_eof(FILE *stream) { oleEntry *e=(oleEntry*)stream; /* fprintf(stderr, "EOF: e->ole_offset=%ld e->length=%ld\n", e->ole_offset, e->length);*/ return (e->ole_offset >= e->length); } /** * * */ void ole_finish(struct ole_params_t *ole_params) { if (ole_params->BBD != NULL) free(ole_params->BBD); if (ole_params->SBD != NULL) free(ole_params->SBD); if (ole_params->properties != NULL) free(ole_params->properties); if (ole_params->rootEntry != NULL) ole_close((FILE*)(ole_params->rootEntry)); ole_params->properties = ole_params->SBD = ole_params->BBD = NULL; ole_params->rootEntry = NULL; } /** * * * @param stream * * @return */ int ole_close(FILE *stream) { oleEntry *e=(oleEntry*)stream; if (e == NULL) return -1; if (e->blocks != NULL) free(e->blocks); free(e); return 0; } /** * * * @param stream pointer to OLE stream structure * @param offset * @param whence * * @return */ int ole_seek(FILE *stream, long offset, int whence, struct ole_params_t *ole_params) { oleEntry *e=(oleEntry*)stream; long int new_ole_offset=0, new_file_offset; int ssize, modBlock, blockNumber; switch (whence) { case SEEK_SET: new_ole_offset=offset; break; case SEEK_CUR: new_ole_offset=e->ole_offset+offset; break; case SEEK_END: new_ole_offset=e->length+offset; break; default: errno=EINVAL; return -1; } if (new_ole_offset<0) new_ole_offset=0; if (new_ole_offset >= e->length) new_ole_offset=e->length; ssize = (e->isBigBlock ? ole_params->sectorSize : ole_params->shortSectorSize); blockNumber=new_ole_offset/ssize; if (blockNumber >= e->numOfBlocks) return -1; modBlock=new_ole_offset%ssize; new_file_offset = calcFileBlockOffset(e,blockNumber, ole_params)+modBlock; fseek(e->file, e->file_offset=new_file_offset, SEEK_SET); e->ole_offset=new_ole_offset; return 0; } /** * Tell position inside OLE stream * * @param stream pointer to OLE stream * * @return current position inside OLE stream */ long ole_tell(FILE *stream) { oleEntry *e=(oleEntry*)stream; return e->ole_offset; } void set_ole_func(struct io_funcs_t *io_funcs) { io_funcs->catdoc_read=ole_read; io_funcs->catdoc_eof=ole_eof; io_funcs->catdoc_seek=ole_seek; io_funcs->catdoc_tell=ole_tell; } size_t my_fread(void *ptr, size_t size, size_t nmemb, FILE *stream, struct ole_params_t *ole_params) { return fread(ptr, size, nmemb, stream); } int my_fseek(FILE *stream, long offset, int whence, struct ole_params_t *ole_params) { return fseek(stream, offset, whence); } void set_std_func(struct io_funcs_t *io_funcs) { io_funcs->catdoc_read=my_fread; io_funcs->catdoc_eof=feof; io_funcs->catdoc_seek=my_fseek; io_funcs->catdoc_tell=ftell; }