Skip to content

Commit dcc497d

Browse files
poire-zvirxkane
authored andcommitted
EPUB: workaround ZIP files with corrupted central directories
If the main reading method (using zip header at the end of the archive) fails, we can try using the alternative method already used when this zip header is missing ("truncated"), which uses local zip headers met along while scanning the zip.
1 parent 7ad4fc9 commit dcc497d

File tree

1 file changed

+41
-1
lines changed

1 file changed

+41
-1
lines changed

crengine/src/lvstream.cpp

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2097,6 +2097,10 @@ struct ZipHd2
20972097
lUInt16 getZIPAttr() { return _Attr_and_Offset[0]; }
20982098
lUInt32 getAttr() { return _Attr_and_Offset[1] | ((lUInt32)_Attr_and_Offset[2]<<16); }
20992099
lUInt32 getOffset() { return _Attr_and_Offset[3] | ((lUInt32)_Attr_and_Offset[4]<<16); }
2100+
void setOffset(lUInt32 offset) {
2101+
_Attr_and_Offset[3] = (lUInt16)(offset & 0xFFFF);
2102+
_Attr_and_Offset[4] = (lUInt16)(offset >> 16);
2103+
}
21002104
void byteOrderConv()
21012105
{
21022106
//
@@ -2489,14 +2493,20 @@ class LVZipDecodeStream : public LVNamedStream
24892493

24902494
class LVZipArc : public LVArcContainerBase
24912495
{
2496+
protected:
2497+
// whether the alternative "truncated" method was used, or is to be used
2498+
bool m_alt_reading_method = false;
24922499
public:
2500+
bool usedAltReadingMethod() { return m_alt_reading_method; }
2501+
void useAltReadingMethod() { m_alt_reading_method = true; }
2502+
24932503
virtual LVStreamRef OpenStream( const wchar_t * fname, lvopen_mode_t /*mode*/ )
24942504
{
24952505
if ( fname[0]=='/' )
24962506
fname++;
24972507
int found_index = -1;
24982508
for (int i=0; i<m_list.length(); i++) {
2499-
if ( !lStr_cmp( fname, m_list[i]->GetName() ) ) {
2509+
if ( m_list[i]->GetName() != NULL && !lStr_cmp( fname, m_list[i]->GetName() ) ) {
25002510
if ( m_list[i]->IsContainer() ) {
25012511
// found directory with same name!!!
25022512
return LVStreamRef();
@@ -2592,6 +2602,18 @@ class LVZipArc : public LVArcContainerBase
25922602
}
25932603

25942604
truncated = !found;
2605+
2606+
// If the main reading method (using zip header at the end of the
2607+
// archive) failed, we can try using the alternative method used
2608+
// when this zip header is missing ("truncated"), which uses
2609+
// local zip headers met along while scanning the zip.
2610+
if (m_alt_reading_method)
2611+
truncated = true; // do as if truncated
2612+
else if (truncated) // archive detected as truncated
2613+
// flag that, so there's no need to try that alt method,
2614+
// as it was used on first scan
2615+
m_alt_reading_method = true;
2616+
25952617
if (truncated)
25962618
NextPosition=0;
25972619

@@ -2612,6 +2634,11 @@ class LVZipArc : public LVArcContainerBase
26122634

26132635
if (truncated)
26142636
{
2637+
// The offset (that we don't find in a local header, but
2638+
// that we will store in the ZipHeader we're building)
2639+
// happens to be the current position here.
2640+
lUInt32 offset = (lUInt32)m_stream->GetPos();
2641+
26152642
m_stream->Read( &ZipHd1, ZipHd1_size, &ReadSize);
26162643
ZipHd1.byteOrderConv();
26172644

@@ -2636,6 +2663,10 @@ class LVZipArc : public LVArcContainerBase
26362663
ZipHeader.NameLen=ZipHd1.getNameLen();
26372664
ZipHeader.AddLen=ZipHd1.getAddLen();
26382665
ZipHeader.Method=ZipHd1.getMethod();
2666+
ZipHeader.setOffset(offset);
2667+
// We may get a last invalid record with NameLen=0, which shouldn't hurt.
2668+
// If it does, use:
2669+
// if (ZipHeader.NameLen == 0) break;
26392670
} else {
26402671

26412672
m_stream->Read( &ZipHeader, ZipHeader_size, &ReadSize);
@@ -2742,8 +2773,17 @@ class LVZipArc : public LVArcContainerBase
27422773
return NULL;
27432774
LVZipArc * arc = new LVZipArc( stream );
27442775
int itemCount = arc->ReadContents();
2776+
if ( itemCount > 0 && arc->usedAltReadingMethod() ) {
2777+
printf("CRE WARNING: zip file truncated: going on with possibly partial content.\n");
2778+
}
2779+
else if ( itemCount <= 0 && !arc->usedAltReadingMethod() ) {
2780+
printf("CRE WARNING: zip file corrupted or invalid: trying alternative processing...\n");
2781+
arc->useAltReadingMethod();
2782+
itemCount = arc->ReadContents();
2783+
}
27452784
if ( itemCount <= 0 )
27462785
{
2786+
printf("CRE WARNING: zip file corrupted or invalid: processing failure.\n");
27472787
delete arc;
27482788
return NULL;
27492789
}

0 commit comments

Comments
 (0)