1 /* POLE - Portable C++ library to access OLE Storage
2    Copyright (C) 2002-2005 Ariya Hidayat <ariya@kde.org>
3 
4    Redistribution and use in source and binary forms, with or without
5    modification, are permitted provided that the following conditions
6    are met:
7    * Redistributions of source code must retain the above copyright notice,
8      this list of conditions and the following disclaimer.
9    * Redistributions in binary form must reproduce the above copyright notice,
10      this list of conditions and the following disclaimer in the documentation
11      and/or other materials provided with the distribution.
12    * Neither the name of the authors nor the names of its contributors may be
13      used to endorse or promote products derived from this software without
14      specific prior written permission.
15 
16    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
26    THE POSSIBILITY OF SUCH DAMAGE.
27 */
28 
29 /*
30  This file taken from libwpg WPGOLEStream.cpp 1.5 Thu Aug 17 21:21:30 2006
31 */
32 
33 #include "WPGOLEStream.h"
34 
35 #include <sstream>
36 #include <iostream>
37 #include <list>
38 #include <string>
39 #include <vector>
40 
41 #include <string.h>
42 
43 namespace libwpg
44 {
45 
46 class Header
47 {
48   public:
49     unsigned char id[8];       // signature, or magic identifier
50     unsigned b_shift;          // bbat->blockSize = 1 << b_shift
51     unsigned s_shift;          // sbat->blockSize = 1 << s_shift
52     unsigned num_bat;          // blocks allocated for big bat
53     unsigned dirent_start;     // starting block for directory info
54     unsigned threshold;        // switch from small to big file (usually 4K)
55     unsigned sbat_start;       // starting block index to store small bat
56     unsigned num_sbat;         // blocks allocated for small bat
57     unsigned mbat_start;       // starting block to store meta bat
58     unsigned num_mbat;         // blocks allocated for meta bat
59     unsigned long bb_blocks[109];
60 
61     Header();
62     bool valid();
63     void load( const unsigned char* buffer );
64     void save( unsigned char* buffer );
65     void debug();
66 };
67 
68 class AllocTable
69 {
70   public:
71     static const unsigned Eof;
72     static const unsigned Avail;
73     static const unsigned Bat;
74     static const unsigned MetaBat;
75     unsigned blockSize;
76     AllocTable();
77     void clear();
78     unsigned long count();
79     void resize( unsigned long newsize );
80     void preserve( unsigned long n );
81     void set( unsigned long index, unsigned long val );
82     unsigned unused();
83     void setChain( std::vector<unsigned long> );
84     std::vector<unsigned long> follow( unsigned long start );
85     unsigned long operator[](unsigned long index );
86     void load( const unsigned char* buffer, unsigned len );
87     void save( unsigned char* buffer );
88   private:
89     std::vector<unsigned long> data;
90     AllocTable( const AllocTable& );
91     AllocTable& operator=( const AllocTable& );
92 };
93 
94 class DirEntry
95 {
96   public:
DirEntry()97     DirEntry() : valid(false), name(), dir(false), size(0), start(0),
98     	prev(0), next(0), child(0) {};
99     bool valid;            // false if invalid (should be skipped)
100     std::string name;      // the name, not in unicode anymore
101     bool dir;              // true if directory
102     unsigned long size;    // size (not valid if directory)
103     unsigned long start;   // starting block
104     unsigned prev;         // previous sibling
105     unsigned next;         // next sibling
106     unsigned child;        // first child
107 };
108 
109 class DirTree
110 {
111   public:
112     static const unsigned End;
113     DirTree();
114     void clear();
115     unsigned entryCount();
116     DirEntry* entry( unsigned index );
117     DirEntry* entry( const std::string& name );
118     int parent( unsigned index );
119     std::string fullName( unsigned index );
120     std::vector<unsigned> children( unsigned index );
121     void load( unsigned char* buffer, unsigned len );
122     void save( unsigned char* buffer );
123   private:
124     std::vector<DirEntry> entries;
125     DirTree( const DirTree& );
126     DirTree& operator=( const DirTree& );
127 };
128 
129 class StorageIO
130 {
131   public:
132     Storage* storage;         // owner
133     std::stringstream buf;
134     int result;               // result of operation
135     unsigned long bufsize;    // size of the buffer
136 
137     Header* header;           // storage header
138     DirTree* dirtree;         // directory tree
139     AllocTable* bbat;         // allocation table for big blocks
140     AllocTable* sbat;         // allocation table for small blocks
141 
142     std::vector<unsigned long> sb_blocks; // blocks for "small" files
143 
144     std::list<Stream*> streams;
145 
146     StorageIO( Storage* storage, const std::stringstream &memorystream );
147     ~StorageIO();
148 
149     bool isOLEStream();
150     void load();
151 
152     unsigned long loadBigBlocks( std::vector<unsigned long> blocks, unsigned char* buffer, unsigned long maxlen );
153 
154     unsigned long loadBigBlock( unsigned long block, unsigned char* buffer, unsigned long maxlen );
155 
156     unsigned long loadSmallBlocks( std::vector<unsigned long> blocks, unsigned char* buffer, unsigned long maxlen );
157 
158     unsigned long loadSmallBlock( unsigned long block, unsigned char* buffer, unsigned long maxlen );
159 
160     StreamIO* streamIO( const std::string& name );
161 
162   private:
163     // no copy or assign
164     StorageIO( const StorageIO& );
165     StorageIO& operator=( const StorageIO& );
166 
167 };
168 
169 class StreamIO
170 {
171   public:
172     StorageIO* io;
173     DirEntry* entry;
174     std::string fullName;
175     bool eof;
176     bool fail;
177 
178     StreamIO( StorageIO* io, DirEntry* entry );
179     ~StreamIO();
180     unsigned long size();
181     unsigned long tell();
182     int getch();
183     unsigned long read( unsigned char* data, unsigned long maxlen );
184     unsigned long read( unsigned long pos, unsigned char* data, unsigned long maxlen );
185 
186 
187   private:
188     std::vector<unsigned long> blocks;
189 
190     // no copy or assign
191     StreamIO( const StreamIO& );
192     StreamIO& operator=( const StreamIO& );
193 
194     // pointer for read
195     unsigned long m_pos;
196 
197     // simple cache system to speed-up getch()
198     unsigned char* cache_data;
199     unsigned long cache_size;
200     unsigned long cache_pos;
201     void updateCache();
202 };
203 
204 } // namespace libwpg
205 
readU16(const unsigned char * ptr)206 static inline unsigned long readU16( const unsigned char* ptr )
207 {
208   return ptr[0]+(ptr[1]<<8);
209 }
210 
readU32(const unsigned char * ptr)211 static inline unsigned long readU32( const unsigned char* ptr )
212 {
213   return ptr[0]+(ptr[1]<<8)+(ptr[2]<<16)+(ptr[3]<<24);
214 }
215 
216 static const unsigned char wpsole_magic[] =
217  { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 };
218 
219 
220 // =========== Header ==========
221 
Header()222 libwpg::Header::Header() :
223 	b_shift(9),
224 	s_shift(6),
225 	num_bat(0),
226 	dirent_start(0),
227 	threshold(4096),
228 	sbat_start(0),
229 	num_sbat(0),
230 	mbat_start(0),
231 	num_mbat(0)
232 {
233   for( unsigned i = 0; i < 8; i++ )
234     id[i] = wpsole_magic[i];
235   for( unsigned j=0; j<109; j++ )
236     bb_blocks[j] = libwpg::AllocTable::Avail;
237 }
238 
valid()239 bool libwpg::Header::valid()
240 {
241   if( threshold != 4096 ) return false;
242   if( num_bat == 0 ) return false;
243   if( (num_bat > 109) && (num_bat > (num_mbat * 127) + 109)) return false;
244   if( (num_bat < 109) && (num_mbat != 0) ) return false;
245   if( s_shift > b_shift ) return false;
246   if( b_shift <= 6 ) return false;
247   if( b_shift >=31 ) return false;
248 
249   return true;
250 }
251 
load(const unsigned char * buffer)252 void libwpg::Header::load( const unsigned char* buffer )
253 {
254   b_shift      = readU16( buffer + 0x1e );
255   s_shift      = readU16( buffer + 0x20 );
256   num_bat      = readU32( buffer + 0x2c );
257   dirent_start = readU32( buffer + 0x30 );
258   threshold    = readU32( buffer + 0x38 );
259   sbat_start   = readU32( buffer + 0x3c );
260   num_sbat     = readU32( buffer + 0x40 );
261   mbat_start   = readU32( buffer + 0x44 );
262   num_mbat     = readU32( buffer + 0x48 );
263 
264   for( unsigned i = 0; i < 8; i++ )
265     id[i] = buffer[i];
266   for( unsigned j=0; j<109; j++ )
267     bb_blocks[j] = readU32( buffer + 0x4C+j*4 );
268 }
269 
270 
271 
272 // =========== AllocTable ==========
273 
274 const unsigned libwpg::AllocTable::Avail = 0xffffffff;
275 const unsigned libwpg::AllocTable::Eof = 0xfffffffe;
276 const unsigned libwpg::AllocTable::Bat = 0xfffffffd;
277 const unsigned libwpg::AllocTable::MetaBat = 0xfffffffc;
278 
AllocTable()279 libwpg::AllocTable::AllocTable() :
280 	blockSize(4096),
281 	data()
282 {
283   // initial size
284   resize( 128 );
285 }
286 
count()287 unsigned long libwpg::AllocTable::count()
288 {
289   return data.size();
290 }
291 
resize(unsigned long newsize)292 void libwpg::AllocTable::resize( unsigned long newsize )
293 {
294   unsigned oldsize = data.size();
295   data.resize( newsize );
296   if( newsize > oldsize )
297     for( unsigned i = oldsize; i<newsize; i++ )
298       data[i] = Avail;
299 }
300 
301 // make sure there're still free blocks
preserve(unsigned long n)302 void libwpg::AllocTable::preserve( unsigned long n )
303 {
304   std::vector<unsigned long> pre;
305   for( unsigned i=0; i < n; i++ )
306     pre.push_back( unused() );
307 }
308 
operator [](unsigned long index)309 unsigned long libwpg::AllocTable::operator[]( unsigned long index )
310 {
311   unsigned long result;
312   result = data[index];
313   return result;
314 }
315 
set(unsigned long index,unsigned long value)316 void libwpg::AllocTable::set( unsigned long index, unsigned long value )
317 {
318   if( index >= count() ) resize( index + 1);
319   data[ index ] = value;
320 }
321 
setChain(std::vector<unsigned long> chain)322 void libwpg::AllocTable::setChain( std::vector<unsigned long> chain )
323 {
324   if( chain.size() )
325   {
326     for( unsigned i=0; i<chain.size()-1; i++ )
327       set( chain[i], chain[i+1] );
328     set( chain[ chain.size()-1 ], AllocTable::Eof );
329   }
330 }
331 
332 // TODO: optimize this with better search
already_exist(const std::vector<unsigned long> & chain,unsigned long item)333 static bool already_exist(const std::vector<unsigned long>& chain,
334 unsigned long item)
335 {
336  for(unsigned i = 0; i < chain.size(); i++)
337    if(chain[i] == item) return true;
338 
339  return false;
340 }
341 
342 // follow
follow(unsigned long start)343 std::vector<unsigned long> libwpg::AllocTable::follow( unsigned long start )
344 {
345   std::vector<unsigned long> chain;
346 
347   if( start >= count() ) return chain;
348 
349   unsigned long p = start;
350   while( p < count() )
351   {
352     if( p == (unsigned long)Eof ) break;
353     if( p == (unsigned long)Bat ) break;
354     if( p == (unsigned long)MetaBat ) break;
355     if( already_exist(chain, p) ) break;
356     chain.push_back( p );
357     if( data[p] >= count() ) break;
358     p = data[ p ];
359   }
360 
361   return chain;
362 }
363 
unused()364 unsigned libwpg::AllocTable::unused()
365 {
366   // find first available block
367   for( unsigned i = 0; i < data.size(); i++ )
368     if( data[i] == Avail )
369       return i;
370 
371   // completely full, so enlarge the table
372   unsigned block = data.size();
373   resize( data.size()+10 );
374   return block;
375 }
376 
load(const unsigned char * buffer,unsigned len)377 void libwpg::AllocTable::load( const unsigned char* buffer, unsigned len )
378 {
379   resize( len / 4 );
380   for( unsigned i = 0; i < count(); i++ )
381     set( i, readU32( buffer + i*4 ) );
382 }
383 
384 // =========== DirTree ==========
385 
386 const unsigned libwpg::DirTree::End = 0xffffffff;
387 
DirTree()388 libwpg::DirTree::DirTree() :
389 	entries()
390 {
391   clear();
392 }
393 
clear()394 void libwpg::DirTree::clear()
395 {
396   // leave only root entry
397   entries.resize( 1 );
398   entries[0].valid = true;
399   entries[0].name = "Root Entry";
400   entries[0].dir = true;
401   entries[0].size = 0;
402   entries[0].start = End;
403   entries[0].prev = End;
404   entries[0].next = End;
405   entries[0].child = End;
406 }
407 
entryCount()408 unsigned libwpg::DirTree::entryCount()
409 {
410   return entries.size();
411 }
412 
entry(unsigned index)413 libwpg::DirEntry* libwpg::DirTree::entry( unsigned index )
414 {
415   if( index >= entryCount() ) return (libwpg::DirEntry*) 0;
416   return &entries[ index ];
417 }
418 
parent(unsigned index)419 int libwpg::DirTree::parent( unsigned index )
420 {
421   // brute-force, basically we iterate for each entries, find its children
422   // and check if one of the children is 'index'
423   for( unsigned j=0; j<entryCount(); j++ )
424   {
425     std::vector<unsigned> chi = children( j );
426     for( unsigned i=0; i<chi.size();i++ )
427       if( chi[i] == index )
428         return j;
429   }
430 
431   return -1;
432 }
433 
fullName(unsigned index)434 std::string libwpg::DirTree::fullName( unsigned index )
435 {
436   // don't use root name ("Root Entry"), just give "/"
437   if( index == 0 ) return "/";
438 
439   std::string result = entry( index )->name;
440   result.insert( 0,  "/" );
441   int p = parent( index );
442   DirEntry * _entry = 0;
443   while( p > 0 )
444   {
445     _entry = entry( p );
446     if (_entry->dir && _entry->valid)
447     {
448       result.insert( 0,  _entry->name);
449       result.insert( 0,  "/" );
450     }
451     --p;
452     index = p;
453     if( index <= 0 ) break;
454   }
455   return result;
456 }
457 
458 // given a fullname (e.g "/ObjectPool/_1020961869"), find the entry
entry(const std::string & name)459 libwpg::DirEntry* libwpg::DirTree::entry( const std::string& name )
460 {
461 
462    if( !name.length() ) return (libwpg::DirEntry*)0;
463 
464    // quick check for "/" (that's root)
465    if( name == "/" ) return entry( 0 );
466 
467    // split the names, e.g  "/ObjectPool/_1020961869" will become:
468    // "ObjectPool" and "_1020961869"
469    std::list<std::string> names;
470    std::string::size_type start = 0, end = 0;
471    if( name[0] == '/' ) start++;
472    while( start < name.length() )
473    {
474      end = name.find_first_of( '/', start );
475      if( end == std::string::npos ) end = name.length();
476      names.push_back( name.substr( start, end-start ) );
477      start = end+1;
478    }
479 
480    // start from root
481    int index = 0 ;
482 
483    // trace one by one
484    std::list<std::string>::iterator it;
485 
486    for( it = names.begin(); it != names.end(); ++it )
487    {
488      // find among the children of index
489      std::vector<unsigned> chi = children( index );
490      unsigned child = 0;
491      for( unsigned i = 0; i < chi.size(); i++ )
492      {
493        libwpg::DirEntry* ce = entry( chi[i] );
494        if( ce )
495        if( ce->valid && ( ce->name.length()>1 ) )
496        if( ce->name == *it )
497              child = chi[i];
498      }
499 
500      // traverse to the child
501      if( child > 0 ) index = child;
502      else return (libwpg::DirEntry*)0;
503    }
504 
505    return entry( index );
506 }
507 
508 // helper function: recursively find siblings of index
dirtree_find_siblings(libwpg::DirTree * dirtree,std::vector<unsigned> & result,unsigned index)509 void dirtree_find_siblings( libwpg::DirTree* dirtree, std::vector<unsigned>& result,
510   unsigned index )
511 {
512   libwpg::DirEntry* e = dirtree->entry( index );
513   if( !e ) return;
514   if( !e->valid ) return;
515 
516   // prevent infinite loop
517   for( unsigned i = 0; i < result.size(); i++ )
518     if( result[i] == index ) return;
519 
520   // add myself
521   result.push_back( index );
522 
523   // visit previous sibling, don't go infinitely
524   unsigned prev = e->prev;
525   if( ( prev > 0 ) && ( prev < dirtree->entryCount() ) )
526   {
527     for( unsigned i = 0; i < result.size(); i++ )
528       if( result[i] == prev ) prev = 0;
529     if( prev ) dirtree_find_siblings( dirtree, result, prev );
530   }
531 
532   // visit next sibling, don't go infinitely
533   unsigned next = e->next;
534   if( ( next > 0 ) && ( next < dirtree->entryCount() ) )
535   {
536     for( unsigned i = 0; i < result.size(); i++ )
537       if( result[i] == next ) next = 0;
538     if( next ) dirtree_find_siblings( dirtree, result, next );
539   }
540 }
541 
children(unsigned index)542 std::vector<unsigned> libwpg::DirTree::children( unsigned index )
543 {
544   std::vector<unsigned> result;
545 
546   DirEntry* e = entry( index );
547   if( e ) if( e->valid && e->child < entryCount() )
548     dirtree_find_siblings( this, result, e->child );
549 
550   return result;
551 }
552 
load(unsigned char * buffer,unsigned size)553 void libwpg::DirTree::load( unsigned char* buffer, unsigned size )
554 {
555   entries.clear();
556 
557   for( unsigned i = 0; i < size/128; i++ )
558   {
559     unsigned p = i * 128;
560 
561     // would be < 32 if first char in the name isn't printable
562   //  unsigned prefix = 32;
563 
564     // parse name of this entry, which stored as Unicode 16-bit
565     std::string name;
566     int name_len = readU16( buffer + 0x40+p );
567     if( name_len > 64 ) name_len = 64;
568     for( int j=0; ( buffer[j+p]) && (j<name_len); j+= 2 )
569       name.append( 1, buffer[j+p] );
570 
571     // first char isn't printable ? remove it...
572     if( buffer[p] < 32 )
573     {
574    //   prefix = buffer[0];
575       name.erase( 0,1 );
576     }
577 
578     // 2 = file (aka stream), 1 = directory (aka storage), 5 = root
579     unsigned type = buffer[ 0x42 + p];
580 
581     libwpg::DirEntry e;
582     e.valid = true;
583     e.name = name;
584     e.start = readU32( buffer + 0x74+p );
585     e.size = readU32( buffer + 0x78+p );
586     e.prev = readU32( buffer + 0x44+p );
587     e.next = readU32( buffer + 0x48+p );
588     e.child = readU32( buffer + 0x4C+p );
589     e.dir = ( type!=2 );
590 
591     // sanity checks
592     if( (type != 2) && (type != 1 ) && (type != 5 ) ) e.valid = false;
593     if( name_len < 1 ) e.valid = false;
594 
595     entries.push_back( e );
596   }
597 }
598 
599 // =========== StorageIO ==========
600 
StorageIO(libwpg::Storage * st,const std::stringstream & memorystream)601 libwpg::StorageIO::StorageIO( libwpg::Storage* st, const std::stringstream &memorystream ) :
602 	storage(st),
603 	buf( memorystream.str(), std::ios::binary | std::ios::in ),
604 	result(libwpg::Storage::Ok),
605 	bufsize(0),
606 	header(new libwpg::Header()),
607 	dirtree(new libwpg::DirTree()),
608 	bbat(new libwpg::AllocTable()),
609 	sbat(new libwpg::AllocTable()),
610 	sb_blocks(),
611 	streams()
612 {
613   bbat->blockSize = 1 << header->b_shift;
614   sbat->blockSize = 1 << header->s_shift;
615 }
616 
~StorageIO()617 libwpg::StorageIO::~StorageIO()
618 {
619   delete sbat;
620   delete bbat;
621   delete dirtree;
622   delete header;
623 
624   std::list<libwpg::Stream*>::iterator it;
625   for( it = streams.begin(); it != streams.end(); ++it )
626     delete *it;
627 }
628 
isOLEStream()629 bool libwpg::StorageIO::isOLEStream()
630 {
631   load();
632   return (result == libwpg::Storage::Ok);
633 }
634 
load()635 void libwpg::StorageIO::load()
636 {
637   unsigned char* buffer = 0;
638   unsigned long buflen = 0;
639   std::vector<unsigned long> blocks;
640 
641   // find size of input file
642   buf.seekg( 0, std::ios::end );
643   bufsize = buf.tellg();
644 
645   // load header
646   buffer = new unsigned char[512];
647   buf.seekg( 0 );
648   buf.read( (char*)buffer, 512 );
649   header->load( buffer );
650   delete[] buffer;
651 
652   // check OLE magic id
653   result = libwpg::Storage::NotOLE;
654   for( unsigned i=0; i<8; i++ )
655     if( header->id[i] != wpsole_magic[i] )
656       return;
657 
658   // sanity checks
659   result = libwpg::Storage::BadOLE;
660   if( !header->valid() ) return;
661   if( header->threshold != 4096 ) return;
662 
663   // important block size
664   bbat->blockSize = 1 << header->b_shift;
665   sbat->blockSize = 1 << header->s_shift;
666 
667   // find blocks allocated to store big bat
668   // the first 109 blocks are in header, the rest in meta bat
669   blocks.clear();
670   blocks.resize( header->num_bat );
671   for( unsigned j = 0; j < 109; j++ )
672     if( j >= header->num_bat ) break;
673     else blocks[j] = header->bb_blocks[j];
674   if( (header->num_bat > 109) && (header->num_mbat > 0) )
675   {
676     unsigned char* buffer2 = new unsigned char[ bbat->blockSize ];
677     unsigned k = 109;
678     for( unsigned r = 0; r < header->num_mbat; r++ )
679     {
680       loadBigBlock( header->mbat_start+r, buffer2, bbat->blockSize );
681       for( unsigned s=0; s < bbat->blockSize; s+=4 )
682       {
683         if( k >= header->num_bat ) break;
684         else  blocks[k++] = readU32( buffer2 + s );
685       }
686      }
687     delete[] buffer2;
688   }
689 
690   // load big bat
691   buflen = blocks.size()*bbat->blockSize;
692   if( buflen > 0 )
693   {
694     buffer = new unsigned char[ buflen ];
695     loadBigBlocks( blocks, buffer, buflen );
696     bbat->load( buffer, buflen );
697     delete[] buffer;
698   }
699 
700   // load small bat
701   blocks.clear();
702   blocks = bbat->follow( header->sbat_start );
703   buflen = blocks.size()*bbat->blockSize;
704   if( buflen > 0 )
705   {
706     buffer = new unsigned char[ buflen ];
707     loadBigBlocks( blocks, buffer, buflen );
708     sbat->load( buffer, buflen );
709     delete[] buffer;
710   }
711 
712   // load directory tree
713   blocks.clear();
714   blocks = bbat->follow( header->dirent_start );
715   buflen = blocks.size()*bbat->blockSize;
716   buffer = new unsigned char[ buflen ];
717   loadBigBlocks( blocks, buffer, buflen );
718   dirtree->load( buffer, buflen );
719   unsigned sb_start = readU32( buffer + 0x74 );
720   delete[] buffer;
721 
722   // fetch block chain as data for small-files
723   sb_blocks = bbat->follow( sb_start ); // small files
724 
725   // so far so good
726   result = libwpg::Storage::Ok;
727 }
728 
streamIO(const std::string & name)729 libwpg::StreamIO* libwpg::StorageIO::streamIO( const std::string& name )
730 {
731   load();
732 
733   // sanity check
734   if( !name.length() ) return (libwpg::StreamIO*)0;
735 
736   // search in the entries
737   libwpg::DirEntry* entry = dirtree->entry( name );
738   if( !entry ) return (libwpg::StreamIO*)0;
739   if( entry->dir ) return (libwpg::StreamIO*)0;
740 
741   libwpg::StreamIO* result = new libwpg::StreamIO( this, entry );
742   result->fullName = name;
743 
744   return result;
745 }
746 
loadBigBlocks(std::vector<unsigned long> blocks,unsigned char * data,unsigned long maxlen)747 unsigned long libwpg::StorageIO::loadBigBlocks( std::vector<unsigned long> blocks,
748   unsigned char* data, unsigned long maxlen )
749 {
750   // sentinel
751   if( !data ) return 0;
752   if( blocks.size() < 1 ) return 0;
753   if( maxlen == 0 ) return 0;
754 
755   // read block one by one, seems fast enough
756   unsigned long bytes = 0;
757   for( unsigned long i=0; (i < blocks.size() ) & ( bytes<maxlen ); i++ )
758   {
759     unsigned long block = blocks[i];
760     unsigned long pos =  bbat->blockSize * ( block+1 );
761     unsigned long p = (bbat->blockSize < maxlen-bytes) ? bbat->blockSize : maxlen-bytes;
762     if( pos + p > bufsize ) p = bufsize - pos;
763     buf.seekg( pos );
764     buf.read( (char*)data + bytes, p );
765     bytes += p;
766   }
767 
768   return bytes;
769 }
770 
loadBigBlock(unsigned long block,unsigned char * data,unsigned long maxlen)771 unsigned long libwpg::StorageIO::loadBigBlock( unsigned long block,
772   unsigned char* data, unsigned long maxlen )
773 {
774   // sentinel
775   if( !data ) return 0;
776 
777   // wraps call for loadBigBlocks
778   std::vector<unsigned long> blocks;
779   blocks.resize( 1 );
780   blocks[ 0 ] = block;
781 
782   return loadBigBlocks( blocks, data, maxlen );
783 }
784 
785 // return number of bytes which has been read
loadSmallBlocks(std::vector<unsigned long> blocks,unsigned char * data,unsigned long maxlen)786 unsigned long libwpg::StorageIO::loadSmallBlocks( std::vector<unsigned long> blocks,
787   unsigned char* data, unsigned long maxlen )
788 {
789   // sentinel
790   if( !data ) return 0;
791   if( blocks.size() < 1 ) return 0;
792   if( maxlen == 0 ) return 0;
793 
794   // our own local buffer
795   unsigned char* buf = new unsigned char[ bbat->blockSize ];
796 
797   // read small block one by one
798   unsigned long bytes = 0;
799   for( unsigned long i=0; ( i<blocks.size() ) & ( bytes<maxlen ); i++ )
800   {
801     unsigned long block = blocks[i];
802 
803     // find where the small-block exactly is
804     unsigned long pos = block * sbat->blockSize;
805     unsigned long bbindex = pos / bbat->blockSize;
806     if( bbindex >= sb_blocks.size() ) break;
807 
808     loadBigBlock( sb_blocks[ bbindex ], buf, bbat->blockSize );
809 
810     // copy the data
811     unsigned offset = pos % bbat->blockSize;
812     unsigned long p = (maxlen-bytes < bbat->blockSize-offset ) ? maxlen-bytes :  bbat->blockSize-offset;
813     p = (sbat->blockSize<p ) ? sbat->blockSize : p;
814     memcpy( data + bytes, buf + offset, p );
815     bytes += p;
816   }
817 
818   delete[] buf;
819 
820   return bytes;
821 }
822 
loadSmallBlock(unsigned long block,unsigned char * data,unsigned long maxlen)823 unsigned long libwpg::StorageIO::loadSmallBlock( unsigned long block,
824   unsigned char* data, unsigned long maxlen )
825 {
826   // sentinel
827   if( !data ) return 0;
828 
829   // wraps call for loadSmallBlocks
830   std::vector<unsigned long> blocks;
831   blocks.resize( 1 );
832   blocks.assign( 1, block );
833 
834   return loadSmallBlocks( blocks, data, maxlen );
835 }
836 
837 // =========== StreamIO ==========
838 
StreamIO(libwpg::StorageIO * s,libwpg::DirEntry * e)839 libwpg::StreamIO::StreamIO( libwpg::StorageIO* s, libwpg::DirEntry* e) :
840 	io(s),
841 	entry(e),
842 	fullName(),
843 	eof(false),
844 	fail(false),
845 	blocks(),
846 	m_pos(0),
847 	cache_data(nullptr),
848 	cache_size(4096),
849 	cache_pos(0)
850 {
851   if( entry->size >= io->header->threshold )
852     blocks = io->bbat->follow( entry->start );
853   else
854     blocks = io->sbat->follow( entry->start );
855 
856   // prepare cache
857   cache_data = new unsigned char[cache_size];
858   updateCache();
859 }
860 
861 // FIXME tell parent we're gone
~StreamIO()862 libwpg::StreamIO::~StreamIO()
863 {
864   delete[] cache_data;
865 }
866 
tell()867 unsigned long libwpg::StreamIO::tell()
868 {
869   return m_pos;
870 }
871 
getch()872 int libwpg::StreamIO::getch()
873 {
874   // past end-of-file ?
875   if( m_pos > entry->size ) return -1;
876 
877   // need to update cache ?
878   if( !cache_size || ( m_pos < cache_pos ) ||
879     ( m_pos >= cache_pos + cache_size ) )
880       updateCache();
881 
882   // something bad if we don't get good cache
883   if( !cache_size ) return -1;
884 
885   int data = cache_data[m_pos - cache_pos];
886   m_pos++;
887 
888   return data;
889 }
890 
read(unsigned long pos,unsigned char * data,unsigned long maxlen)891 unsigned long libwpg::StreamIO::read( unsigned long pos, unsigned char* data, unsigned long maxlen )
892 {
893   // sanity checks
894   if( !data ) return 0;
895   if( maxlen == 0 ) return 0;
896 
897   unsigned long totalbytes = 0;
898 
899   if ( entry->size < io->header->threshold )
900   {
901     // small file
902     unsigned long index = pos / io->sbat->blockSize;
903 
904     if( index >= blocks.size() ) return 0;
905 
906     unsigned char* buf = new unsigned char[ io->sbat->blockSize ];
907     unsigned long offset = pos % io->sbat->blockSize;
908     while( totalbytes < maxlen )
909     {
910       if( index >= blocks.size() ) break;
911       io->loadSmallBlock( blocks[index], buf, io->bbat->blockSize );
912       unsigned long count = io->sbat->blockSize - offset;
913       if( count > maxlen-totalbytes ) count = maxlen-totalbytes;
914       memcpy( data+totalbytes, buf + offset, count );
915       totalbytes += count;
916       offset = 0;
917       index++;
918     }
919     delete[] buf;
920 
921   }
922   else
923   {
924     // big file
925     unsigned long index = pos / io->bbat->blockSize;
926 
927     if( index >= blocks.size() ) return 0;
928 
929     unsigned char* buf = new unsigned char[ io->bbat->blockSize ];
930     unsigned long offset = pos % io->bbat->blockSize;
931     while( totalbytes < maxlen )
932     {
933       if( index >= blocks.size() ) break;
934       io->loadBigBlock( blocks[index], buf, io->bbat->blockSize );
935       unsigned long count = io->bbat->blockSize - offset;
936       if( count > maxlen-totalbytes ) count = maxlen-totalbytes;
937       memcpy( data+totalbytes, buf + offset, count );
938       totalbytes += count;
939       index++;
940       offset = 0;
941     }
942     delete [] buf;
943 
944   }
945 
946   return totalbytes;
947 }
948 
read(unsigned char * data,unsigned long maxlen)949 unsigned long libwpg::StreamIO::read( unsigned char* data, unsigned long maxlen )
950 {
951   unsigned long bytes = read( tell(), data, maxlen );
952   m_pos += bytes;
953   return bytes;
954 }
955 
updateCache()956 void libwpg::StreamIO::updateCache()
957 {
958   // sanity check
959   if( !cache_data ) return;
960 
961   cache_pos = m_pos - ( m_pos % cache_size );
962   unsigned long bytes = cache_size;
963   if( cache_pos + bytes > entry->size ) bytes = entry->size - cache_pos;
964   cache_size = read( cache_pos, cache_data, bytes );
965 }
966 
967 
968 // =========== Storage ==========
969 
Storage(const std::stringstream & memorystream)970 libwpg::Storage::Storage( const std::stringstream &memorystream ) :
971 	io(nullptr)
972 {
973 	io = new StorageIO( this, memorystream );
974 }
975 
~Storage()976 libwpg::Storage::~Storage()
977 {
978   delete io;
979 }
980 
result()981 int libwpg::Storage::result()
982 {
983   return io->result;
984 }
985 
isOLEStream()986 bool libwpg::Storage::isOLEStream()
987 {
988   return io->isOLEStream();
989 }
990 
991 // =========== Stream ==========
992 
Stream(libwpg::Storage * storage,const std::string & name)993 libwpg::Stream::Stream( libwpg::Storage* storage, const std::string& name ) :
994 	io(storage->io->streamIO( name ))
995 {
996 }
997 
998 // FIXME tell parent we're gone
~Stream()999 libwpg::Stream::~Stream()
1000 {
1001   delete io;
1002 }
1003 
size()1004 unsigned long libwpg::Stream::size()
1005 {
1006   return io ? io->entry->size : 0;
1007 }
1008 
read(unsigned char * data,unsigned long maxlen)1009 unsigned long libwpg::Stream::read( unsigned char* data, unsigned long maxlen )
1010 {
1011   return io ? io->read( data, maxlen ) : 0;
1012 }
1013