123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338 |
- #include "rar.hpp"
- #include "arccmt.cpp"
- Archive::Archive(RAROptions *InitCmd)
- {
- Cmd=NULL; // Just in case we'll have an exception in 'new' below.
- DummyCmd=(InitCmd==NULL);
- Cmd=DummyCmd ? (new RAROptions):InitCmd;
- OpenShared=Cmd->OpenShared;
- Format=RARFMT15;
- Solid=false;
- Volume=false;
- MainComment=false;
- Locked=false;
- Signed=false;
- FirstVolume=false;
- NewNumbering=false;
- SFXSize=0;
- LatestTime.Reset();
- Protected=false;
- Encrypted=false;
- FailedHeaderDecryption=false;
- BrokenHeader=false;
- LastReadBlock=0;
- CurBlockPos=0;
- NextBlockPos=0;
- memset(&MainHead,0,sizeof(MainHead));
- memset(&CryptHead,0,sizeof(CryptHead));
- memset(&EndArcHead,0,sizeof(EndArcHead));
- VolNumber=0;
- VolWrite=0;
- AddingFilesSize=0;
- AddingHeadersSize=0;
- *FirstVolumeName=0;
- Splitting=false;
- NewArchive=false;
- SilentOpen=false;
- #ifdef USE_QOPEN
- ProhibitQOpen=false;
- #endif
- }
- Archive::~Archive()
- {
- if (DummyCmd)
- delete Cmd;
- }
- void Archive::CheckArc(bool EnableBroken)
- {
- if (!IsArchive(EnableBroken))
- {
- // If FailedHeaderDecryption is set, we already reported that archive
- // password is incorrect.
- if (!FailedHeaderDecryption)
- uiMsg(UIERROR_BADARCHIVE,FileName);
- ErrHandler.Exit(RARX_FATAL);
- }
- }
- #if !defined(SFX_MODULE)
- void Archive::CheckOpen(const wchar *Name)
- {
- TOpen(Name);
- CheckArc(false);
- }
- #endif
- bool Archive::WCheckOpen(const wchar *Name)
- {
- if (!WOpen(Name))
- return false;
- if (!IsArchive(false))
- {
- uiMsg(UIERROR_BADARCHIVE,FileName);
- Close();
- return false;
- }
- return true;
- }
- RARFORMAT Archive::IsSignature(const byte *D,size_t Size)
- {
- RARFORMAT Type=RARFMT_NONE;
- if (Size>=1 && D[0]==0x52)
- #ifndef SFX_MODULE
- if (Size>=4 && D[1]==0x45 && D[2]==0x7e && D[3]==0x5e)
- Type=RARFMT14;
- else
- #endif
- if (Size>=7 && D[1]==0x61 && D[2]==0x72 && D[3]==0x21 && D[4]==0x1a && D[5]==0x07)
- {
- // We check the last signature byte, so we can return a sensible
- // warning in case we'll want to change the archive format
- // sometimes in the future.
- if (D[6]==0)
- Type=RARFMT15;
- else
- if (D[6]==1)
- Type=RARFMT50;
- else
- if (D[6]>1 && D[6]<5)
- Type=RARFMT_FUTURE;
- }
- return Type;
- }
- bool Archive::IsArchive(bool EnableBroken)
- {
- Encrypted=false;
- BrokenHeader=false; // Might be left from previous volume.
-
- #ifndef SFX_MODULE
- if (IsDevice())
- {
- uiMsg(UIERROR_INVALIDNAME,FileName,FileName);
- return false;
- }
- #endif
- if (Read(MarkHead.Mark,SIZEOF_MARKHEAD3)!=SIZEOF_MARKHEAD3)
- return false;
- SFXSize=0;
-
- RARFORMAT Type;
- if ((Type=IsSignature(MarkHead.Mark,SIZEOF_MARKHEAD3))!=RARFMT_NONE)
- {
- Format=Type;
- if (Format==RARFMT14)
- Seek(Tell()-SIZEOF_MARKHEAD3,SEEK_SET);
- }
- else
- {
- Array<char> Buffer(MAXSFXSIZE);
- long CurPos=(long)Tell();
- int ReadSize=Read(&Buffer[0],Buffer.Size()-16);
- for (int I=0;I<ReadSize;I++)
- if (Buffer[I]==0x52 && (Type=IsSignature((byte *)&Buffer[I],ReadSize-I))!=RARFMT_NONE)
- {
- Format=Type;
- if (Format==RARFMT14 && I>0 && CurPos<28 && ReadSize>31)
- {
- char *D=&Buffer[28-CurPos];
- if (D[0]!=0x52 || D[1]!=0x53 || D[2]!=0x46 || D[3]!=0x58)
- continue;
- }
- SFXSize=CurPos+I;
- Seek(SFXSize,SEEK_SET);
- if (Format==RARFMT15 || Format==RARFMT50)
- Read(MarkHead.Mark,SIZEOF_MARKHEAD3);
- break;
- }
- if (SFXSize==0)
- return false;
- }
- if (Format==RARFMT_FUTURE)
- {
- uiMsg(UIERROR_NEWRARFORMAT,FileName);
- return false;
- }
- if (Format==RARFMT50) // RAR 5.0 signature is by one byte longer.
- {
- if (Read(MarkHead.Mark+SIZEOF_MARKHEAD3,1)!=1 || MarkHead.Mark[SIZEOF_MARKHEAD3]!=0)
- return false;
- MarkHead.HeadSize=SIZEOF_MARKHEAD5;
- }
- else
- MarkHead.HeadSize=SIZEOF_MARKHEAD3;
- #ifdef RARDLL
- // If callback function is not set, we cannot get the password,
- // so we skip the initial header processing for encrypted header archive.
- // It leads to skipped archive comment, but the rest of archive data
- // is processed correctly.
- if (Cmd->Callback==NULL)
- SilentOpen=true;
- #endif
- bool HeadersLeft; // Any headers left to read.
- bool StartFound=false; // Main or encryption headers found.
- // Skip the archive encryption header if any and read the main header.
- while ((HeadersLeft=(ReadHeader()!=0))==true) // Additional parentheses to silence Clang.
- {
- SeekToNext();
- HEADER_TYPE Type=GetHeaderType();
- // In RAR 5.0 we need to quit after reading HEAD_CRYPT if we wish to
- // avoid the password prompt.
- StartFound=Type==HEAD_MAIN || SilentOpen && Type==HEAD_CRYPT;
- if (StartFound)
- break;
- }
-
- // We should not do it for EnableBroken or we'll get 'not RAR archive'
- // messages when extracting encrypted archives with wrong password.
- if (FailedHeaderDecryption && !EnableBroken)
- return false;
- if (BrokenHeader || !StartFound) // Main archive header is corrupt or missing.
- {
- if (!FailedHeaderDecryption) // If not reported a wrong password already.
- uiMsg(UIERROR_MHEADERBROKEN,FileName);
- if (!EnableBroken)
- return false;
- }
- MainComment=MainHead.CommentInHeader;
- // If we process non-encrypted archive or can request a password,
- // we set 'first volume' flag based on file attributes below.
- // It is necessary for RAR 2.x archives, which did not have 'first volume'
- // flag in main header. Also for all RAR formats we need to scan until
- // first file header to set "comment" flag when reading service header.
- // Unless we are in silent mode, we need to know about presence of comment
- // immediately after IsArchive call.
- if (HeadersLeft && (!SilentOpen || !Encrypted) && IsSeekable())
- {
- int64 SavePos=Tell();
- int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos;
- HEADER_TYPE SaveCurHeaderType=CurHeaderType;
- while (ReadHeader()!=0)
- {
- HEADER_TYPE HeaderType=GetHeaderType();
- if (HeaderType==HEAD_SERVICE)
- {
- // If we have a split service headers, it surely indicates non-first
- // volume. But not split service header does not guarantee the first
- // volume, because we can have split file after non-split archive
- // comment. So we do not quit from loop here.
- FirstVolume=Volume && !SubHead.SplitBefore;
- }
- else
- if (HeaderType==HEAD_FILE)
- {
- FirstVolume=Volume && !FileHead.SplitBefore;
- break;
- }
- else
- if (HeaderType==HEAD_ENDARC) // Might happen if archive contains only a split service header.
- break;
- SeekToNext();
- }
- CurBlockPos=SaveCurBlockPos;
- NextBlockPos=SaveNextBlockPos;
- CurHeaderType=SaveCurHeaderType;
- Seek(SavePos,SEEK_SET);
- }
- if (!Volume || FirstVolume)
- wcsncpyz(FirstVolumeName,FileName,ASIZE(FirstVolumeName));
- return true;
- }
- void Archive::SeekToNext()
- {
- Seek(NextBlockPos,SEEK_SET);
- }
- // Calculate the block size including encryption fields and padding if any.
- uint Archive::FullHeaderSize(size_t Size)
- {
- if (Encrypted)
- {
- Size = ALIGN_VALUE(Size, CRYPT_BLOCK_SIZE); // Align to encryption block size.
- if (Format == RARFMT50)
- Size += SIZE_INITV;
- else
- Size += SIZE_SALT30;
- }
- return uint(Size);
- }
- #ifdef USE_QOPEN
- bool Archive::Open(const wchar *Name,uint Mode)
- {
- // Important if we reuse Archive object and it has virtual QOpen
- // file position not matching real. For example, for 'l -v volname'.
- QOpen.Unload();
- return File::Open(Name,Mode);
- }
- int Archive::Read(void *Data,size_t Size)
- {
- size_t Result;
- if (QOpen.Read(Data,Size,Result))
- return (int)Result;
- return File::Read(Data,Size);
- }
- void Archive::Seek(int64 Offset,int Method)
- {
- if (!QOpen.Seek(Offset,Method))
- File::Seek(Offset,Method);
- }
- int64 Archive::Tell()
- {
- int64 QPos;
- if (QOpen.Tell(&QPos))
- return QPos;
- return File::Tell();
- }
- #endif
|