From 67f689c7fbfbaabab6a59493878ffe3a1e031ea9 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 24 Nov 2012 20:46:13 +0530 Subject: [PATCH] Initial import of unrar source code --- src/unrar/UnRAR.vcproj | 623 ++++++++++ src/unrar/UnRARDll.vcproj | 848 +++++++++++++ src/unrar/acknow.txt | 81 ++ src/unrar/arccmt.cpp | 235 ++++ src/unrar/archive.cpp | 284 +++++ src/unrar/archive.hpp | 121 ++ src/unrar/arcread.cpp | 765 ++++++++++++ src/unrar/array.hpp | 131 ++ src/unrar/beosea.cpp | 113 ++ src/unrar/cmddata.cpp | 1431 ++++++++++++++++++++++ src/unrar/cmddata.hpp | 64 + src/unrar/coder.cpp | 48 + src/unrar/coder.hpp | 25 + src/unrar/compress.hpp | 40 + src/unrar/consio.cpp | 319 +++++ src/unrar/consio.hpp | 37 + src/unrar/crc.cpp | 91 ++ src/unrar/crc.hpp | 8 + src/unrar/crypt.cpp | 387 ++++++ src/unrar/crypt.hpp | 62 + src/unrar/dll.cpp | 375 ++++++ src/unrar/dll.def | 12 + src/unrar/dll.hpp | 147 +++ src/unrar/dll.rc | 28 + src/unrar/encname.cpp | 57 + src/unrar/encname.hpp | 20 + src/unrar/errhnd.cpp | 405 +++++++ src/unrar/errhnd.hpp | 67 ++ src/unrar/extinfo.cpp | 76 ++ src/unrar/extinfo.hpp | 8 + src/unrar/extract.cpp | 1026 ++++++++++++++++ src/unrar/extract.hpp | 49 + src/unrar/filcreat.cpp | 260 ++++ src/unrar/filcreat.hpp | 13 + src/unrar/file.cpp | 710 +++++++++++ src/unrar/file.hpp | 120 ++ src/unrar/filefn.cpp | 564 +++++++++ src/unrar/filefn.hpp | 48 + src/unrar/filestr.cpp | 208 ++++ src/unrar/filestr.hpp | 16 + src/unrar/find.cpp | 291 +++++ src/unrar/find.hpp | 53 + src/unrar/getbits.cpp | 35 + src/unrar/getbits.hpp | 51 + src/unrar/global.cpp | 4 + src/unrar/global.hpp | 15 + src/unrar/headers.hpp | 327 +++++ src/unrar/isnt.cpp | 24 + src/unrar/isnt.hpp | 11 + src/unrar/license.txt | 42 + src/unrar/list.cpp | 391 ++++++ src/unrar/list.hpp | 6 + src/unrar/loclang.hpp | 357 ++++++ src/unrar/log.cpp | 24 + src/unrar/log.hpp | 18 + src/unrar/makefile.bcc | 493 ++++++++ src/unrar/makefile.dj | 50 + src/unrar/makefile.dmc | 54 + src/unrar/makefile.unix | 146 +++ src/unrar/match.cpp | 300 +++++ src/unrar/match.hpp | 35 + src/unrar/model.cpp | 610 ++++++++++ src/unrar/model.hpp | 132 ++ src/unrar/msc.dep | 2390 +++++++++++++++++++++++++++++++++++++ src/unrar/options.cpp | 30 + src/unrar/options.hpp | 175 +++ src/unrar/os.hpp | 254 ++++ src/unrar/os2ea.cpp | 94 ++ src/unrar/pathfn.cpp | 1457 ++++++++++++++++++++++ src/unrar/pathfn.hpp | 66 + src/unrar/rar.cpp | 144 +++ src/unrar/rar.hpp | 79 ++ src/unrar/rardefs.hpp | 28 + src/unrar/rarlang.hpp | 10 + src/unrar/raros.hpp | 46 + src/unrar/rarpch.cpp | 2 + src/unrar/rartypes.hpp | 47 + src/unrar/rarvm.cpp | 1139 ++++++++++++++++++ src/unrar/rarvm.hpp | 113 ++ src/unrar/rarvmtbl.cpp | 53 + src/unrar/rawread.cpp | 126 ++ src/unrar/rawread.hpp | 32 + src/unrar/rdwrfn.cpp | 295 +++++ src/unrar/rdwrfn.hpp | 88 ++ src/unrar/readme.txt | 66 + src/unrar/recvol.cpp | 572 +++++++++ src/unrar/recvol.hpp | 20 + src/unrar/resource.cpp | 27 + src/unrar/resource.hpp | 13 + src/unrar/rijndael.cpp | 298 +++++ src/unrar/rijndael.hpp | 37 + src/unrar/rs.cpp | 172 +++ src/unrar/rs.hpp | 32 + src/unrar/savepos.cpp | 15 + src/unrar/savepos.hpp | 15 + src/unrar/scantree.cpp | 413 +++++++ src/unrar/scantree.hpp | 60 + src/unrar/secpassword.cpp | 203 ++++ src/unrar/secpassword.hpp | 30 + src/unrar/sha1.cpp | 248 ++++ src/unrar/sha1.hpp | 19 + src/unrar/smallfn.cpp | 42 + src/unrar/smallfn.hpp | 8 + src/unrar/strfn.cpp | 413 +++++++ src/unrar/strfn.hpp | 53 + src/unrar/strlist.cpp | 204 ++++ src/unrar/strlist.hpp | 37 + src/unrar/suballoc.cpp | 294 +++++ src/unrar/suballoc.hpp | 87 ++ src/unrar/system.cpp | 102 ++ src/unrar/system.hpp | 28 + src/unrar/timefn.cpp | 336 ++++++ src/unrar/timefn.hpp | 54 + src/unrar/ulinks.cpp | 35 + src/unrar/ulinks.hpp | 9 + src/unrar/unicode.cpp | 420 +++++++ src/unrar/unicode.hpp | 90 ++ src/unrar/unios2.cpp | 128 ++ src/unrar/unpack.cpp | 1187 ++++++++++++++++++ src/unrar/unpack.hpp | 222 ++++ src/unrar/unpack15.cpp | 506 ++++++++ src/unrar/unpack20.cpp | 370 ++++++ src/unrar/uowners.cpp | 84 ++ src/unrar/version.hpp | 6 + src/unrar/volume.cpp | 280 +++++ src/unrar/volume.hpp | 11 + src/unrar/win32acl.cpp | 127 ++ src/unrar/win32stm.cpp | 154 +++ 128 files changed, 27586 insertions(+) create mode 100644 src/unrar/UnRAR.vcproj create mode 100644 src/unrar/UnRARDll.vcproj create mode 100644 src/unrar/acknow.txt create mode 100644 src/unrar/arccmt.cpp create mode 100644 src/unrar/archive.cpp create mode 100644 src/unrar/archive.hpp create mode 100644 src/unrar/arcread.cpp create mode 100644 src/unrar/array.hpp create mode 100644 src/unrar/beosea.cpp create mode 100644 src/unrar/cmddata.cpp create mode 100644 src/unrar/cmddata.hpp create mode 100644 src/unrar/coder.cpp create mode 100644 src/unrar/coder.hpp create mode 100644 src/unrar/compress.hpp create mode 100644 src/unrar/consio.cpp create mode 100644 src/unrar/consio.hpp create mode 100644 src/unrar/crc.cpp create mode 100644 src/unrar/crc.hpp create mode 100644 src/unrar/crypt.cpp create mode 100644 src/unrar/crypt.hpp create mode 100644 src/unrar/dll.cpp create mode 100644 src/unrar/dll.def create mode 100644 src/unrar/dll.hpp create mode 100644 src/unrar/dll.rc create mode 100644 src/unrar/encname.cpp create mode 100644 src/unrar/encname.hpp create mode 100644 src/unrar/errhnd.cpp create mode 100644 src/unrar/errhnd.hpp create mode 100644 src/unrar/extinfo.cpp create mode 100644 src/unrar/extinfo.hpp create mode 100644 src/unrar/extract.cpp create mode 100644 src/unrar/extract.hpp create mode 100644 src/unrar/filcreat.cpp create mode 100644 src/unrar/filcreat.hpp create mode 100644 src/unrar/file.cpp create mode 100644 src/unrar/file.hpp create mode 100644 src/unrar/filefn.cpp create mode 100644 src/unrar/filefn.hpp create mode 100644 src/unrar/filestr.cpp create mode 100644 src/unrar/filestr.hpp create mode 100644 src/unrar/find.cpp create mode 100644 src/unrar/find.hpp create mode 100644 src/unrar/getbits.cpp create mode 100644 src/unrar/getbits.hpp create mode 100644 src/unrar/global.cpp create mode 100644 src/unrar/global.hpp create mode 100644 src/unrar/headers.hpp create mode 100644 src/unrar/isnt.cpp create mode 100644 src/unrar/isnt.hpp create mode 100644 src/unrar/license.txt create mode 100644 src/unrar/list.cpp create mode 100644 src/unrar/list.hpp create mode 100644 src/unrar/loclang.hpp create mode 100644 src/unrar/log.cpp create mode 100644 src/unrar/log.hpp create mode 100644 src/unrar/makefile.bcc create mode 100644 src/unrar/makefile.dj create mode 100644 src/unrar/makefile.dmc create mode 100644 src/unrar/makefile.unix create mode 100644 src/unrar/match.cpp create mode 100644 src/unrar/match.hpp create mode 100644 src/unrar/model.cpp create mode 100644 src/unrar/model.hpp create mode 100644 src/unrar/msc.dep create mode 100644 src/unrar/options.cpp create mode 100644 src/unrar/options.hpp create mode 100644 src/unrar/os.hpp create mode 100644 src/unrar/os2ea.cpp create mode 100644 src/unrar/pathfn.cpp create mode 100644 src/unrar/pathfn.hpp create mode 100644 src/unrar/rar.cpp create mode 100644 src/unrar/rar.hpp create mode 100644 src/unrar/rardefs.hpp create mode 100644 src/unrar/rarlang.hpp create mode 100644 src/unrar/raros.hpp create mode 100644 src/unrar/rarpch.cpp create mode 100644 src/unrar/rartypes.hpp create mode 100644 src/unrar/rarvm.cpp create mode 100644 src/unrar/rarvm.hpp create mode 100644 src/unrar/rarvmtbl.cpp create mode 100644 src/unrar/rawread.cpp create mode 100644 src/unrar/rawread.hpp create mode 100644 src/unrar/rdwrfn.cpp create mode 100644 src/unrar/rdwrfn.hpp create mode 100644 src/unrar/readme.txt create mode 100644 src/unrar/recvol.cpp create mode 100644 src/unrar/recvol.hpp create mode 100644 src/unrar/resource.cpp create mode 100644 src/unrar/resource.hpp create mode 100644 src/unrar/rijndael.cpp create mode 100644 src/unrar/rijndael.hpp create mode 100644 src/unrar/rs.cpp create mode 100644 src/unrar/rs.hpp create mode 100644 src/unrar/savepos.cpp create mode 100644 src/unrar/savepos.hpp create mode 100644 src/unrar/scantree.cpp create mode 100644 src/unrar/scantree.hpp create mode 100644 src/unrar/secpassword.cpp create mode 100644 src/unrar/secpassword.hpp create mode 100644 src/unrar/sha1.cpp create mode 100644 src/unrar/sha1.hpp create mode 100644 src/unrar/smallfn.cpp create mode 100644 src/unrar/smallfn.hpp create mode 100644 src/unrar/strfn.cpp create mode 100644 src/unrar/strfn.hpp create mode 100644 src/unrar/strlist.cpp create mode 100644 src/unrar/strlist.hpp create mode 100644 src/unrar/suballoc.cpp create mode 100644 src/unrar/suballoc.hpp create mode 100644 src/unrar/system.cpp create mode 100644 src/unrar/system.hpp create mode 100644 src/unrar/timefn.cpp create mode 100644 src/unrar/timefn.hpp create mode 100644 src/unrar/ulinks.cpp create mode 100644 src/unrar/ulinks.hpp create mode 100644 src/unrar/unicode.cpp create mode 100644 src/unrar/unicode.hpp create mode 100644 src/unrar/unios2.cpp create mode 100644 src/unrar/unpack.cpp create mode 100644 src/unrar/unpack.hpp create mode 100644 src/unrar/unpack15.cpp create mode 100644 src/unrar/unpack20.cpp create mode 100644 src/unrar/uowners.cpp create mode 100644 src/unrar/version.hpp create mode 100644 src/unrar/volume.cpp create mode 100644 src/unrar/volume.hpp create mode 100644 src/unrar/win32acl.cpp create mode 100644 src/unrar/win32stm.cpp diff --git a/src/unrar/UnRAR.vcproj b/src/unrar/UnRAR.vcproj new file mode 100644 index 0000000000..da8c21458a --- /dev/null +++ b/src/unrar/UnRAR.vcproj @@ -0,0 +1,623 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/unrar/UnRARDll.vcproj b/src/unrar/UnRARDll.vcproj new file mode 100644 index 0000000000..4d3b819c4e --- /dev/null +++ b/src/unrar/UnRARDll.vcproj @@ -0,0 +1,848 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/unrar/acknow.txt b/src/unrar/acknow.txt new file mode 100644 index 0000000000..d473c52413 --- /dev/null +++ b/src/unrar/acknow.txt @@ -0,0 +1,81 @@ + ACKNOWLEDGMENTS + +* RAR text compression algorithm is based on Dmitry Shkarin PPMII + and Dmitry Subbotin carryless rangecoder public domain source code. + You may find it in ftp.elf.stuba.sk/pub/pc/pack. + +* RAR encryption includes parts of code from Szymon Stefanek + and Brian Gladman AES implementations also as Steve Reid SHA-1 source. + + --------------------------------------------------------------------------- + Copyright (c) 2002, Dr Brian Gladman < >, Worcester, UK. + All rights reserved. + + LICENSE TERMS + + The free distribution and use of this software in both source and binary + form is allowed (with or without changes) provided that: + + 1. distributions of this source code include the above copyright + notice, this list of conditions and the following disclaimer; + + 2. distributions in binary form include the above copyright + notice, this list of conditions and the following disclaimer + in the documentation and/or other associated materials; + + 3. the copyright holder's name is not used to endorse products + built using this software without specific written permission. + + ALTERNATIVELY, provided that this notice is retained in full, this product + may be distributed under the terms of the GNU General Public License (GPL), + in which case the provisions of the GPL apply INSTEAD OF those given above. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + + Source code of this package also as other cryptographic technology + and computing project related links are available on Brian Gladman's + web site: http://www.gladman.me.uk + +* RAR uses CRC32 function based on Intel Slicing-by-8 algorithm. + Original Intel Slicing-by-8 code is available here: + + http://sourceforge.net/projects/slicing-by-8/ + + Original Intel Slicing-by-8 code is licensed under BSD License + available at http://www.opensource.org/licenses/bsd-license.html + + Copyright (c) 2004-2006 Intel Corporation. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with + the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + +* Useful hints provided by Alexander Khoroshev and Bulat Ziganshin allowed + to significantly improve RAR compression and speed. diff --git a/src/unrar/arccmt.cpp b/src/unrar/arccmt.cpp new file mode 100644 index 0000000000..189fc791e3 --- /dev/null +++ b/src/unrar/arccmt.cpp @@ -0,0 +1,235 @@ +bool IsAnsiComment(const char *Data,int Size); + +bool Archive::GetComment(Array *CmtData,Array *CmtDataW) +{ + if (!MainComment) + return(false); + SaveFilePos SavePos(*this); + +#ifndef SFX_MODULE + ushort CmtLength; + if (OldFormat) + { + Seek(SFXSize+SIZEOF_OLDMHD,SEEK_SET); + CmtLength=GetByte(); + CmtLength+=(GetByte()<<8); + } + else +#endif + { + if ((NewMhd.Flags & MHD_COMMENT)!=0) + { + // Old style (RAR 2.9) archive comment embedded into the main + // archive header. + Seek(SFXSize+SIZEOF_MARKHEAD+SIZEOF_NEWMHD,SEEK_SET); + ReadHeader(); + } + else + { + // Current (RAR 3.0+) version of archive comment. + Seek(SFXSize+SIZEOF_MARKHEAD+NewMhd.HeadSize,SEEK_SET); + return(SearchSubBlock(SUBHEAD_TYPE_CMT)!=0 && ReadCommentData(CmtData,CmtDataW)!=0); + } +#ifndef SFX_MODULE + // Old style (RAR 2.9) comment header embedded into the main + // archive header. + if (CommHead.HeadCRC!=HeaderCRC) + { + Log(FileName,St(MLogCommHead)); + Alarm(); + return(false); + } + CmtLength=CommHead.HeadSize-SIZEOF_COMMHEAD; +#endif + } +#ifndef SFX_MODULE + if (OldFormat && (OldMhd.Flags & MHD_PACK_COMMENT)!=0 || !OldFormat && CommHead.Method!=0x30) + { + if (!OldFormat && (CommHead.UnpVer < 15 || CommHead.UnpVer > UNP_VER || CommHead.Method > 0x35)) + return(false); + ComprDataIO DataIO; + DataIO.SetTestMode(true); + uint UnpCmtLength; + if (OldFormat) + { +#ifdef RAR_NOCRYPT + return(false); +#else + UnpCmtLength=GetByte(); + UnpCmtLength+=(GetByte()<<8); + CmtLength-=2; + DataIO.SetCmt13Encryption(); +#endif + } + else + UnpCmtLength=CommHead.UnpSize; + DataIO.SetFiles(this,NULL); + DataIO.EnableShowProgress(false); + DataIO.SetPackedSizeToRead(CmtLength); + + Unpack Unpack(&DataIO); + Unpack.Init(); + Unpack.SetDestSize(UnpCmtLength); + Unpack.DoUnpack(CommHead.UnpVer,false); + + if (!OldFormat && ((~DataIO.UnpFileCRC)&0xffff)!=CommHead.CommCRC) + { + Log(FileName,St(MLogCommBrk)); + Alarm(); + return(false); + } + else + { + byte *UnpData; + size_t UnpDataSize; + DataIO.GetUnpackedData(&UnpData,&UnpDataSize); + CmtData->Alloc(UnpDataSize); + memcpy(&((*CmtData)[0]),UnpData,UnpDataSize); + } + } + else + { + CmtData->Alloc(CmtLength); + + Read(&((*CmtData)[0]),CmtLength); + if (!OldFormat && CommHead.CommCRC!=(~CRC(0xffffffff,&((*CmtData)[0]),CmtLength)&0xffff)) + { + Log(FileName,St(MLogCommBrk)); + Alarm(); + CmtData->Reset(); + return(false); + } + } +#endif +#if defined(_WIN_ALL) && !defined(_WIN_CE) + if (CmtData->Size()>0) + { + size_t CmtSize=CmtData->Size(); + char *DataA=(char *)CmtData->Addr(); + OemToCharBuffA(DataA,DataA,(DWORD)CmtSize); + + if (CmtDataW!=NULL) + { + CmtDataW->Alloc(CmtSize+1); + + // It can cause reallocation, so we should not use 'DataA' variable + // with previosuly saved CmtData->Addr() after Push() call. + CmtData->Push(0); + + CharToWide((char *)CmtData->Addr(),CmtDataW->Addr(),CmtSize+1); + CmtData->Alloc(CmtSize); + CmtDataW->Alloc(wcslen(CmtDataW->Addr())); + } + } +#endif + return(CmtData->Size()>0); +} + + +size_t Archive::ReadCommentData(Array *CmtData,Array *CmtDataW) +{ + bool Unicode=SubHead.SubFlags & SUBHEAD_FLAGS_CMT_UNICODE; + if (!ReadSubData(CmtData,NULL)) + return(0); + size_t CmtSize=CmtData->Size(); + if (Unicode) + { + CmtSize/=2; + Array DataW(CmtSize+1); + RawToWide(CmtData->Addr(),DataW.Addr(),CmtSize); + DataW[CmtSize]=0; + size_t DestSize=CmtSize*4; + CmtData->Alloc(DestSize+1); + WideToChar(DataW.Addr(),(char *)CmtData->Addr(),DestSize); + (*CmtData)[DestSize]=0; + CmtSize=strlen((char *)CmtData->Addr()); + CmtData->Alloc(CmtSize); + if (CmtDataW!=NULL) + { + *CmtDataW=DataW; + CmtDataW->Alloc(CmtSize); + } + } + else + if (CmtDataW!=NULL) + { + CmtData->Push(0); + CmtDataW->Alloc(CmtSize+1); + CharToWide((char *)CmtData->Addr(),CmtDataW->Addr(),CmtSize+1); + CmtData->Alloc(CmtSize); + CmtDataW->Alloc(wcslen(CmtDataW->Addr())); + } + return(CmtSize); +} + + +void Archive::ViewComment() +{ +#ifndef GUI + if (Cmd->DisableComment) + return; + Array CmtBuf; + if (GetComment(&CmtBuf,NULL)) + { + size_t CmtSize=CmtBuf.Size(); + char *ChPtr=(char *)memchr(&CmtBuf[0],0x1A,CmtSize); + if (ChPtr!=NULL) + CmtSize=ChPtr-(char *)&CmtBuf[0]; + mprintf("\n"); + OutComment((char *)&CmtBuf[0],CmtSize); + } +#endif +} + + +#ifndef SFX_MODULE +// Used for archives created by old RAR versions up to and including RAR 2.9. +// New RAR versions store file comments in separate headers and such comments +// are displayed in ListNewSubHeader function. +void Archive::ViewFileComment() +{ + if (!(NewLhd.Flags & LHD_COMMENT) || Cmd->DisableComment || OldFormat) + return; +#ifndef GUI + mprintf(St(MFileComment)); +#endif + const int MaxSize=0x8000; + Array CmtBuf(MaxSize); + SaveFilePos SavePos(*this); + Seek(CurBlockPos+SIZEOF_NEWLHD+NewLhd.NameSize,SEEK_SET); + int64 SaveCurBlockPos=CurBlockPos; + int64 SaveNextBlockPos=NextBlockPos; + + size_t Size=ReadHeader(); + + CurBlockPos=SaveCurBlockPos; + NextBlockPos=SaveNextBlockPos; + + if (Size<7 || CommHead.HeadType!=COMM_HEAD) + return; + if (CommHead.HeadCRC!=HeaderCRC) + { +#ifndef GUI + Log(FileName,St(MLogCommHead)); +#endif + return; + } + if (CommHead.UnpVer < 15 || CommHead.UnpVer > UNP_VER || + CommHead.Method > 0x30 || CommHead.UnpSize > MaxSize) + return; + Read(&CmtBuf[0],CommHead.UnpSize); + if (CommHead.CommCRC!=((~CRC(0xffffffff,&CmtBuf[0],CommHead.UnpSize)&0xffff))) + { + Log(FileName,St(MLogBrokFCmt)); + } + else + { + OutComment(&CmtBuf[0],CommHead.UnpSize); +#ifndef GUI + mprintf("\n"); +#endif + } +} +#endif + + diff --git a/src/unrar/archive.cpp b/src/unrar/archive.cpp new file mode 100644 index 0000000000..b061e540a4 --- /dev/null +++ b/src/unrar/archive.cpp @@ -0,0 +1,284 @@ +#include "rar.hpp" + +#ifndef SHELL_EXT +#include "arccmt.cpp" +#endif + + +Archive::Archive(RAROptions *InitCmd) +{ + Cmd=InitCmd==NULL ? &DummyCmd:InitCmd; + OpenShared=Cmd->OpenShared; + OldFormat=false; + Solid=false; + Volume=false; + MainComment=false; + Locked=false; + Signed=false; + NotFirstVolume=false; + SFXSize=0; + LatestTime.Reset(); + Protected=false; + Encrypted=false; + FailedHeaderDecryption=false; + BrokenFileHeader=false; + LastReadBlock=0; + + CurBlockPos=0; + NextBlockPos=0; + + RecoveryPos=SIZEOF_MARKHEAD; + RecoverySectors=-1; + + memset(&NewMhd,0,sizeof(NewMhd)); + NewMhd.HeadType=MAIN_HEAD; + NewMhd.HeadSize=SIZEOF_NEWMHD; + HeaderCRC=0; + VolWrite=0; + AddingFilesSize=0; + AddingHeadersSize=0; +#if !defined(SHELL_EXT) && !defined(RAR_NOCRYPT) + *HeadersSalt=0; + *SubDataSalt=0; +#endif + *FirstVolumeName=0; + *FirstVolumeNameW=0; + + Splitting=false; + NewArchive=false; + + SilentOpen=false; + +} + + + +#ifndef SHELL_EXT +void Archive::CheckArc(bool EnableBroken) +{ + if (!IsArchive(EnableBroken)) + { + Log(FileName,St(MBadArc),FileName); + ErrHandler.Exit(RARX_FATAL); + } +} +#endif + + +#if !defined(SHELL_EXT) && !defined(SFX_MODULE) +void Archive::CheckOpen(const char *Name,const wchar *NameW) +{ + TOpen(Name,NameW); + CheckArc(false); +} +#endif + + +bool Archive::WCheckOpen(const char *Name,const wchar *NameW) +{ + if (!WOpen(Name,NameW)) + return(false); + if (!IsArchive(false)) + { +#ifndef SHELL_EXT + Log(FileName,St(MNotRAR),FileName); +#endif + Close(); + return(false); + } + return(true); +} + + +ARCSIGN_TYPE Archive::IsSignature(const byte *D,size_t Size) +{ + ARCSIGN_TYPE Type=ARCSIGN_NONE; + if (Size>=1 && D[0]==0x52) +#ifndef SFX_MODULE + if (Size>=4 && D[1]==0x45 && D[2]==0x7e && D[3]==0x5e) + Type=ARCSIGN_OLD; + else +#endif + if (Size>=7 && D[1]==0x61 && D[2]==0x72 && D[3]==0x21 && D[4]==0x1a && D[5]==0x07) + { + // We check for non-zero last signature byte, so we can return + // a sensible warning in case we'll want to change the archive + // format sometimes in the future. + Type=D[6]==0 ? ARCSIGN_CURRENT:ARCSIGN_FUTURE; + } + return Type; +} + + +bool Archive::IsArchive(bool EnableBroken) +{ + Encrypted=false; +#ifndef SFX_MODULE + if (IsDevice()) + { +#ifndef SHELL_EXT + Log(FileName,St(MInvalidName),FileName); +#endif + return(false); + } +#endif + if (Read(MarkHead.Mark,SIZEOF_MARKHEAD)!=SIZEOF_MARKHEAD) + return(false); + SFXSize=0; + + ARCSIGN_TYPE Type; + if ((Type=IsSignature(MarkHead.Mark,sizeof(MarkHead.Mark)))!=ARCSIGN_NONE) + { + OldFormat=(Type==ARCSIGN_OLD); + if (OldFormat) + Seek(0,SEEK_SET); + } + else + { + Array Buffer(MAXSFXSIZE); + long CurPos=(long)Tell(); + int ReadSize=Read(&Buffer[0],Buffer.Size()-16); + for (int I=0;I0 && 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 (!OldFormat) + Read(MarkHead.Mark,SIZEOF_MARKHEAD); + break; + } + if (SFXSize==0) + return false; + } + if (Type==ARCSIGN_FUTURE) + { +#if !defined(SHELL_EXT) && !defined(SFX_MODULE) + Log(FileName,St(MNewRarFormat)); +#endif + return false; + } + ReadHeader(); + SeekToNext(); +#ifndef SFX_MODULE + if (OldFormat) + { + NewMhd.Flags=OldMhd.Flags & 0x3f; + NewMhd.HeadSize=OldMhd.HeadSize; + } + else +#endif + { + if (HeaderCRC!=NewMhd.HeadCRC) + { +#ifndef SHELL_EXT + Log(FileName,St(MLogMainHead)); +#endif + Alarm(); + if (!EnableBroken) + return(false); + } + } + Volume=(NewMhd.Flags & MHD_VOLUME); + Solid=(NewMhd.Flags & MHD_SOLID)!=0; + MainComment=(NewMhd.Flags & MHD_COMMENT)!=0; + Locked=(NewMhd.Flags & MHD_LOCK)!=0; + Signed=(NewMhd.PosAV!=0); + Protected=(NewMhd.Flags & MHD_PROTECT)!=0; + Encrypted=(NewMhd.Flags & MHD_PASSWORD)!=0; + + if (NewMhd.EncryptVer>UNP_VER) + { +#ifdef RARDLL + Cmd->DllError=ERAR_UNKNOWN_FORMAT; +#else + ErrHandler.SetErrorCode(RARX_WARNING); + #if !defined(SILENT) && !defined(SFX_MODULE) + Log(FileName,St(MUnknownMeth),FileName); + Log(FileName,St(MVerRequired),NewMhd.EncryptVer/10,NewMhd.EncryptVer%10); + #endif +#endif + return(false); + } +#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 + + // If not encrypted, we'll check it below. + NotFirstVolume=Encrypted && (NewMhd.Flags & MHD_FIRSTVOLUME)==0; + + if (!SilentOpen || !Encrypted) + { + SaveFilePos SavePos(*this); + int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos; + + NotFirstVolume=false; + while (ReadHeader()!=0) + { + int HeaderType=GetHeaderType(); + if (HeaderType==NEWSUB_HEAD) + { + if (SubHead.CmpName(SUBHEAD_TYPE_CMT)) + MainComment=true; + if ((SubHead.Flags & LHD_SPLIT_BEFORE) || + Volume && (NewMhd.Flags & MHD_FIRSTVOLUME)==0) + NotFirstVolume=true; + } + else + { + if (HeaderType==FILE_HEAD && ((NewLhd.Flags & LHD_SPLIT_BEFORE)!=0 || + Volume && NewLhd.UnpVer>=29 && (NewMhd.Flags & MHD_FIRSTVOLUME)==0)) + NotFirstVolume=true; + break; + } + SeekToNext(); + } + CurBlockPos=SaveCurBlockPos; + NextBlockPos=SaveNextBlockPos; + } + if (!Volume || !NotFirstVolume) + { + strcpy(FirstVolumeName,FileName); + wcscpy(FirstVolumeNameW,FileNameW); + } + + return(true); +} + + + + +void Archive::SeekToNext() +{ + Seek(NextBlockPos,SEEK_SET); +} + + +#ifndef SFX_MODULE +int Archive::GetRecoverySize(bool Required) +{ + if (!Protected) + return(0); + if (RecoverySectors!=-1 || !Required) + return(RecoverySectors); + SaveFilePos SavePos(*this); + Seek(SFXSize,SEEK_SET); + SearchSubBlock(SUBHEAD_TYPE_RR); + return(RecoverySectors); +} +#endif + + + + diff --git a/src/unrar/archive.hpp b/src/unrar/archive.hpp new file mode 100644 index 0000000000..58b4cd3efc --- /dev/null +++ b/src/unrar/archive.hpp @@ -0,0 +1,121 @@ +#ifndef _RAR_ARCHIVE_ +#define _RAR_ARCHIVE_ + +class Pack; + +enum {EN_LOCK=1,EN_VOL=2,EN_FIRSTVOL=4}; + +enum ARCSIGN_TYPE {ARCSIGN_NONE,ARCSIGN_OLD,ARCSIGN_CURRENT,ARCSIGN_FUTURE}; + +class Archive:public File +{ + private: + ARCSIGN_TYPE IsSignature(const byte *D,size_t Size); + void UpdateLatestTime(FileHeader *CurBlock); + void ConvertNameCase(char *Name); + void ConvertNameCase(wchar *Name); + void ConvertUnknownHeader(); + size_t ReadOldHeader(); + void UnexpEndArcMsg(); + +#if !defined(SHELL_EXT) && !defined(RAR_NOCRYPT) + CryptData HeadersCrypt; + byte HeadersSalt[SALT_SIZE]; +#endif +#ifndef SHELL_EXT + ComprDataIO SubDataIO; + byte SubDataSalt[SALT_SIZE]; +#endif + RAROptions *Cmd,DummyCmd; + + MarkHeader MarkHead; + OldMainHeader OldMhd; + + int RecoverySectors; + int64 RecoveryPos; + + bool FailedHeaderDecryption; + + RarTime LatestTime; + int LastReadBlock; + int CurHeaderType; + + bool SilentOpen; + public: + Archive(RAROptions *InitCmd=NULL); + bool IsArchive(bool EnableBroken); + size_t SearchBlock(int BlockType); + size_t SearchSubBlock(const char *Type); + int ReadBlock(int BlockType); + void WriteBlock(int BlockType,BaseBlock *wb=NULL); + int PrepareNamesToWrite(char *Name,wchar *NameW,char *DestName,byte *DestNameW); + void SetLhdSize(); + size_t ReadHeader(); + void CheckArc(bool EnableBroken); + void CheckOpen(const char *Name,const wchar *NameW=NULL); + bool WCheckOpen(const char *Name,const wchar *NameW=NULL); + bool GetComment(Array *CmtData,Array *CmtDataW); + void ViewComment(); + void ViewFileComment(); + void SetLatestTime(RarTime *NewTime); + void SeekToNext(); + bool CheckAccess(); + bool IsArcDir(); + bool IsArcLabel(); + void ConvertAttributes(); + int GetRecoverySize(bool Required); + void VolSubtractHeaderSize(size_t SubSize); + void AddSubData(byte *SrcData,size_t DataSize,File *SrcFile,const char *Name,bool AllowSplit); + bool ReadSubData(Array *UnpData,File *DestFile); + int GetHeaderType() {return(CurHeaderType);}; + size_t ReadCommentData(Array *CmtData,Array *CmtDataW); + void WriteCommentData(byte *Data,size_t DataSize,bool FileComment); + RAROptions* GetRAROptions() {return(Cmd);} + void SetSilentOpen(bool Mode) {SilentOpen=Mode;} + + BaseBlock ShortBlock; + MainHeader NewMhd; + FileHeader NewLhd; + EndArcHeader EndArcHead; + SubBlockHeader SubBlockHead; + FileHeader SubHead; + CommentHeader CommHead; + ProtectHeader ProtectHead; + AVHeader AVHead; + SignHeader SignHead; + UnixOwnersHeader UOHead; + MacFInfoHeader MACHead; + EAHeader EAHead; + StreamHeader StreamHead; + + int64 CurBlockPos; + int64 NextBlockPos; + + bool OldFormat; + bool Solid; + bool Volume; + bool MainComment; + bool Locked; + bool Signed; + bool NotFirstVolume; + bool Protected; + bool Encrypted; + size_t SFXSize; + bool BrokenFileHeader; + + bool Splitting; + + ushort HeaderCRC; + + int64 VolWrite; + int64 AddingFilesSize; + size_t AddingHeadersSize; + + bool NewArchive; + + char FirstVolumeName[NM]; + wchar FirstVolumeNameW[NM]; +}; + + +#endif diff --git a/src/unrar/arcread.cpp b/src/unrar/arcread.cpp new file mode 100644 index 0000000000..a524a96124 --- /dev/null +++ b/src/unrar/arcread.cpp @@ -0,0 +1,765 @@ +#include "rar.hpp" + +size_t Archive::SearchBlock(int BlockType) +{ + size_t Size,Count=0; + while ((Size=ReadHeader())!=0 && + (BlockType==ENDARC_HEAD || GetHeaderType()!=ENDARC_HEAD)) + { + if ((++Count & 127)==0) + Wait(); + if (GetHeaderType()==BlockType) + return(Size); + SeekToNext(); + } + return(0); +} + + +size_t Archive::SearchSubBlock(const char *Type) +{ + size_t Size; + while ((Size=ReadHeader())!=0 && GetHeaderType()!=ENDARC_HEAD) + { + if (GetHeaderType()==NEWSUB_HEAD && SubHead.CmpName(Type)) + return(Size); + SeekToNext(); + } + return(0); +} + + +void Archive::UnexpEndArcMsg() +{ + int64 ArcSize=FileLength(); + if (CurBlockPos>ArcSize || NextBlockPos>ArcSize) + { +#ifndef SHELL_EXT + Log(FileName,St(MLogUnexpEOF)); +#endif + ErrHandler.SetErrorCode(RARX_WARNING); + } +} + + +size_t Archive::ReadHeader() +{ + // Once we failed to decrypt an encrypted block, there is no reason to + // attempt to do it further. We'll never be successful and only generate + // endless errors. + if (FailedHeaderDecryption) + return 0; + + CurBlockPos=Tell(); + +#ifndef SFX_MODULE + if (OldFormat) + return(ReadOldHeader()); +#endif + + RawRead Raw(this); + + bool Decrypt=Encrypted && CurBlockPos>=(int64)SFXSize+SIZEOF_MARKHEAD+SIZEOF_NEWMHD; + + if (Decrypt) + { +#if defined(SHELL_EXT) || defined(RAR_NOCRYPT) + return(0); +#else + if (Read(HeadersSalt,SALT_SIZE)!=SALT_SIZE) + { + UnexpEndArcMsg(); + return(0); + } + if (!Cmd->Password.IsSet()) + { +#ifdef RARDLL + if (Cmd->Callback!=NULL) + { + wchar PasswordW[MAXPASSWORD]; + *PasswordW=0; + if (Cmd->Callback(UCM_NEEDPASSWORDW,Cmd->UserData,(LPARAM)PasswordW,ASIZE(PasswordW))==-1) + *PasswordW=0; + if (*PasswordW==0) + { + char PasswordA[MAXPASSWORD]; + *PasswordA=0; + if (Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)PasswordA,ASIZE(PasswordA))==-1) + *PasswordA=0; + GetWideName(PasswordA,NULL,PasswordW,ASIZE(PasswordW)); + cleandata(PasswordA,sizeof(PasswordA)); + } + Cmd->Password.Set(PasswordW); + cleandata(PasswordW,sizeof(PasswordW)); + } + if (!Cmd->Password.IsSet()) + { + Close(); + Cmd->DllError=ERAR_MISSING_PASSWORD; + ErrHandler.Exit(RARX_USERBREAK); + } +#else + if (!GetPassword(PASSWORD_ARCHIVE,FileName,FileNameW,&Cmd->Password)) + { + Close(); + ErrHandler.Exit(RARX_USERBREAK); + } +#endif + } + HeadersCrypt.SetCryptKeys(&Cmd->Password,HeadersSalt,false,false,NewMhd.EncryptVer>=36); + Raw.SetCrypt(&HeadersCrypt); +#endif + } + + Raw.Read(SIZEOF_SHORTBLOCKHEAD); + if (Raw.Size()==0) + { + UnexpEndArcMsg(); + return(0); + } + + Raw.Get(ShortBlock.HeadCRC); + byte HeadType; + Raw.Get(HeadType); + ShortBlock.HeadType=(HEADER_TYPE)HeadType; + Raw.Get(ShortBlock.Flags); + Raw.Get(ShortBlock.HeadSize); + if (ShortBlock.HeadSizePackSize); + Raw.Get(hd->UnpSize); + Raw.Get(hd->HostOS); + Raw.Get(hd->FileCRC); + Raw.Get(hd->FileTime); + Raw.Get(hd->UnpVer); + Raw.Get(hd->Method); + Raw.Get(hd->NameSize); + Raw.Get(hd->FileAttr); + if (hd->Flags & LHD_LARGE) + { + Raw.Get(hd->HighPackSize); + Raw.Get(hd->HighUnpSize); + } + else + { + hd->HighPackSize=hd->HighUnpSize=0; + if (hd->UnpSize==0xffffffff) + { + // UnpSize equal to 0xffffffff without LHD_LARGE flag indicates + // that we do not know the unpacked file size and must unpack it + // until we find the end of file marker in compressed data. + hd->UnpSize=(uint)(INT64NDF); + hd->HighUnpSize=(uint)(INT64NDF>>32); + } + } + hd->FullPackSize=INT32TO64(hd->HighPackSize,hd->PackSize); + hd->FullUnpSize=INT32TO64(hd->HighUnpSize,hd->UnpSize); + + char FileName[NM*4]; + size_t NameSize=Min(hd->NameSize,sizeof(FileName)-1); + Raw.Get((byte *)FileName,NameSize); + FileName[NameSize]=0; + + strncpyz(hd->FileName,FileName,ASIZE(hd->FileName)); + + if (hd->HeadType==NEWSUB_HEAD) + { + // Let's calculate the size of optional data. + int DataSize=hd->HeadSize-hd->NameSize-SIZEOF_NEWLHD; + if (hd->Flags & LHD_SALT) + DataSize-=SALT_SIZE; + + if (DataSize>0) + { + // Here we read optional additional fields for subheaders. + // They are stored after the file name and before salt. + hd->SubData.Alloc(DataSize); + Raw.Get(&hd->SubData[0],DataSize); + if (hd->CmpName(SUBHEAD_TYPE_RR)) + { + byte *D=&hd->SubData[8]; + RecoverySectors=D[0]+((uint)D[1]<<8)+((uint)D[2]<<16)+((uint)D[3]<<24); + } + } + } + else + if (hd->HeadType==FILE_HEAD) + { + if (hd->Flags & LHD_UNICODE) + { + EncodeFileName NameCoder; + size_t Length=strlen(FileName); + if (Length==hd->NameSize) + { + UtfToWide(FileName,hd->FileNameW,sizeof(hd->FileNameW)/sizeof(hd->FileNameW[0])-1); + WideToChar(hd->FileNameW,hd->FileName,sizeof(hd->FileName)/sizeof(hd->FileName[0])-1); + ExtToInt(hd->FileName,hd->FileName); + } + else + { + Length++; + NameCoder.Decode(FileName,(byte *)FileName+Length, + hd->NameSize-Length,hd->FileNameW, + sizeof(hd->FileNameW)/sizeof(hd->FileNameW[0])); + } + if (*hd->FileNameW==0) + hd->Flags &= ~LHD_UNICODE; + } + else + *hd->FileNameW=0; +#ifndef SFX_MODULE + ConvertNameCase(hd->FileName); + ConvertNameCase(hd->FileNameW); +#endif + ConvertUnknownHeader(); + } + if (hd->Flags & LHD_SALT) + Raw.Get(hd->Salt,SALT_SIZE); + hd->mtime.SetDos(hd->FileTime); + hd->ctime.Reset(); + hd->atime.Reset(); + hd->arctime.Reset(); + if (hd->Flags & LHD_EXTTIME) + { + ushort Flags; + Raw.Get(Flags); + RarTime *tbl[4]; + tbl[0]=&NewLhd.mtime; + tbl[1]=&NewLhd.ctime; + tbl[2]=&NewLhd.atime; + tbl[3]=&NewLhd.arctime; + for (int I=0;I<4;I++) + { + RarTime *CurTime=tbl[I]; + uint rmode=Flags>>(3-I)*4; + if ((rmode & 8)==0) + continue; + if (I!=0) + { + uint DosTime; + Raw.Get(DosTime); + CurTime->SetDos(DosTime); + } + RarLocalTime rlt; + CurTime->GetLocal(&rlt); + if (rmode & 4) + rlt.Second++; + rlt.Reminder=0; + int count=rmode&3; + for (int J=0;JSetLocal(&rlt); + } + } + NextBlockPos+=hd->FullPackSize; + bool CRCProcessedOnly=(hd->Flags & LHD_COMMENT)!=0; + HeaderCRC=~Raw.GetCRC(CRCProcessedOnly)&0xffff; + if (hd->HeadCRC!=HeaderCRC) + { + if (hd->HeadType==NEWSUB_HEAD && strlen(hd->FileName)FileName)-5) + strcat(hd->FileName,"- ???"); + BrokenFileHeader=true; + ErrHandler.SetErrorCode(RARX_WARNING); + + // If we have a broken encrypted header, we do not need to display + // the error message here, because it will be displayed for such + // headers later in this function. Also such headers are unlikely + // to have anything sensible in file name field, so it is useless + // to display the file name. + bool EncBroken=Decrypt && ShortBlock.HeadCRC!=(~Raw.GetCRC(false)&0xffff); + if (!EncBroken) + { +#ifndef SHELL_EXT + Log(Archive::FileName,St(MLogFileHead),IntNameToExt(hd->FileName)); + Alarm(); +#endif + } + } + } + break; +#ifndef SFX_MODULE + case COMM_HEAD: + *(BaseBlock *)&CommHead=ShortBlock; + Raw.Get(CommHead.UnpSize); + Raw.Get(CommHead.UnpVer); + Raw.Get(CommHead.Method); + Raw.Get(CommHead.CommCRC); + break; + case SIGN_HEAD: + *(BaseBlock *)&SignHead=ShortBlock; + Raw.Get(SignHead.CreationTime); + Raw.Get(SignHead.ArcNameSize); + Raw.Get(SignHead.UserNameSize); + break; + case AV_HEAD: + *(BaseBlock *)&AVHead=ShortBlock; + Raw.Get(AVHead.UnpVer); + Raw.Get(AVHead.Method); + Raw.Get(AVHead.AVVer); + Raw.Get(AVHead.AVInfoCRC); + break; + case PROTECT_HEAD: + *(BaseBlock *)&ProtectHead=ShortBlock; + Raw.Get(ProtectHead.DataSize); + Raw.Get(ProtectHead.Version); + Raw.Get(ProtectHead.RecSectors); + Raw.Get(ProtectHead.TotalBlocks); + Raw.Get(ProtectHead.Mark,8); + NextBlockPos+=ProtectHead.DataSize; + RecoverySectors=ProtectHead.RecSectors; + break; + case SUB_HEAD: + *(BaseBlock *)&SubBlockHead=ShortBlock; + Raw.Get(SubBlockHead.DataSize); + NextBlockPos+=SubBlockHead.DataSize; + Raw.Get(SubBlockHead.SubType); + Raw.Get(SubBlockHead.Level); + switch(SubBlockHead.SubType) + { + case UO_HEAD: + *(SubBlockHeader *)&UOHead=SubBlockHead; + Raw.Get(UOHead.OwnerNameSize); + Raw.Get(UOHead.GroupNameSize); + if (UOHead.OwnerNameSize>NM-1) + UOHead.OwnerNameSize=NM-1; + if (UOHead.GroupNameSize>NM-1) + UOHead.GroupNameSize=NM-1; + Raw.Get((byte *)UOHead.OwnerName,UOHead.OwnerNameSize); + Raw.Get((byte *)UOHead.GroupName,UOHead.GroupNameSize); + UOHead.OwnerName[UOHead.OwnerNameSize]=0; + UOHead.GroupName[UOHead.GroupNameSize]=0; + break; + case MAC_HEAD: + *(SubBlockHeader *)&MACHead=SubBlockHead; + Raw.Get(MACHead.fileType); + Raw.Get(MACHead.fileCreator); + break; + case EA_HEAD: + case BEEA_HEAD: + case NTACL_HEAD: + *(SubBlockHeader *)&EAHead=SubBlockHead; + Raw.Get(EAHead.UnpSize); + Raw.Get(EAHead.UnpVer); + Raw.Get(EAHead.Method); + Raw.Get(EAHead.EACRC); + break; + case STREAM_HEAD: + *(SubBlockHeader *)&StreamHead=SubBlockHead; + Raw.Get(StreamHead.UnpSize); + Raw.Get(StreamHead.UnpVer); + Raw.Get(StreamHead.Method); + Raw.Get(StreamHead.StreamCRC); + Raw.Get(StreamHead.StreamNameSize); + if (StreamHead.StreamNameSize>NM-1) + StreamHead.StreamNameSize=NM-1; + Raw.Get((byte *)StreamHead.StreamName,StreamHead.StreamNameSize); + StreamHead.StreamName[StreamHead.StreamNameSize]=0; + break; + } + break; +#endif + default: + if (ShortBlock.Flags & LONG_BLOCK) + { + uint DataSize; + Raw.Get(DataSize); + NextBlockPos+=DataSize; + } + break; + } + HeaderCRC=~Raw.GetCRC(false)&0xffff; + CurHeaderType=ShortBlock.HeadType; + if (Decrypt) + { + NextBlockPos+=Raw.PaddedSize()+SALT_SIZE; + + if (ShortBlock.HeadCRC!=HeaderCRC) + { + bool Recovered=false; + if (ShortBlock.HeadType==ENDARC_HEAD && (EndArcHead.Flags & EARC_REVSPACE)!=0) + { + // Last 7 bytes of recovered volume can contain zeroes, because + // REV files store its own information (volume number, etc.) here. + SaveFilePos SavePos(*this); + int64 Length=Tell(); + Seek(Length-7,SEEK_SET); + Recovered=true; + for (int J=0;J<7;J++) + if (GetByte()!=0) + Recovered=false; + } + if (!Recovered) + { +#ifndef SILENT + Log(FileName,St(MEncrBadCRC),FileName); +#endif +// Close(); + FailedHeaderDecryption=true; + BrokenFileHeader=true; + + ErrHandler.SetErrorCode(RARX_CRC); + return(0); + } + } + } + + if (NextBlockPos<=CurBlockPos) + { +#ifndef SHELL_EXT + Log(FileName,St(MLogFileHead),"???"); +#endif + BrokenFileHeader=true; + ErrHandler.SetErrorCode(RARX_CRC); + return(0); + } + return(Raw.Size()); +} + + +#ifndef SFX_MODULE +size_t Archive::ReadOldHeader() +{ + RawRead Raw(this); + if (CurBlockPos<=(int64)SFXSize) + { + Raw.Read(SIZEOF_OLDMHD); + Raw.Get(OldMhd.Mark,4); + Raw.Get(OldMhd.HeadSize); + Raw.Get(OldMhd.Flags); + NextBlockPos=CurBlockPos+OldMhd.HeadSize; + CurHeaderType=MAIN_HEAD; + } + else + { + OldFileHeader OldLhd; + Raw.Read(SIZEOF_OLDLHD); + NewLhd.HeadType=FILE_HEAD; + Raw.Get(NewLhd.PackSize); + Raw.Get(NewLhd.UnpSize); + Raw.Get(OldLhd.FileCRC); + Raw.Get(NewLhd.HeadSize); + Raw.Get(NewLhd.FileTime); + Raw.Get(OldLhd.FileAttr); + Raw.Get(OldLhd.Flags); + Raw.Get(OldLhd.UnpVer); + Raw.Get(OldLhd.NameSize); + Raw.Get(OldLhd.Method); + + NewLhd.Flags=OldLhd.Flags|LONG_BLOCK; + NewLhd.UnpVer=(OldLhd.UnpVer==2) ? 13 : 10; + NewLhd.Method=OldLhd.Method+0x30; + NewLhd.NameSize=OldLhd.NameSize; + NewLhd.FileAttr=OldLhd.FileAttr; + NewLhd.FileCRC=OldLhd.FileCRC; + NewLhd.FullPackSize=NewLhd.PackSize; + NewLhd.FullUnpSize=NewLhd.UnpSize; + + NewLhd.mtime.SetDos(NewLhd.FileTime); + NewLhd.ctime.Reset(); + NewLhd.atime.Reset(); + NewLhd.arctime.Reset(); + + Raw.Read(OldLhd.NameSize); + Raw.Get((byte *)NewLhd.FileName,OldLhd.NameSize); + NewLhd.FileName[OldLhd.NameSize]=0; + ConvertNameCase(NewLhd.FileName); + *NewLhd.FileNameW=0; + + if (Raw.Size()!=0) + NextBlockPos=CurBlockPos+NewLhd.HeadSize+NewLhd.PackSize; + CurHeaderType=FILE_HEAD; + } + return(NextBlockPos>CurBlockPos ? Raw.Size():0); +} +#endif + + +void Archive::ConvertNameCase(char *Name) +{ + if (Cmd->ConvertNames==NAMES_UPPERCASE) + { + IntToExt(Name,Name); + strupper(Name); + ExtToInt(Name,Name); + } + if (Cmd->ConvertNames==NAMES_LOWERCASE) + { + IntToExt(Name,Name); + strlower(Name); + ExtToInt(Name,Name); + } +} + + +#ifndef SFX_MODULE +void Archive::ConvertNameCase(wchar *Name) +{ + if (Cmd->ConvertNames==NAMES_UPPERCASE) + wcsupper(Name); + if (Cmd->ConvertNames==NAMES_LOWERCASE) + wcslower(Name); +} +#endif + + +bool Archive::IsArcDir() +{ + return((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY); +} + + +bool Archive::IsArcLabel() +{ + return(NewLhd.HostOS<=HOST_WIN32 && (NewLhd.FileAttr & 8)); +} + + +void Archive::ConvertAttributes() +{ +#if defined(_WIN_ALL) || defined(_EMX) + switch(NewLhd.HostOS) + { + case HOST_MSDOS: + case HOST_OS2: + case HOST_WIN32: + break; + case HOST_UNIX: + case HOST_BEOS: + if ((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY) + NewLhd.FileAttr=0x10; + else + NewLhd.FileAttr=0x20; + break; + default: + if ((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY) + NewLhd.FileAttr=0x10; + else + NewLhd.FileAttr=0x20; + break; + } +#endif +#ifdef _UNIX + // umask defines which permission bits must not be set by default + // when creating a file or directory. The typical default value + // for the process umask is S_IWGRP | S_IWOTH (octal 022), + // resulting in 0644 mode for new files. + static mode_t mask = (mode_t) -1; + + if (mask == (mode_t) -1) + { + // umask call returns the current umask value. Argument (022) is not + // really important here. + mask = umask(022); + + // Restore the original umask value, which was changed to 022 above. + umask(mask); + } + + switch(NewLhd.HostOS) + { + case HOST_MSDOS: + case HOST_OS2: + case HOST_WIN32: + { + // Mapping MSDOS, OS/2 and Windows file attributes to Unix. + + if (NewLhd.FileAttr & 0x10) // FILE_ATTRIBUTE_DIRECTORY + { + // For directories we use 0777 mask. + NewLhd.FileAttr=0777 & ~mask; + } + else + if (NewLhd.FileAttr & 1) // FILE_ATTRIBUTE_READONLY + { + // For read only files we use 0444 mask with 'w' bits turned off. + NewLhd.FileAttr=0444 & ~mask; + } + else + { + // umask does not set +x for regular files, so we use 0666 + // instead of 0777 as for directories. + NewLhd.FileAttr=0666 & ~mask; + } + } + break; + case HOST_UNIX: + case HOST_BEOS: + break; + default: + if ((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY) + NewLhd.FileAttr=0x41ff & ~mask; + else + NewLhd.FileAttr=0x81b6 & ~mask; + break; + } +#endif +} + + +void Archive::ConvertUnknownHeader() +{ + if (NewLhd.UnpVer<20 && (NewLhd.FileAttr & 0x10)) + NewLhd.Flags|=LHD_DIRECTORY; + if (NewLhd.HostOS>=HOST_MAX) + { + if ((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY) + NewLhd.FileAttr=0x10; + else + NewLhd.FileAttr=0x20; + } + for (char *s=NewLhd.FileName;*s!=0;s=charnext(s)) + { + if (*s=='/' || *s=='\\') + *s=CPATHDIVIDER; +#if defined(_APPLE) && !defined(UNICODE_SUPPORTED) + if ((byte)*s<32 || (byte)*s>127) + *s='_'; +#endif + +#if defined(_WIN_ALL) || defined(_EMX) + // ':' in file names is allowed in Unix, but not in Windows. + // Even worse, file data will be written to NTFS stream on NTFS, + // so automatic name correction on file create error in extraction + // routine does not work. In Windows and DOS versions we better + // replace ':' now. + if (*s==':') + *s='_'; +#endif + + } + + for (wchar *s=NewLhd.FileNameW;*s!=0;s++) + { + if (*s=='/' || *s=='\\') + *s=CPATHDIVIDER; + +#if defined(_WIN_ALL) || defined(_EMX) + // ':' in file names is allowed in Unix, but not in Windows. + // Even worse, file data will be written to NTFS stream on NTFS, + // so automatic name correction on file create error in extraction + // routine does not work. In Windows and DOS versions we better + // replace ':' now. + if (*s==':') + *s='_'; +#endif + } +} + + +#ifndef SHELL_EXT +bool Archive::ReadSubData(Array *UnpData,File *DestFile) +{ + if (HeaderCRC!=SubHead.HeadCRC) + { +#ifndef SHELL_EXT + Log(FileName,St(MSubHeadCorrupt)); +#endif + ErrHandler.SetErrorCode(RARX_CRC); + return(false); + } + if (SubHead.Method<0x30 || SubHead.Method>0x35 || SubHead.UnpVer>/*PACK_VER*/36) + { +#ifndef SHELL_EXT + Log(FileName,St(MSubHeadUnknown)); +#endif + return(false); + } + + if (SubHead.PackSize==0 && (SubHead.Flags & LHD_SPLIT_AFTER)==0) + return(true); + + SubDataIO.Init(); + Unpack Unpack(&SubDataIO); + Unpack.Init(); + + if (DestFile==NULL) + { + UnpData->Alloc(SubHead.UnpSize); + SubDataIO.SetUnpackToMemory(&(*UnpData)[0],SubHead.UnpSize); + } + if (SubHead.Flags & LHD_PASSWORD) + if (Cmd->Password.IsSet()) + SubDataIO.SetEncryption(SubHead.UnpVer,&Cmd->Password, + (SubHead.Flags & LHD_SALT) ? SubHead.Salt:NULL,false, + SubHead.UnpVer>=36); + else + return(false); + SubDataIO.SetPackedSizeToRead(SubHead.PackSize); + SubDataIO.EnableShowProgress(false); + SubDataIO.SetFiles(this,DestFile); + SubDataIO.UnpVolume=(SubHead.Flags & LHD_SPLIT_AFTER)!=0; + SubDataIO.SetSubHeader(&SubHead,NULL); + Unpack.SetDestSize(SubHead.UnpSize); + if (SubHead.Method==0x30) + CmdExtract::UnstoreFile(SubDataIO,SubHead.UnpSize); + else + Unpack.DoUnpack(SubHead.UnpVer,false); + + if (SubHead.FileCRC!=~SubDataIO.UnpFileCRC) + { +#ifndef SHELL_EXT + Log(FileName,St(MSubHeadDataCRC),SubHead.FileName); +#endif + ErrHandler.SetErrorCode(RARX_CRC); + if (UnpData!=NULL) + UnpData->Reset(); + return(false); + } + return(true); +} +#endif diff --git a/src/unrar/array.hpp b/src/unrar/array.hpp new file mode 100644 index 0000000000..f3821bc47d --- /dev/null +++ b/src/unrar/array.hpp @@ -0,0 +1,131 @@ +#ifndef _RAR_ARRAY_ +#define _RAR_ARRAY_ + +extern ErrorHandler ErrHandler; + +template class Array +{ + private: + T *Buffer; + size_t BufSize; + size_t AllocSize; + public: + Array(); + Array(size_t Size); + ~Array(); + inline void CleanData(); + inline T& operator [](size_t Item); + inline size_t Size(); // Returns the size in items, not in bytes. + void Add(size_t Items); + void Alloc(size_t Items); + void Reset(); + void SoftReset(); + void operator = (Array &Src); + void Push(T Item); + T* Addr() {return(Buffer);} +}; + +template void Array::CleanData() +{ + Buffer=NULL; + BufSize=0; + AllocSize=0; +} + + +template Array::Array() +{ + CleanData(); +} + + +template Array::Array(size_t Size) +{ + Buffer=(T *)malloc(sizeof(T)*Size); + if (Buffer==NULL && Size!=0) + ErrHandler.MemoryError(); + + AllocSize=BufSize=Size; +} + + +template Array::~Array() +{ + if (Buffer!=NULL) + free(Buffer); +} + + +template inline T& Array::operator [](size_t Item) +{ + return(Buffer[Item]); +} + + +template inline size_t Array::Size() +{ + return(BufSize); +} + + +template void Array::Add(size_t Items) +{ + BufSize+=Items; + if (BufSize>AllocSize) + { + size_t Suggested=AllocSize+AllocSize/4+32; + size_t NewSize=Max(BufSize,Suggested); + + Buffer=(T *)realloc(Buffer,NewSize*sizeof(T)); + if (Buffer==NULL) + ErrHandler.MemoryError(); + AllocSize=NewSize; + } +} + + +template void Array::Alloc(size_t Items) +{ + if (Items>AllocSize) + Add(Items-BufSize); + else + BufSize=Items; +} + + +template void Array::Reset() +{ + if (Buffer!=NULL) + { + free(Buffer); + Buffer=NULL; + } + BufSize=0; + AllocSize=0; +} + + +// Reste buffer size, but preserve already allocated memory if any, +// so we can reuse it without wasting time to allocation. +template void Array::SoftReset() +{ + BufSize=0; +} + + +template void Array::operator =(Array &Src) +{ + Reset(); + Alloc(Src.BufSize); + if (Src.BufSize!=0) + memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T)); +} + + +template void Array::Push(T Item) +{ + Add(1); + (*this)[Size()-1]=Item; +} + +#endif diff --git a/src/unrar/beosea.cpp b/src/unrar/beosea.cpp new file mode 100644 index 0000000000..3b6e862e45 --- /dev/null +++ b/src/unrar/beosea.cpp @@ -0,0 +1,113 @@ + + +void ExtractBeEA(Archive &Arc,char *FileName) +{ + if (Arc.HeaderCRC!=Arc.EAHead.HeadCRC) + { + Log(Arc.FileName,St(MEABroken),FileName); + ErrHandler.SetErrorCode(RARX_CRC); + return; + } + if (Arc.EAHead.Method<0x31 || Arc.EAHead.Method>0x35 || Arc.EAHead.UnpVer>PACK_VER) + { + Log(Arc.FileName,St(MEAUnknHeader),FileName); + return; + } + + ComprDataIO DataIO; + Unpack Unpack(&DataIO); + Unpack.Init(); + + Array UnpData(Arc.EAHead.UnpSize); + DataIO.SetUnpackToMemory(&UnpData[0],Arc.EAHead.UnpSize); + DataIO.SetPackedSizeToRead(Arc.EAHead.DataSize); + DataIO.EnableShowProgress(false); + DataIO.SetFiles(&Arc,NULL); + Unpack.SetDestSize(Arc.EAHead.UnpSize); + Unpack.DoUnpack(Arc.EAHead.UnpVer,false); + + if (Arc.EAHead.EACRC!=~DataIO.UnpFileCRC) + { + Log(Arc.FileName,St(MEABroken),FileName); + ErrHandler.SetErrorCode(RARX_CRC); + return; + } + int fd = open(FileName,O_WRONLY); + if (fd==-1) + { + Log(Arc.FileName,St(MCannotSetEA),FileName); + ErrHandler.SetErrorCode(RARX_WARNING); + return; + } + + int AttrPos=0; + while (AttrPos=sizeof(Name)) + { + Log(Arc.FileName,St(MCannotSetEA),FileName); + ErrHandler.SetErrorCode(RARX_WARNING); + break; + } + memcpy(Name,CurItem+10,NameSize); + Name[NameSize]=0; + if (fs_write_attr(fd,Name,Type,0,CurItem+10+NameSize,Size)==-1) + { + Log(Arc.FileName,St(MCannotSetEA),FileName); + ErrHandler.SetErrorCode(RARX_WARNING); + break; + } + AttrPos+=10+NameSize+Size; + } + close(fd); + mprintf(St(MShowEA)); +} + + +void ExtractBeEANew(Archive &Arc,char *FileName) +{ + Array SubData; + if (!Arc.ReadSubData(&SubData,NULL)) + return; + + int fd = open(FileName,O_WRONLY); + if (fd==-1) + { + Log(Arc.FileName,St(MCannotSetEA),FileName); + ErrHandler.SetErrorCode(RARX_WARNING); + return; + } + + int AttrPos=0; + while (AttrPos=sizeof(Name)) + { + Log(Arc.FileName,St(MCannotSetEA),FileName); + ErrHandler.SetErrorCode(RARX_WARNING); + break; + } + memcpy(Name,CurItem+10,NameSize); + Name[NameSize]=0; + if (fs_write_attr(fd,Name,Type,0,CurItem+10+NameSize,Size)==-1) + { + Log(Arc.FileName,St(MCannotSetEA),FileName); + ErrHandler.SetErrorCode(RARX_WARNING); + break; + } + AttrPos+=10+NameSize+Size; + } + close(fd); + mprintf(St(MShowEA)); +} + diff --git a/src/unrar/cmddata.cpp b/src/unrar/cmddata.cpp new file mode 100644 index 0000000000..e17fa20616 --- /dev/null +++ b/src/unrar/cmddata.cpp @@ -0,0 +1,1431 @@ +#include "rar.hpp" + + +CommandData::CommandData() +{ + FileArgs=ExclArgs=InclArgs=StoreArgs=ArcNames=NULL; + Init(); +} + + +CommandData::~CommandData() +{ + Close(); +} + + +void CommandData::Init() +{ + RAROptions::Init(); + Close(); + + *Command=0; + *CommandW=0; + *ArcName=0; + *ArcNameW=0; + FileLists=false; + NoMoreSwitches=false; + + ListMode=RCLM_AUTO; + + + FileArgs=new StringList; + ExclArgs=new StringList; + InclArgs=new StringList; + StoreArgs=new StringList; + ArcNames=new StringList; +} + + +void CommandData::Close() +{ + delete FileArgs; + delete ExclArgs; + delete InclArgs; + delete StoreArgs; + delete ArcNames; + FileArgs=ExclArgs=InclArgs=StoreArgs=ArcNames=NULL; + NextVolSizes.Reset(); +} + + +#ifdef CUSTOM_CMDLINE_PARSER +// Return the pointer to next position in the string and store dynamically +// allocated command line parameters in Unicode and ASCII in ParW and ParA. +static const wchar *AllocCmdParam(const wchar *CmdLine,wchar **ParW,char **ParA) +{ + const wchar *NextCmd=GetCmdParam(CmdLine,NULL,0); + if (NextCmd==NULL) + return NULL; + size_t ParSize=NextCmd-CmdLine+2; // Parameter size including the trailing zero. + *ParW=(wchar *)malloc(ParSize*sizeof(wchar)); + if (*ParW==NULL) + return NULL; + CmdLine=GetCmdParam(CmdLine,*ParW,ParSize); + size_t ParSizeA=ParSize*2; // One Unicode char can be converted to several MBCS chars. + *ParA=(char *)malloc(ParSizeA); + if (*ParA==NULL) + { + free(*ParW); + return NULL; + } + GetAsciiName(*ParW,*ParA,ParSizeA); + return CmdLine; +} +#endif + + +#ifndef SFX_MODULE +void CommandData::PreprocessCommandLine(int argc, char *argv[]) +{ +#ifdef CUSTOM_CMDLINE_PARSER + // In Windows we may prefer to implement our own command line parser + // to avoid replacing \" by " in standard parser. Such replacing corrupts + // destination paths like "dest path\" in extraction commands. + const wchar *CmdLine=GetCommandLine(); + + wchar *ParW; + char *ParA; + for (bool FirstParam=true;;FirstParam=false) + { + if ((CmdLine=AllocCmdParam(CmdLine,&ParW,&ParA))==NULL) + break; + bool Code=FirstParam ? true:PreprocessSwitch(ParA); + free(ParW); + free(ParA); + if (!Code) + break; + } +#else + for (int I=1;IAddString(Arg,ArgW); + else + { + FindData FileData; + bool Found=FindFile::FastFind(Arg,ArgW,&FileData); + if ((!Found || ListMode==RCLM_ACCEPT_LISTS) && + ListMode!=RCLM_REJECT_LISTS && *Arg=='@' && !IsWildcard(Arg,ArgW)) + { + FileLists=true; + + RAR_CHARSET Charset=FilelistCharset; + +#if defined(_WIN_ALL) && !defined(GUI) + // for compatibility reasons we use OEM encoding + // in Win32 console version by default + + if (Charset==RCH_DEFAULT) + Charset=RCH_OEM; +#endif + + wchar *WideArgName=(ArgW!=NULL && *ArgW!=0 ? ArgW+1:NULL); + ReadTextFile(Arg+1,WideArgName,FileArgs,false,true,Charset,true,true,true); + + } + else + if (Found && FileData.IsDir && Extract && *ExtrPath==0 && *ExtrPathW==0) + { + strncpyz(ExtrPath,Arg,ASIZE(ExtrPath)-1); + AddEndSlash(ExtrPath); + if (ArgW!=NULL) + { + wcsncpyz(ExtrPathW,ArgW,ASIZE(ExtrPathW)-1); + AddEndSlash(ExtrPathW); + } + } + else + FileArgs->AddString(Arg,ArgW); + } + } +} +#endif + + +void CommandData::ParseDone() +{ + if (FileArgs->ItemsCount()==0 && !FileLists) + FileArgs->AddString(MASKALL); + char CmdChar=etoupper(*Command); + bool Extract=CmdChar=='X' || CmdChar=='E' || CmdChar=='P'; + if (Test && Extract) + Test=false; // Switch '-t' is senseless for 'X', 'E', 'P' commands. + BareOutput=(CmdChar=='L' || CmdChar=='V') && Command[1]=='B'; +} + + +#if !defined(SFX_MODULE) && !defined(_WIN_CE) +void CommandData::ParseEnvVar() +{ + char *EnvStr=getenv("RAR"); + if (EnvStr!=NULL) + { + ProcessSwitchesString(EnvStr); + } +} +#endif + + + +#ifndef SFX_MODULE +// Preprocess those parameters, which must be processed before the rest of +// command line. Return 'false' to stop further processing. +bool CommandData::PreprocessSwitch(const char *Switch) +{ + if (IsSwitch(Switch[0])) + { + Switch++; + if (stricomp(Switch,"-")==0) // Switch "--". + return false; + if (stricomp(Switch,"cfg-")==0) + ConfigDisabled=true; +#ifndef GUI + if (strnicomp(Switch,"ilog",4)==0) + { + // Ensure that correct log file name is already set + // if we need to report an error when processing the command line. + ProcessSwitch(Switch); + InitLogOptions(LogName); + } +#endif + if (strnicomp(Switch,"sc",2)==0) + { + // Process -sc before reading any file lists. + ProcessSwitch(Switch); + } + } + return true; +} +#endif + + +#if !defined(GUI) && !defined(SFX_MODULE) +void CommandData::ReadConfig() +{ + StringList List; + if (ReadTextFile(DefConfigName,NULL,&List,true)) + { + char *Str; + while ((Str=List.GetString())!=NULL) + { + while (IsSpace(*Str)) + Str++; + if (strnicomp(Str,"switches=",9)==0) + ProcessSwitchesString(Str+9); + } + } +} +#endif + + +#if !defined(SFX_MODULE) && !defined(_WIN_CE) +void CommandData::ProcessSwitchesString(char *Str) +{ + while (*Str) + { + while (!IsSwitch(*Str) && *Str!=0) + Str++; + if (*Str==0) + break; + char *Next=Str; + while (!(Next[0]==' ' && IsSwitch(Next[1])) && *Next!=0) + Next++; + char NextChar=*Next; + *Next=0; + ProcessSwitch(Str+1); + *Next=NextChar; + Str=Next; + } +} +#endif + + +#if !defined(SFX_MODULE) +void CommandData::ProcessSwitch(const char *Switch,const wchar *SwitchW) +{ + + bool WidePresent=SwitchW!=NULL && *SwitchW!=0; // If 'true', SwitchW is not empty. + + switch(etoupper(Switch[0])) + { + case '@': + ListMode=Switch[1]=='+' ? RCLM_ACCEPT_LISTS:RCLM_REJECT_LISTS; + break; + case 'I': + if (strnicomp(&Switch[1],"LOG",3)==0) + { + strncpyz(LogName,Switch[4] ? Switch+4:DefLogName,ASIZE(LogName)); + break; + } + if (stricomp(&Switch[1],"SND")==0) + { + Sound=true; + break; + } + if (stricomp(&Switch[1],"ERR")==0) + { + MsgStream=MSG_STDERR; + break; + } + if (strnicomp(&Switch[1],"EML",3)==0) + { + strncpyz(EmailTo,Switch[4] ? Switch+4:"@",ASIZE(EmailTo)); + EmailTo[sizeof(EmailTo)-1]=0; + break; + } + if (stricomp(&Switch[1],"NUL")==0) + { + MsgStream=MSG_NULL; + break; + } + if (etoupper(Switch[1])=='D') + { + for (int I=2;Switch[I]!=0;I++) + switch(etoupper(Switch[I])) + { + case 'Q': + MsgStream=MSG_ERRONLY; + break; + case 'C': + DisableCopyright=true; + break; + case 'D': + DisableDone=true; + break; + case 'P': + DisablePercentage=true; + break; + } + break; + } + if (stricomp(&Switch[1],"OFF")==0) + { + Shutdown=true; + break; + } + break; + case 'T': + switch(etoupper(Switch[1])) + { + case 'K': + ArcTime=ARCTIME_KEEP; + break; + case 'L': + ArcTime=ARCTIME_LATEST; + break; + case 'O': + FileTimeBefore.SetAgeText(Switch+2); + break; + case 'N': + FileTimeAfter.SetAgeText(Switch+2); + break; + case 'B': + FileTimeBefore.SetIsoText(Switch+2); + break; + case 'A': + FileTimeAfter.SetIsoText(Switch+2); + break; + case 'S': + { + EXTTIME_MODE Mode=EXTTIME_HIGH3; + bool CommonMode=Switch[2]>='0' && Switch[2]<='4'; + if (CommonMode) + Mode=(EXTTIME_MODE)(Switch[2]-'0'); + if (Switch[2]=='-') + Mode=EXTTIME_NONE; + if (CommonMode || Switch[2]=='-' || Switch[2]=='+' || Switch[2]==0) + xmtime=xctime=xatime=Mode; + else + { + if (Switch[3]>='0' && Switch[3]<='4') + Mode=(EXTTIME_MODE)(Switch[3]-'0'); + if (Switch[3]=='-') + Mode=EXTTIME_NONE; + switch(etoupper(Switch[2])) + { + case 'M': + xmtime=Mode; + break; + case 'C': + xctime=Mode; + break; + case 'A': + xatime=Mode; + break; + case 'R': + xarctime=Mode; + break; + } + } + } + break; + case '-': + Test=false; + break; + case 0: + Test=true; + break; + default: + BadSwitch(Switch); + break; + } + break; + case 'A': + switch(etoupper(Switch[1])) + { + case 'C': + ClearArc=true; + break; + case 'D': + AppendArcNameToPath=true; + break; +#ifndef SFX_MODULE + case 'G': + if (Switch[2]=='-' && Switch[3]==0) + GenerateArcName=0; + else + { + GenerateArcName=true; + strncpyz(GenerateMask,Switch+2,ASIZE(GenerateMask)); + } + break; +#endif + case 'I': + IgnoreGeneralAttr=true; + break; + case 'N': // Reserved for archive name. + break; + case 'O': + AddArcOnly=true; + break; + case 'P': + strcpy(ArcPath,Switch+2); + if (WidePresent) + wcscpy(ArcPathW,SwitchW+2); + break; + case 'S': + SyncFiles=true; + break; + default: + BadSwitch(Switch); + break; + } + break; + case 'D': + if (Switch[2]==0) + switch(etoupper(Switch[1])) + { + case 'S': + DisableSortSolid=true; + break; + case 'H': + OpenShared=true; + break; + case 'F': + DeleteFiles=true; + break; + } + break; + case 'O': + switch(etoupper(Switch[1])) + { + case '+': + Overwrite=OVERWRITE_ALL; + break; + case '-': + Overwrite=OVERWRITE_NONE; + break; + case 0: + Overwrite=OVERWRITE_FORCE_ASK; + break; + case 'R': + Overwrite=OVERWRITE_AUTORENAME; + break; + case 'W': + ProcessOwners=true; + break; +#ifdef SAVE_LINKS + case 'L': + SaveLinks=true; + break; +#endif +#ifdef _WIN_ALL + case 'S': + SaveStreams=true; + break; + case 'C': + SetCompressedAttr=true; + break; +#endif + default : + BadSwitch(Switch); + break; + } + break; + case 'R': + switch(etoupper(Switch[1])) + { + case 0: + Recurse=RECURSE_ALWAYS; + break; + case '-': + Recurse=RECURSE_DISABLE; + break; + case '0': + Recurse=RECURSE_WILDCARDS; + break; +#ifndef _WIN_CE + case 'I': + { + Priority=atoi(Switch+2); + if (Priority<0 || Priority>15) + BadSwitch(Switch); + const char *ChPtr=strchr(Switch+2,':'); + if (ChPtr!=NULL) + { + SleepTime=atoi(ChPtr+1); + if (SleepTime>1000) + BadSwitch(Switch); + InitSystemOptions(SleepTime); + } + SetPriority(Priority); + } + break; +#endif + } + break; + case 'Y': + AllYes=true; + break; + case 'N': + case 'X': + if (Switch[1]!=0) + { + StringList *Args=etoupper(Switch[0])=='N' ? InclArgs:ExclArgs; + if (Switch[1]=='@' && !IsWildcard(Switch)) + { + RAR_CHARSET Charset=FilelistCharset; + +#if defined(_WIN_ALL) && !defined(GUI) + // for compatibility reasons we use OEM encoding + // in Win32 console version by default + + if (Charset==RCH_DEFAULT) + Charset=RCH_OEM; +#endif + + ReadTextFile(Switch+2,NULL,Args,false,true,Charset,true,true,true); + } + else + Args->AddString(Switch+1); + } + break; + case 'E': + switch(etoupper(Switch[1])) + { + case 'P': + switch(Switch[2]) + { + case 0: + ExclPath=EXCL_SKIPWHOLEPATH; + break; + case '1': + ExclPath=EXCL_BASEPATH; + break; + case '2': + ExclPath=EXCL_SAVEFULLPATH; + break; + case '3': + ExclPath=EXCL_ABSPATH; + break; + } + break; + case 'E': + ProcessEA=false; + break; + case 'N': + NoEndBlock=true; + break; + default: + if (Switch[1]=='+') + { + InclFileAttr=GetExclAttr(&Switch[2]); + InclAttrSet=true; + } + else + ExclFileAttr=GetExclAttr(&Switch[1]); + break; + } + break; + case 'P': + if (Switch[1]==0) + { + GetPassword(PASSWORD_GLOBAL,NULL,NULL,&Password); + eprintf("\n"); + } + else + { + wchar PlainPsw[MAXPASSWORD]; + CharToWide(Switch+1,PlainPsw,ASIZE(PlainPsw)); + PlainPsw[ASIZE(PlainPsw)-1]=0; + Password.Set(PlainPsw); + cleandata(PlainPsw,ASIZE(PlainPsw)); + } + break; + case 'H': + if (etoupper(Switch[1])=='P') + { + EncryptHeaders=true; + if (Switch[2]!=0) + { + wchar PlainPsw[MAXPASSWORD]; + CharToWide(Switch+2,PlainPsw,ASIZE(PlainPsw)); + PlainPsw[ASIZE(PlainPsw)-1]=0; + Password.Set(PlainPsw); + cleandata(PlainPsw,ASIZE(PlainPsw)); + } + else + if (!Password.IsSet()) + { + GetPassword(PASSWORD_GLOBAL,NULL,NULL,&Password); + eprintf("\n"); + } + } + break; + case 'Z': + if (Switch[1]==0 && (!WidePresent || SwitchW[1]==0)) + { +#ifndef GUI // stdin is not supported by WinRAR. + // If comment file is not specified, we read data from stdin. + strcpy(CommentFile,"stdin"); +#endif + } + else + { + strncpyz(CommentFile,Switch+1,ASIZE(CommentFile)); + if (WidePresent) + wcsncpyz(CommentFileW,SwitchW+1,ASIZE(CommentFileW)); + } + break; + case 'M': + switch(etoupper(Switch[1])) + { + case 'C': + { + const char *Str=Switch+2; + if (*Str=='-') + for (uint I=0;IAddString(Mask); + if (End==NULL) + break; + Names=End+1; + } + } + break; +#ifdef RAR_SMP + case 'T': + Threads=atoi(Switch+2); + if (Threads>MaxPoolThreads || Threads<1) + BadSwitch(Switch); + else + { + } + break; +#endif + default: + Method=Switch[1]-'0'; + if (Method>5 || Method<0) + BadSwitch(Switch); + break; + } + break; + case 'V': + switch(etoupper(Switch[1])) + { + case 'N': + OldNumbering=true; + break; + case 'P': + VolumePause=true; + break; + case 'E': + if (etoupper(Switch[2])=='R') + VersionControl=atoi(Switch+3)+1; + break; + case '-': + VolSize=0; + break; + default: + VolSize=VOLSIZE_AUTO; // UnRAR -v switch for list command. + break; + } + break; + case 'F': + if (Switch[1]==0) + FreshFiles=true; + else + BadSwitch(Switch); + break; + case 'U': + if (Switch[1]==0) + UpdateFiles=true; + else + BadSwitch(Switch); + break; + case 'W': + strncpyz(TempPath,&Switch[1],ASIZE(TempPath)); + AddEndSlash(TempPath); + break; + case 'S': + if (IsDigit(Switch[1])) + { + Solid|=SOLID_COUNT; + SolidCount=atoi(&Switch[1]); + } + else + switch(etoupper(Switch[1])) + { + case 0: + Solid|=SOLID_NORMAL; + break; + case '-': + Solid=SOLID_NONE; + break; + case 'E': + Solid|=SOLID_FILEEXT; + break; + case 'V': + Solid|=Switch[2]=='-' ? SOLID_VOLUME_DEPENDENT:SOLID_VOLUME_INDEPENDENT; + break; + case 'D': + Solid|=SOLID_VOLUME_DEPENDENT; + break; + case 'L': + if (IsDigit(Switch[2])) + FileSizeLess=atoil(Switch+2); + break; + case 'M': + if (IsDigit(Switch[2])) + FileSizeMore=atoil(Switch+2); + break; + case 'C': + { + // Switch is already found bad, avoid reporting it several times. + bool AlreadyBad=false; + + RAR_CHARSET rch=RCH_DEFAULT; + switch(etoupper(Switch[2])) + { + case 'A': + rch=RCH_ANSI; + break; + case 'O': + rch=RCH_OEM; + break; + case 'U': + rch=RCH_UNICODE; + break; + default : + BadSwitch(Switch); + AlreadyBad=true; + break; + }; + if (!AlreadyBad) + if (Switch[3]==0) + CommentCharset=FilelistCharset=rch; + else + for (int I=3;Switch[I]!=0 && !AlreadyBad;I++) + switch(etoupper(Switch[I])) + { + case 'C': + CommentCharset=rch; + break; + case 'L': + FilelistCharset=rch; + break; + default: + BadSwitch(Switch); + AlreadyBad=true; + break; + } + } + break; + + } + break; + case 'C': + if (Switch[2]==0) + switch(etoupper(Switch[1])) + { + case '-': + DisableComment=true; + break; + case 'U': + ConvertNames=NAMES_UPPERCASE; + break; + case 'L': + ConvertNames=NAMES_LOWERCASE; + break; + } + break; + case 'K': + switch(etoupper(Switch[1])) + { + case 'B': + KeepBroken=true; + break; + case 0: + Lock=true; + break; + } + break; +#ifndef GUI + case '?' : + OutHelp(RARX_SUCCESS); + break; +#endif + default : + BadSwitch(Switch); + break; + } +} +#endif + + +#ifndef SFX_MODULE +void CommandData::BadSwitch(const char *Switch) +{ + mprintf(St(MUnknownOption),Switch); + ErrHandler.Exit(RARX_USERERROR); +} +#endif + + +#ifndef GUI +void CommandData::OutTitle() +{ + if (BareOutput || DisableCopyright) + return; +#if defined(__GNUC__) && defined(SFX_MODULE) + mprintf(St(MCopyrightS)); +#else +#ifndef SILENT + static bool TitleShown=false; + if (TitleShown) + return; + TitleShown=true; + char Version[50]; + int Beta=RARVER_BETA; + if (Beta!=0) + sprintf(Version,"%d.%02d %s %d",RARVER_MAJOR,RARVER_MINOR,St(MBeta),RARVER_BETA); + else + sprintf(Version,"%d.%02d",RARVER_MAJOR,RARVER_MINOR); +#ifdef UNRAR + mprintf(St(MUCopyright),Version,RARVER_YEAR); +#else +#endif +#endif +#endif +} +#endif + + +inline bool CmpMSGID(MSGID i1,MSGID i2) +{ +#ifdef MSGID_INT + return(i1==i2); +#else + // If MSGID is const char*, we cannot compare pointers only. + // Pointers to different instances of same strings can differ, + // so we need to compare complete strings. + return(strcmp(i1,i2)==0); +#endif +} + +void CommandData::OutHelp(RAR_EXIT ExitCode) +{ +#if !defined(GUI) && !defined(SILENT) + OutTitle(); + static MSGID Help[]={ +#ifdef SFX_MODULE + // Console SFX switches definition. + MCHelpCmd,MSHelpCmdE,MSHelpCmdT,MSHelpCmdV +#elif defined(UNRAR) + // UnRAR switches definition. + MUNRARTitle1,MRARTitle2,MCHelpCmd,MCHelpCmdE,MCHelpCmdL, + MCHelpCmdP,MCHelpCmdT,MCHelpCmdV,MCHelpCmdX,MCHelpSw,MCHelpSwm, + MCHelpSwAT,MCHelpSwAC,MCHelpSwAD,MCHelpSwAG,MCHelpSwAI,MCHelpSwAP, + MCHelpSwCm,MCHelpSwCFGm,MCHelpSwCL,MCHelpSwCU, + MCHelpSwDH,MCHelpSwEP,MCHelpSwEP3,MCHelpSwF,MCHelpSwIDP,MCHelpSwIERR, + MCHelpSwINUL,MCHelpSwIOFF,MCHelpSwKB,MCHelpSwN,MCHelpSwNa,MCHelpSwNal, + MCHelpSwO,MCHelpSwOC,MCHelpSwOR,MCHelpSwOW,MCHelpSwP, + MCHelpSwPm,MCHelpSwR,MCHelpSwRI,MCHelpSwSL,MCHelpSwSM,MCHelpSwTA, + MCHelpSwTB,MCHelpSwTN,MCHelpSwTO,MCHelpSwTS,MCHelpSwU,MCHelpSwVUnr, + MCHelpSwVER,MCHelpSwVP,MCHelpSwX,MCHelpSwXa,MCHelpSwXal,MCHelpSwY +#else +#endif + }; + + for (int I=0;IItemsCount()==0) + return(false); + if (ExclCheckArgs(InclArgs,Dir,CheckName,false,MATCH_WILDSUBPATH)) + return(false); + return(true); +} + + +bool CommandData::ExclCheckArgs(StringList *Args,bool Dir,char *CheckName,bool CheckFullPath,int MatchMode) +{ + char *Name=ConvertPath(CheckName,NULL); + char FullName[NM]; + char CurMask[NM+1]; // We reserve the space to append "*" to mask. + *FullName=0; + Args->Rewind(); + while (Args->GetString(CurMask,ASIZE(CurMask)-1)) + { + char *LastMaskChar=PointToLastChar(CurMask); + bool DirMask=IsPathDiv(*LastMaskChar); // Mask for directories only. + + if (Dir) + { + // CheckName is a directory. + if (DirMask) + { + // We process the directory and have the directory exclusion mask. + // So let's convert "mask\" to "mask" and process it normally. + + *LastMaskChar=0; + } + else + { + // If mask has wildcards in name part and does not have the trailing + // '\' character, we cannot use it for directories. + + if (IsWildcard(PointToName(CurMask))) + continue; + } + } + else + { + // If we process a file inside of directory excluded by "dirmask\". + // we want to exclude such file too. So we convert "dirmask\" to + // "dirmask\*". It is important for operations other than archiving. + // When archiving, directory matched by "dirmask\" is excluded + // from further scanning. + + if (DirMask) + strcat(CurMask,"*"); + } + +#ifndef SFX_MODULE + if (CheckFullPath && IsFullPath(CurMask)) + { + // We do not need to do the special "*\" processing here, because + // unlike the "else" part of this "if", now we convert names to full + // format, so they all include the path, which is matched by "*\" + // correctly. Moreover, removing "*\" from mask would break + // the comparison, because now all names have the path. + + if (*FullName==0) + ConvertNameToFull(CheckName,FullName); + if (CmpName(CurMask,FullName,MatchMode)) + return(true); + } + else +#endif + { + char NewName[NM+2],*CurName=Name; + if (CurMask[0]=='*' && IsPathDiv(CurMask[1])) + { + // We want "*\name" to match 'name' not only in subdirectories, + // but also in the current directory. We convert the name + // from 'name' to '.\name' to be matched by "*\" part even if it is + // in current directory. + NewName[0]='.'; + NewName[1]=CPATHDIVIDER; + strncpyz(NewName+2,Name,ASIZE(NewName)-2); + CurName=NewName; + } + + if (CmpName(ConvertPath(CurMask,NULL),CurName,MatchMode)) + return(true); + } + } + return(false); +} + + +#ifndef SFX_MODULE +// Now this function performs only one task and only in Windows version: +// it skips symlinks to directories if -e1024 switch is specified. +// Symlinks are skipped in ScanTree class, so their entire contents +// is skipped too. Without this function we would check the attribute +// only directly before archiving, so we would skip the symlink record, +// but not the contents of symlinked directory. +bool CommandData::ExclDirByAttr(uint FileAttr) +{ +#ifdef _WIN_ALL + if ((FileAttr & FILE_ATTRIBUTE_REPARSE_POINT)!=0 && + (ExclFileAttr & FILE_ATTRIBUTE_REPARSE_POINT)!=0) + return true; +#endif + return false; +} +#endif + + + + +#ifndef SFX_MODULE +// Return 'true' if we need to exclude the file from processing. +bool CommandData::TimeCheck(RarTime &ft) +{ + if (FileTimeBefore.IsSet() && ft>=FileTimeBefore) + return(true); + if (FileTimeAfter.IsSet() && ft<=FileTimeAfter) + return(true); + return(false); +} +#endif + + +#ifndef SFX_MODULE +// Return 'true' if we need to exclude the file from processing. +bool CommandData::SizeCheck(int64 Size) +{ + if (FileSizeLess!=INT64NDF && Size>=FileSizeLess) + return(true); + if (FileSizeMore!=INT64NDF && Size<=FileSizeMore) + return(true); + return(false); +} +#endif + + + + +int CommandData::IsProcessFile(FileHeader &NewLhd,bool *ExactMatch,int MatchType) +{ + if (strlen(NewLhd.FileName)>=NM || wcslen(NewLhd.FileNameW)>=NM) + return(0); + bool Dir=(NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY; + if (ExclCheck(NewLhd.FileName,Dir,false,true)) + return(0); +#ifndef SFX_MODULE + if (TimeCheck(NewLhd.mtime)) + return(0); + if ((NewLhd.FileAttr & ExclFileAttr)!=0 || InclAttrSet && (NewLhd.FileAttr & InclFileAttr)==0) + return(0); + if (!Dir && SizeCheck(NewLhd.FullUnpSize)) + return(0); +#endif + char *ArgName; + wchar *ArgNameW; + FileArgs->Rewind(); + for (int StringCount=1;FileArgs->GetString(&ArgName,&ArgNameW);StringCount++) + { +#ifndef SFX_MODULE + bool Unicode=(NewLhd.Flags & LHD_UNICODE) || ArgNameW!=NULL && *ArgNameW!=0; + if (Unicode) + { + wchar NameW[NM],ArgW[NM],*NamePtr=NewLhd.FileNameW; + bool CorrectUnicode=true; + if (ArgNameW==NULL || *ArgNameW==0) + { + if (!CharToWide(ArgName,ArgW) || *ArgW==0) + CorrectUnicode=false; + ArgNameW=ArgW; + } + if ((NewLhd.Flags & LHD_UNICODE)==0) + { + if (!CharToWide(NewLhd.FileName,NameW) || *NameW==0) + CorrectUnicode=false; + NamePtr=NameW; + } + if (CmpName(ArgNameW,NamePtr,MatchType)) + { + if (ExactMatch!=NULL) + *ExactMatch=wcsicompc(ArgNameW,NamePtr)==0; + return(StringCount); + } + if (CorrectUnicode) + continue; + } +#endif + if (CmpName(ArgName,NewLhd.FileName,MatchType)) + { + if (ExactMatch!=NULL) + *ExactMatch=stricompc(ArgName,NewLhd.FileName)==0; + return(StringCount); + } + } + return(0); +} + + +#ifndef GUI +void CommandData::ProcessCommand() +{ +#ifndef SFX_MODULE + + const char *SingleCharCommands="FUADPXETK"; + if (Command[0]!=0 && Command[1]!=0 && strchr(SingleCharCommands,*Command)!=NULL || *ArcName==0) + OutHelp(*Command==0 ? RARX_SUCCESS:RARX_USERERROR); // Return 'success' for 'rar' without parameters. + +#ifdef _UNIX + if (GetExt(ArcName)==NULL && (!FileExist(ArcName) || IsDir(GetFileAttr(ArcName)))) + strncatz(ArcName,".rar",ASIZE(ArcName)); +#else + if (GetExt(ArcName)==NULL) + strncatz(ArcName,".rar",ASIZE(ArcName)); +#endif + + if (strchr("AFUMD",*Command)==NULL) + { + if (GenerateArcName) + GenerateArchiveName(ArcName,ArcNameW,ASIZE(ArcName),GenerateMask,false); + + StringList ArcMasks; + ArcMasks.AddString(ArcName); + ScanTree Scan(&ArcMasks,Recurse,SaveLinks,SCAN_SKIPDIRS); + FindData FindData; + while (Scan.GetNext(&FindData)==SCAN_SUCCESS) + AddArcName(FindData.Name,FindData.NameW); + } + else + AddArcName(ArcName,NULL); +#endif + + switch(Command[0]) + { + case 'P': + case 'X': + case 'E': + case 'T': + case 'I': + { + CmdExtract Extract; + Extract.DoExtract(this); + } + break; +#ifndef SILENT + case 'V': + case 'L': + ListArchive(this); + break; + default: + OutHelp(RARX_USERERROR); +#endif + } + if (!BareOutput) + mprintf("\n"); +} +#endif + + +void CommandData::AddArcName(const char *Name,const wchar *NameW) +{ + ArcNames->AddString(Name,NameW); +} + + +bool CommandData::GetArcName(char *Name,wchar *NameW,int MaxSize) +{ + if (!ArcNames->GetString(Name,NameW,NM)) + return(false); + return(true); +} + + +bool CommandData::IsSwitch(int Ch) +{ +#if defined(_WIN_ALL) || defined(_EMX) + return(Ch=='-' || Ch=='/'); +#else + return(Ch=='-'); +#endif +} + + +#ifndef SFX_MODULE +uint CommandData::GetExclAttr(const char *Str) +{ + if (IsDigit(*Str)) + return(strtol(Str,NULL,0)); + else + { + uint Attr; + for (Attr=0;*Str;Str++) + switch(etoupper(*Str)) + { +#ifdef _UNIX + case 'D': + Attr|=S_IFDIR; + break; + case 'V': + Attr|=S_IFCHR; + break; +#elif defined(_WIN_ALL) || defined(_EMX) + case 'R': + Attr|=0x1; + break; + case 'H': + Attr|=0x2; + break; + case 'S': + Attr|=0x4; + break; + case 'D': + Attr|=0x10; + break; + case 'A': + Attr|=0x20; + break; +#endif + } + return(Attr); + } +} +#endif + + + + +#ifndef SFX_MODULE +bool CommandData::CheckWinSize() +{ + static int ValidSize[]={ + 0x10000,0x20000,0x40000,0x80000,0x100000,0x200000,0x400000 + }; + for (int I=0;IGetChar()); +} + + +void RangeCoder::InitDecoder(Unpack *UnpackRead) +{ + RangeCoder::UnpackRead=UnpackRead; + + low=code=0; + range=uint(-1); + for (int i=0;i < 4;i++) + code=(code << 8) | GetChar(); +} + + +// (int) cast before "low" added only to suppress compiler warnings. +#define ARI_DEC_NORMALIZE(code,low,range,read) \ +{ \ + while ((low^(low+range))GetChar(); \ + range <<= 8; \ + low <<= 8; \ + } \ +} + + +inline int RangeCoder::GetCurrentCount() +{ + return (code-low)/(range /= SubRange.scale); +} + + +inline uint RangeCoder::GetCurrentShiftCount(uint SHIFT) +{ + return (code-low)/(range >>= SHIFT); +} + + +inline void RangeCoder::Decode() +{ + low += range*SubRange.LowCount; + range *= SubRange.HighCount-SubRange.LowCount; +} diff --git a/src/unrar/coder.hpp b/src/unrar/coder.hpp new file mode 100644 index 0000000000..0c8156c44b --- /dev/null +++ b/src/unrar/coder.hpp @@ -0,0 +1,25 @@ +/**************************************************************************** + * Contents: 'Carryless rangecoder' by Dmitry Subbotin * + ****************************************************************************/ + +const uint TOP=1 << 24, BOT=1 << 15; + + +class RangeCoder +{ + public: + void InitDecoder(Unpack *UnpackRead); + inline int GetCurrentCount(); + inline uint GetCurrentShiftCount(uint SHIFT); + inline void Decode(); + inline void PutChar(unsigned int c); + inline unsigned int GetChar(); + + uint low, code, range; + struct SUBRANGE + { + uint LowCount, HighCount, scale; + } SubRange; + + Unpack *UnpackRead; +}; diff --git a/src/unrar/compress.hpp b/src/unrar/compress.hpp new file mode 100644 index 0000000000..3157e1a3b1 --- /dev/null +++ b/src/unrar/compress.hpp @@ -0,0 +1,40 @@ +#ifndef _RAR_COMPRESS_ +#define _RAR_COMPRESS_ + +class ComprDataIO; +class PackingFileTable; + +#define MAX_LZ_MATCH 0x101 + +#define MAXWINSIZE 0x400000 +#define MAXWINMASK (MAXWINSIZE-1) + +#define LOW_DIST_REP_COUNT 16 + +#define NC 299 /* alphabet = {0, 1, 2, ..., NC - 1} */ +#define DC 60 +#define LDC 17 +#define RC 28 +#define HUFF_TABLE_SIZE (NC+DC+RC+LDC) +#define BC 20 + +#define NC20 298 /* alphabet = {0, 1, 2, ..., NC - 1} */ +#define DC20 48 +#define RC20 28 +#define BC20 19 +#define MC20 257 + +// Largest alphabet size among all values listed above. +#define LARGEST_TABLE_SIZE 299 + +enum {CODE_HUFFMAN,CODE_LZ,CODE_LZ2,CODE_REPEATLZ,CODE_CACHELZ, + CODE_STARTFILE,CODE_ENDFILE,CODE_VM,CODE_VMDATA}; + + +enum FilterType { + FILTER_NONE, FILTER_PPM /*dummy*/, FILTER_E8, FILTER_E8E9, + FILTER_UPCASETOLOW, FILTER_AUDIO, FILTER_RGB, FILTER_DELTA, + FILTER_ITANIUM, FILTER_E8E9V2 +}; + +#endif diff --git a/src/unrar/consio.cpp b/src/unrar/consio.cpp new file mode 100644 index 0000000000..42b72a617f --- /dev/null +++ b/src/unrar/consio.cpp @@ -0,0 +1,319 @@ +#include "rar.hpp" + +#ifndef GUI +#include "log.cpp" +#endif + +static int KbdAnsi(char *Addr,int Size); + +#if !defined(GUI) && !defined(SILENT) +static void RawPrint(char *Msg,MESSAGE_TYPE MessageType); +static byte GetKey(); +#endif + +static MESSAGE_TYPE MsgStream=MSG_STDOUT; +static bool Sound=false; +const int MaxMsgSize=2*NM+2048; + +void InitConsoleOptions(MESSAGE_TYPE MsgStream,bool Sound) +{ + ::MsgStream=MsgStream; + ::Sound=Sound; +} + +#if !defined(GUI) && !defined(SILENT) +void mprintf(const char *fmt,...) +{ + if (MsgStream==MSG_NULL || MsgStream==MSG_ERRONLY) + return; + char Msg[MaxMsgSize]; + va_list argptr; + va_start(argptr,fmt); + vsnprintf(Msg,ASIZE(Msg),fmt,argptr); + + // Different vsnprintf implementation can return either -1 or >=MaxMsgSize + // if string is truncated. So we do not check exit code and always zero + // terminate the string for safety. It is faster than check for error. + Msg[ASIZE(Msg)-1] = 0; + + RawPrint(Msg,MsgStream); + va_end(argptr); +} +#endif + + +#if !defined(GUI) && !defined(SILENT) +void eprintf(const char *fmt,...) +{ + if (MsgStream==MSG_NULL) + return; + safebuf char Msg[MaxMsgSize]; + va_list argptr; + va_start(argptr,fmt); + vsnprintf(Msg,ASIZE(Msg),fmt,argptr); + + // Different vsnprintf implementation can return either -1 or >=MaxMsgSize + // if string is truncated. So we do not check exit code and always zero + // terminate the string for safety. It is faster than check for error. + Msg[ASIZE(Msg)-1] = 0; + + RawPrint(Msg,MSG_STDERR); + va_end(argptr); +} +#endif + + +#if !defined(GUI) && !defined(SILENT) +void RawPrint(char *Msg,MESSAGE_TYPE MessageType) +{ + File OutFile; + switch(MessageType) + { + case MSG_STDOUT: + OutFile.SetHandleType(FILE_HANDLESTD); + break; + case MSG_STDERR: + case MSG_ERRONLY: + OutFile.SetHandleType(FILE_HANDLEERR); + break; + default: + return; + } +#ifdef _WIN_ALL + CharToOemA(Msg,Msg); + + char OutMsg[MaxMsgSize]; + size_t OutPos=0; + for (size_t I=0;Msg[I]!=0;I++) + { + if (Msg[I]=='\n' && (I==0 || Msg[I-1]!='\r') && OutPosSet(PlainPsw); + cleandata(PlainPsw,sizeof(PlainPsw)); + break; + } + return(true); +} +#endif + + +#if !defined(GUI) && !defined(SILENT) +byte GetKey() +{ + char Str[80]; + bool EndOfFile; +#if defined(__GNUC__) || defined(sun) + EndOfFile=(fgets(Str,sizeof(Str),stdin)==NULL); +#else + File SrcFile; + SrcFile.SetHandleType(FILE_HANDLESTD); + EndOfFile=(SrcFile.Read(Str,sizeof(Str))==0); +#endif + if (EndOfFile) + { + // Looks like stdin is a null device. We can enter to infinite loop + // calling Ask(), so let's better exit. + ErrHandler.Exit(RARX_USERBREAK); + } + return (byte)Str[0]; +} +#endif + + +#if !defined(GUI) && !defined(SILENT) +int Ask(const char *AskStr) +{ + const int MaxItems=10; + char Item[MaxItems][40]; + int ItemKeyPos[MaxItems],NumItems=0; + + for (const char *NextItem=AskStr;NextItem!=NULL;NextItem=strchr(NextItem+1,'_')) + { + char *CurItem=Item[NumItems]; + strncpyz(CurItem,NextItem+1,ASIZE(Item[0])); + char *EndItem=strchr(CurItem,'_'); + if (EndItem!=NULL) + *EndItem=0; + int KeyPos=0,CurKey; + while ((CurKey=CurItem[KeyPos])!=0) + { + bool Found=false; + for (int I=0;I4 ? "\n":" "):", "); + int KeyPos=ItemKeyPos[I]; + for (int J=0;J>1)^0xEDB88320L : (C>>1); + CRCTab[I]=crc_tables[0][I]=C; + } + + for (uint I=0;I<=256;I++) // Build additional lookup tables. + { + uint C=crc_tables[0][I]; + for (uint J=1;J<8;J++) + { + C=crc_tables[0][(byte)C]^(C>>8); + crc_tables[J][I]=C; + } + } +} + + +uint CRC(uint StartCRC,const void *Addr,size_t Size) +{ + if (CRCTab[1]==0) + InitCRC(); + byte *Data=(byte *)Addr; + + // Align Data to 8 for better performance. + for (;Size>0 && ((long)Data & 7);Size--,Data++) + StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8); + + for (;Size>=8;Size-=8,Data+=8) + { +#ifdef BIG_ENDIAN + StartCRC ^= Data[0]|(Data[1] << 8)|(Data[2] << 16)|(Data[3] << 24); +#else + StartCRC ^= *(uint32 *) Data; +#endif + StartCRC = crc_tables[7][(byte) StartCRC ] ^ + crc_tables[6][(byte)(StartCRC >> 8) ] ^ + crc_tables[5][(byte)(StartCRC >> 16)] ^ + crc_tables[4][(byte)(StartCRC >> 24)] ^ + crc_tables[3][Data[4]] ^ + crc_tables[2][Data[5]] ^ + crc_tables[1][Data[6]] ^ + crc_tables[0][Data[7]]; + } + + for (;Size>0;Size--,Data++) // Process left data. + StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8); + + return(StartCRC); +} + + +#ifndef SFX_MODULE +// For RAR 1.4 archives in case somebody still has them. +ushort OldCRC(ushort StartCRC,const void *Addr,size_t Size) +{ + byte *Data=(byte *)Addr; + for (size_t I=0;I>15))&0xffff; + } + return(StartCRC); +} +#endif diff --git a/src/unrar/crc.hpp b/src/unrar/crc.hpp new file mode 100644 index 0000000000..196a7e6ab7 --- /dev/null +++ b/src/unrar/crc.hpp @@ -0,0 +1,8 @@ +#ifndef _RAR_CRC_ +#define _RAR_CRC_ + +void InitCRC(); +uint CRC(uint StartCRC,const void *Addr,size_t Size); +ushort OldCRC(ushort StartCRC,const void *Addr,size_t Size); + +#endif diff --git a/src/unrar/crypt.cpp b/src/unrar/crypt.cpp new file mode 100644 index 0000000000..5cc5aefbf2 --- /dev/null +++ b/src/unrar/crypt.cpp @@ -0,0 +1,387 @@ +#include "rar.hpp" + +#ifndef SFX_MODULE +extern uint CRCTab[256]; +#endif + +#define NROUNDS 32 + +#define rol(x,n,xsize) (((x)<<(n)) | ((x)>>(xsize-(n)))) +#define ror(x,n,xsize) (((x)>>(n)) | ((x)<<(xsize-(n)))) + +#define substLong(t) ( (uint)SubstTable[(uint)t&255] | \ + ((uint)SubstTable[(int)(t>> 8)&255]<< 8) | \ + ((uint)SubstTable[(int)(t>>16)&255]<<16) | \ + ((uint)SubstTable[(int)(t>>24)&255]<<24) ) + +CryptKeyCacheItem CryptData::Cache[4]; +int CryptData::CachePos=0; + + +#ifndef SFX_MODULE +static byte InitSubstTable[256]={ + 215, 19,149, 35, 73,197,192,205,249, 28, 16,119, 48,221, 2, 42, + 232, 1,177,233, 14, 88,219, 25,223,195,244, 90, 87,239,153,137, + 255,199,147, 70, 92, 66,246, 13,216, 40, 62, 29,217,230, 86, 6, + 71, 24,171,196,101,113,218,123, 93, 91,163,178,202, 67, 44,235, + 107,250, 75,234, 49,167,125,211, 83,114,157,144, 32,193,143, 36, + 158,124,247,187, 89,214,141, 47,121,228, 61,130,213,194,174,251, + 97,110, 54,229,115, 57,152, 94,105,243,212, 55,209,245, 63, 11, + 164,200, 31,156, 81,176,227, 21, 76, 99,139,188,127, 17,248, 51, + 207,120,189,210, 8,226, 41, 72,183,203,135,165,166, 60, 98, 7, + 122, 38,155,170, 69,172,252,238, 39,134, 59,128,236, 27,240, 80, + 131, 3, 85,206,145, 79,154,142,159,220,201,133, 74, 64, 20,129, + 224,185,138,103,173,182, 43, 34,254, 82,198,151,231,180, 58, 10, + 118, 26,102, 12, 50,132, 22,191,136,111,162,179, 45, 4,148,108, + 161, 56, 78,126,242,222, 15,175,146, 23, 33,241,181,190, 77,225, + 0, 46,169,186, 68, 95,237, 65, 53,208,253,168, 9, 18,100, 52, + 116,184,160, 96,109, 37, 30,106,140,104,150, 5,204,117,112, 84 +}; +#endif + + + +void CryptData::DecryptBlock(byte *Buf,size_t Size) +{ + rin.blockDecrypt(Buf,Size,Buf); +} + + +#ifndef SFX_MODULE +void CryptData::EncryptBlock20(byte *Buf) +{ + uint A,B,C,D,T,TA,TB; +#if defined(BIG_ENDIAN) || !defined(PRESENT_INT32) || !defined(ALLOW_NOT_ALIGNED_INT) + A=((uint)Buf[0]|((uint)Buf[1]<<8)|((uint)Buf[2]<<16)|((uint)Buf[3]<<24))^Key[0]; + B=((uint)Buf[4]|((uint)Buf[5]<<8)|((uint)Buf[6]<<16)|((uint)Buf[7]<<24))^Key[1]; + C=((uint)Buf[8]|((uint)Buf[9]<<8)|((uint)Buf[10]<<16)|((uint)Buf[11]<<24))^Key[2]; + D=((uint)Buf[12]|((uint)Buf[13]<<8)|((uint)Buf[14]<<16)|((uint)Buf[15]<<24))^Key[3]; +#else + uint32 *BufPtr=(uint32 *)Buf; + A=BufPtr[0]^Key[0]; + B=BufPtr[1]^Key[1]; + C=BufPtr[2]^Key[2]; + D=BufPtr[3]^Key[3]; +#endif + for(int I=0;I>8); + Buf[2]=(byte)(C>>16); + Buf[3]=(byte)(C>>24); + D^=Key[1]; + Buf[4]=(byte)D; + Buf[5]=(byte)(D>>8); + Buf[6]=(byte)(D>>16); + Buf[7]=(byte)(D>>24); + A^=Key[2]; + Buf[8]=(byte)A; + Buf[9]=(byte)(A>>8); + Buf[10]=(byte)(A>>16); + Buf[11]=(byte)(A>>24); + B^=Key[3]; + Buf[12]=(byte)B; + Buf[13]=(byte)(B>>8); + Buf[14]=(byte)(B>>16); + Buf[15]=(byte)(B>>24); +#else + BufPtr[0]=C^Key[0]; + BufPtr[1]=D^Key[1]; + BufPtr[2]=A^Key[2]; + BufPtr[3]=B^Key[3]; +#endif + UpdKeys(Buf); +} + + +void CryptData::DecryptBlock20(byte *Buf) +{ + byte InBuf[16]; + uint A,B,C,D,T,TA,TB; +#if defined(BIG_ENDIAN) || !defined(PRESENT_INT32) || !defined(ALLOW_NOT_ALIGNED_INT) + A=((uint)Buf[0]|((uint)Buf[1]<<8)|((uint)Buf[2]<<16)|((uint)Buf[3]<<24))^Key[0]; + B=((uint)Buf[4]|((uint)Buf[5]<<8)|((uint)Buf[6]<<16)|((uint)Buf[7]<<24))^Key[1]; + C=((uint)Buf[8]|((uint)Buf[9]<<8)|((uint)Buf[10]<<16)|((uint)Buf[11]<<24))^Key[2]; + D=((uint)Buf[12]|((uint)Buf[13]<<8)|((uint)Buf[14]<<16)|((uint)Buf[15]<<24))^Key[3]; +#else + uint32 *BufPtr=(uint32 *)Buf; + A=BufPtr[0]^Key[0]; + B=BufPtr[1]^Key[1]; + C=BufPtr[2]^Key[2]; + D=BufPtr[3]^Key[3]; +#endif + memcpy(InBuf,Buf,sizeof(InBuf)); + for(int I=NROUNDS-1;I>=0;I--) + { + T=((C+rol(D,11,32))^Key[I&3]); + TA=A^substLong(T); + T=((D^rol(C,17,32))+Key[I&3]); + TB=B^substLong(T); + A=C; + B=D; + C=TA; + D=TB; + } +#if defined(BIG_ENDIAN) || !defined(PRESENT_INT32) || !defined(ALLOW_NOT_ALIGNED_INT) + C^=Key[0]; + Buf[0]=(byte)C; + Buf[1]=(byte)(C>>8); + Buf[2]=(byte)(C>>16); + Buf[3]=(byte)(C>>24); + D^=Key[1]; + Buf[4]=(byte)D; + Buf[5]=(byte)(D>>8); + Buf[6]=(byte)(D>>16); + Buf[7]=(byte)(D>>24); + A^=Key[2]; + Buf[8]=(byte)A; + Buf[9]=(byte)(A>>8); + Buf[10]=(byte)(A>>16); + Buf[11]=(byte)(A>>24); + B^=Key[3]; + Buf[12]=(byte)B; + Buf[13]=(byte)(B>>8); + Buf[14]=(byte)(B>>16); + Buf[15]=(byte)(B>>24); +#else + BufPtr[0]=C^Key[0]; + BufPtr[1]=D^Key[1]; + BufPtr[2]=A^Key[2]; + BufPtr[3]=B^Key[3]; +#endif + UpdKeys(InBuf); +} + + +void CryptData::UpdKeys(byte *Buf) +{ + for (int I=0;I<16;I+=4) + { + Key[0]^=CRCTab[Buf[I]]; + Key[1]^=CRCTab[Buf[I+1]]; + Key[2]^=CRCTab[Buf[I+2]]; + Key[3]^=CRCTab[Buf[I+3]]; + } +} + + +void CryptData::Swap(byte *Ch1,byte *Ch2) +{ + byte Ch=*Ch1; + *Ch1=*Ch2; + *Ch2=Ch; +} +#endif + + +void CryptData::SetCryptKeys(SecPassword *Password,const byte *Salt,bool Encrypt,bool OldOnly,bool HandsOffHash) +{ + if (!Password->IsSet()) + return; + wchar PlainPsw[MAXPASSWORD]; + Password->Get(PlainPsw,ASIZE(PlainPsw)); + if (OldOnly) + { +#ifndef SFX_MODULE + if (CRCTab[1]==0) + InitCRC(); + char Psw[MAXPASSWORD]; + memset(Psw,0,sizeof(Psw)); + + // We need to use ASCII password for older encryption algorithms. + WideToChar(PlainPsw,Psw,ASIZE(Psw)); + Psw[ASIZE(Psw)-1]=0; + + size_t PswLength=strlen(Psw); + + SetOldKeys(Psw); + Key[0]=0xD3A3B879L; + Key[1]=0x3F6D12F7L; + Key[2]=0x7515A235L; + Key[3]=0xA4E7F123L; + + memcpy(SubstTable,InitSubstTable,sizeof(SubstTable)); + for (int J=0;J<256;J++) + for (size_t I=0;I>8); + PswNum[2]=(byte)(I>>16); + hash_process( &c, PswNum, 3, HandsOffHash); + if (I%(HashRounds/16)==0) + { + hash_context tempc=c; + uint32 digest[5]; + hash_final( &tempc, digest, HandsOffHash); + AESInit[I/(HashRounds/16)]=(byte)digest[4]; + } + } + uint32 digest[5]; + hash_final( &c, digest, HandsOffHash); + for (int I=0;I<4;I++) + for (int J=0;J<4;J++) + AESKey[I*4+J]=(byte)(digest[I]>>(J*8)); + + Cache[CachePos].Password=*Password; + if ((Cache[CachePos].SaltPresent=(Salt!=NULL))==true) + memcpy(Cache[CachePos].Salt,Salt,SALT_SIZE); + Cache[CachePos].HandsOffHash=HandsOffHash; + memcpy(Cache[CachePos].AESKey,AESKey,sizeof(AESKey)); + memcpy(Cache[CachePos].AESInit,AESInit,sizeof(AESInit)); + CachePos=(CachePos+1)%ASIZE(Cache); + + cleandata(RawPsw,sizeof(RawPsw)); + } + rin.init(Encrypt ? Rijndael::Encrypt : Rijndael::Decrypt,AESKey,AESInit); + cleandata(PlainPsw,sizeof(PlainPsw)); +} + + +#ifndef SFX_MODULE +void CryptData::SetOldKeys(const char *Password) +{ + uint PswCRC=CRC(0xffffffff,Password,strlen(Password)); + OldKey[0]=PswCRC&0xffff; + OldKey[1]=(PswCRC>>16)&0xffff; + OldKey[2]=OldKey[3]=0; + PN1=PN2=PN3=0; + byte Ch; + while ((Ch=*Password)!=0) + { + PN1+=Ch; + PN2^=Ch; + PN3+=Ch; + PN3=(byte)rol(PN3,1,8); + OldKey[2]^=Ch^CRCTab[Ch]; + OldKey[3]+=Ch+(CRCTab[Ch]>>16); + Password++; + } +} + + +void CryptData::SetAV15Encryption() +{ + OldKey[0]=0x4765; + OldKey[1]=0x9021; + OldKey[2]=0x7382; + OldKey[3]=0x5215; +} + + +void CryptData::SetCmt13Encryption() +{ + PN1=0; + PN2=7; + PN3=77; +} + + +void CryptData::Crypt(byte *Data,uint Count,int Method) +{ + if (Method==OLD_DECODE) + Decode13(Data,Count); + else + if (Method==OLD_ENCODE) + Encode13(Data,Count); + else + Crypt15(Data,Count); +} + + +void CryptData::Encode13(byte *Data,uint Count) +{ + while (Count--) + { + PN2+=PN3; + PN1+=PN2; + *Data+=PN1; + Data++; + } +} + + +void CryptData::Decode13(byte *Data,uint Count) +{ + while (Count--) + { + PN2+=PN3; + PN1+=PN2; + *Data-=PN1; + Data++; + } +} + + +void CryptData::Crypt15(byte *Data,uint Count) +{ + while (Count--) + { + OldKey[0]+=0x1234; + OldKey[1]^=CRCTab[(OldKey[0] & 0x1fe)>>1]; + OldKey[2]-=CRCTab[(OldKey[0] & 0x1fe)>>1]>>16; + OldKey[0]^=OldKey[2]; + OldKey[3]=ror(OldKey[3]&0xffff,1,16)^OldKey[1]; + OldKey[3]=ror(OldKey[3]&0xffff,1,16); + OldKey[0]^=OldKey[3]; + *Data^=(byte)(OldKey[0]>>8); + Data++; + } +} +#endif + + diff --git a/src/unrar/crypt.hpp b/src/unrar/crypt.hpp new file mode 100644 index 0000000000..96b8a27f13 --- /dev/null +++ b/src/unrar/crypt.hpp @@ -0,0 +1,62 @@ +#ifndef _RAR_CRYPT_ +#define _RAR_CRYPT_ + +enum { OLD_DECODE=0,OLD_ENCODE=1,NEW_CRYPT=2 }; + + +struct CryptKeyCacheItem +{ +#ifndef _SFX_RTL_ + CryptKeyCacheItem() + { + Password.Set(L""); + } + + ~CryptKeyCacheItem() + { + memset(AESKey,0,sizeof(AESKey)); + memset(AESInit,0,sizeof(AESInit)); + memset(&Password,0,sizeof(Password)); + } +#endif + byte AESKey[16],AESInit[16]; + SecPassword Password; + bool SaltPresent; + byte Salt[SALT_SIZE]; + bool HandsOffHash; +}; + +class CryptData +{ + private: + void Encode13(byte *Data,uint Count); + void Decode13(byte *Data,uint Count); + void Crypt15(byte *Data,uint Count); + void UpdKeys(byte *Buf); + void Swap(byte *Ch1,byte *Ch2); + void SetOldKeys(const char *Password); + + Rijndael rin; + + byte SubstTable[256]; + uint Key[4]; + ushort OldKey[4]; + byte PN1,PN2,PN3; + + byte AESKey[16],AESInit[16]; + + static CryptKeyCacheItem Cache[4]; + static int CachePos; + public: + void SetCryptKeys(SecPassword *Password,const byte *Salt,bool Encrypt,bool OldOnly,bool HandsOffHash); + void SetAV15Encryption(); + void SetCmt13Encryption(); + void EncryptBlock20(byte *Buf); + void DecryptBlock20(byte *Buf); + void EncryptBlock(byte *Buf,size_t Size); + void DecryptBlock(byte *Buf,size_t Size); + void Crypt(byte *Data,uint Count,int Method); + static void SetSalt(byte *Salt,int SaltSize); +}; + +#endif diff --git a/src/unrar/dll.cpp b/src/unrar/dll.cpp new file mode 100644 index 0000000000..535656caf5 --- /dev/null +++ b/src/unrar/dll.cpp @@ -0,0 +1,375 @@ +#include "rar.hpp" +#include "dll.hpp" + +static int RarErrorToDll(RAR_EXIT ErrCode); + +struct DataSet +{ + CommandData Cmd; + CmdExtract Extract; + Archive Arc; + int OpenMode; + int HeaderSize; + + DataSet():Arc(&Cmd) {}; +}; + + +HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *r) +{ + RAROpenArchiveDataEx rx; + memset(&rx,0,sizeof(rx)); + rx.ArcName=r->ArcName; + rx.OpenMode=r->OpenMode; + rx.CmtBuf=r->CmtBuf; + rx.CmtBufSize=r->CmtBufSize; + HANDLE hArc=RAROpenArchiveEx(&rx); + r->OpenResult=rx.OpenResult; + r->CmtSize=rx.CmtSize; + r->CmtState=rx.CmtState; + return(hArc); +} + + +HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r) +{ + DataSet *Data=NULL; + try + { + r->OpenResult=0; + Data=new DataSet; + Data->Cmd.DllError=0; + Data->OpenMode=r->OpenMode; + Data->Cmd.FileArgs->AddString("*"); + + char an[NM]; + if (r->ArcName==NULL && r->ArcNameW!=NULL) + { + WideToChar(r->ArcNameW,an,NM); + r->ArcName=an; + } + + Data->Cmd.AddArcName(r->ArcName,r->ArcNameW); + Data->Cmd.Overwrite=OVERWRITE_ALL; + Data->Cmd.VersionControl=1; + + Data->Cmd.Callback=r->Callback; + Data->Cmd.UserData=r->UserData; + + if (!Data->Arc.Open(r->ArcName,r->ArcNameW,0)) + { + r->OpenResult=ERAR_EOPEN; + delete Data; + return(NULL); + } + if (!Data->Arc.IsArchive(false)) + { + r->OpenResult=Data->Cmd.DllError!=0 ? Data->Cmd.DllError:ERAR_BAD_ARCHIVE; + delete Data; + return(NULL); + } + r->Flags=Data->Arc.NewMhd.Flags; + Array CmtData; + if (r->CmtBufSize!=0 && Data->Arc.GetComment(&CmtData,NULL)) + { + r->Flags|=2; + size_t Size=CmtData.Size()+1; + r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1; + r->CmtSize=(uint)Min(Size,r->CmtBufSize); + memcpy(r->CmtBuf,&CmtData[0],r->CmtSize-1); + if (Size<=r->CmtBufSize) + r->CmtBuf[r->CmtSize-1]=0; + } + else + r->CmtState=r->CmtSize=0; + if (Data->Arc.Signed) + r->Flags|=0x20; + Data->Extract.ExtractArchiveInit(&Data->Cmd,Data->Arc); + return((HANDLE)Data); + } + catch (RAR_EXIT ErrCode) + { + if (Data!=NULL && Data->Cmd.DllError!=0) + r->OpenResult=Data->Cmd.DllError; + else + r->OpenResult=RarErrorToDll(ErrCode); + if (Data != NULL) + delete Data; + return(NULL); + } + catch (std::bad_alloc) // Catch 'new' exception. + { + r->OpenResult=ERAR_NO_MEMORY; + if (Data != NULL) + delete Data; + } +} + + +int PASCAL RARCloseArchive(HANDLE hArcData) +{ + DataSet *Data=(DataSet *)hArcData; + bool Success=Data==NULL ? false:Data->Arc.Close(); + delete Data; + return(Success ? 0:ERAR_ECLOSE); +} + + +int PASCAL RARReadHeader(HANDLE hArcData,struct RARHeaderData *D) +{ + struct RARHeaderDataEx X; + memset(&X,0,sizeof(X)); + + int Code=RARReadHeaderEx(hArcData,&X); + + strncpyz(D->ArcName,X.ArcName,ASIZE(D->ArcName)); + strncpyz(D->FileName,X.FileName,ASIZE(D->FileName)); + D->Flags=X.Flags; + D->PackSize=X.PackSize; + D->UnpSize=X.UnpSize; + D->HostOS=X.HostOS; + D->FileCRC=X.FileCRC; + D->FileTime=X.FileTime; + D->UnpVer=X.UnpVer; + D->Method=X.Method; + D->FileAttr=X.FileAttr; + D->CmtSize=0; + D->CmtState=0; + + return Code; +} + + +int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *D) +{ + DataSet *Data=(DataSet *)hArcData; + try + { + if ((Data->HeaderSize=(int)Data->Arc.SearchBlock(FILE_HEAD))<=0) + { + if (Data->Arc.Volume && Data->Arc.GetHeaderType()==ENDARC_HEAD && + (Data->Arc.EndArcHead.Flags & EARC_NEXT_VOLUME)) + if (MergeArchive(Data->Arc,NULL,false,'L')) + { + Data->Extract.SignatureFound=false; + Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET); + return(RARReadHeaderEx(hArcData,D)); + } + else + return(ERAR_EOPEN); + return(Data->Arc.BrokenFileHeader ? ERAR_BAD_DATA:ERAR_END_ARCHIVE); + } + if (Data->OpenMode==RAR_OM_LIST && (Data->Arc.NewLhd.Flags & LHD_SPLIT_BEFORE)!=0) + { + int Code=RARProcessFile(hArcData,RAR_SKIP,NULL,NULL); + if (Code==0) + return(RARReadHeaderEx(hArcData,D)); + else + return(Code); + } + strncpyz(D->ArcName,Data->Arc.FileName,ASIZE(D->ArcName)); + if (*Data->Arc.FileNameW) + wcsncpy(D->ArcNameW,Data->Arc.FileNameW,ASIZE(D->ArcNameW)); + else + CharToWide(Data->Arc.FileName,D->ArcNameW); + strncpyz(D->FileName,Data->Arc.NewLhd.FileName,ASIZE(D->FileName)); + if (*Data->Arc.NewLhd.FileNameW) + wcsncpy(D->FileNameW,Data->Arc.NewLhd.FileNameW,ASIZE(D->FileNameW)); + else + { +#ifdef _WIN_ALL + char AnsiName[NM]; + OemToCharA(Data->Arc.NewLhd.FileName,AnsiName); + if (!CharToWide(AnsiName,D->FileNameW,ASIZE(D->FileNameW))) + *D->FileNameW=0; +#else + if (!CharToWide(Data->Arc.NewLhd.FileName,D->FileNameW,ASIZE(D->FileNameW))) + *D->FileNameW=0; +#endif + } + D->Flags=Data->Arc.NewLhd.Flags; + D->PackSize=Data->Arc.NewLhd.PackSize; + D->PackSizeHigh=Data->Arc.NewLhd.HighPackSize; + D->UnpSize=Data->Arc.NewLhd.UnpSize; + D->UnpSizeHigh=Data->Arc.NewLhd.HighUnpSize; + D->HostOS=Data->Arc.NewLhd.HostOS; + D->FileCRC=Data->Arc.NewLhd.FileCRC; + D->FileTime=Data->Arc.NewLhd.FileTime; + D->UnpVer=Data->Arc.NewLhd.UnpVer; + D->Method=Data->Arc.NewLhd.Method; + D->FileAttr=Data->Arc.NewLhd.FileAttr; + D->CmtSize=0; + D->CmtState=0; + } + catch (RAR_EXIT ErrCode) + { + return(Data->Cmd.DllError!=0 ? Data->Cmd.DllError:RarErrorToDll(ErrCode)); + } + return(0); +} + + +int PASCAL ProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName,wchar *DestPathW,wchar *DestNameW) +{ + DataSet *Data=(DataSet *)hArcData; + try + { + Data->Cmd.DllError=0; + if (Data->OpenMode==RAR_OM_LIST || Data->OpenMode==RAR_OM_LIST_INCSPLIT || + Operation==RAR_SKIP && !Data->Arc.Solid) + { + if (Data->Arc.Volume && + Data->Arc.GetHeaderType()==FILE_HEAD && + (Data->Arc.NewLhd.Flags & LHD_SPLIT_AFTER)!=0) + if (MergeArchive(Data->Arc,NULL,false,'L')) + { + Data->Extract.SignatureFound=false; + Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET); + return(0); + } + else + return(ERAR_EOPEN); + Data->Arc.SeekToNext(); + } + else + { + Data->Cmd.DllOpMode=Operation; + + if (DestPath!=NULL || DestName!=NULL) + { +#ifdef _WIN_ALL + OemToCharA(NullToEmpty(DestPath),Data->Cmd.ExtrPath); +#else + strcpy(Data->Cmd.ExtrPath,NullToEmpty(DestPath)); +#endif + AddEndSlash(Data->Cmd.ExtrPath); +#ifdef _WIN_ALL + OemToCharA(NullToEmpty(DestName),Data->Cmd.DllDestName); +#else + strcpy(Data->Cmd.DllDestName,NullToEmpty(DestName)); +#endif + } + else + { + *Data->Cmd.ExtrPath=0; + *Data->Cmd.DllDestName=0; + } + + if (DestPathW!=NULL || DestNameW!=NULL) + { + wcsncpy(Data->Cmd.ExtrPathW,NullToEmpty(DestPathW),NM-2); + AddEndSlash(Data->Cmd.ExtrPathW); + wcsncpy(Data->Cmd.DllDestNameW,NullToEmpty(DestNameW),NM-1); + + if (*Data->Cmd.DllDestNameW!=0 && *Data->Cmd.DllDestName==0) + WideToChar(Data->Cmd.DllDestNameW,Data->Cmd.DllDestName); + } + else + { + *Data->Cmd.ExtrPathW=0; + *Data->Cmd.DllDestNameW=0; + } + + strcpy(Data->Cmd.Command,Operation==RAR_EXTRACT ? "X":"T"); + Data->Cmd.Test=Operation!=RAR_EXTRACT; + bool Repeat=false; + Data->Extract.ExtractCurrentFile(&Data->Cmd,Data->Arc,Data->HeaderSize,Repeat); + + // Now we process extra file information if any. + // + // Archive can be closed if we process volumes, next volume is missing + // and current one is already removed or deleted. So we need to check + // if archive is still open to avoid calling file operations on + // the invalid file handle. Some of our file operations like Seek() + // process such invalid handle correctly, some not. + while (Data->Arc.IsOpened() && Data->Arc.ReadHeader()!=0 && + Data->Arc.GetHeaderType()==NEWSUB_HEAD) + { + Data->Extract.ExtractCurrentFile(&Data->Cmd,Data->Arc,Data->HeaderSize,Repeat); + Data->Arc.SeekToNext(); + } + Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET); + } + } + catch (RAR_EXIT ErrCode) + { + return(Data->Cmd.DllError!=0 ? Data->Cmd.DllError:RarErrorToDll(ErrCode)); + } + return(Data->Cmd.DllError); +} + + +int PASCAL RARProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName) +{ + return(ProcessFile(hArcData,Operation,DestPath,DestName,NULL,NULL)); +} + + +int PASCAL RARProcessFileW(HANDLE hArcData,int Operation,wchar *DestPath,wchar *DestName) +{ + return(ProcessFile(hArcData,Operation,NULL,NULL,DestPath,DestName)); +} + + +void PASCAL RARSetChangeVolProc(HANDLE hArcData,CHANGEVOLPROC ChangeVolProc) +{ + DataSet *Data=(DataSet *)hArcData; + Data->Cmd.ChangeVolProc=ChangeVolProc; +} + + +void PASCAL RARSetCallback(HANDLE hArcData,UNRARCALLBACK Callback,LPARAM UserData) +{ + DataSet *Data=(DataSet *)hArcData; + Data->Cmd.Callback=Callback; + Data->Cmd.UserData=UserData; +} + + +void PASCAL RARSetProcessDataProc(HANDLE hArcData,PROCESSDATAPROC ProcessDataProc) +{ + DataSet *Data=(DataSet *)hArcData; + Data->Cmd.ProcessDataProc=ProcessDataProc; +} + + +#ifndef RAR_NOCRYPT +void PASCAL RARSetPassword(HANDLE hArcData,char *Password) +{ + DataSet *Data=(DataSet *)hArcData; + wchar PasswordW[MAXPASSWORD]; + GetWideName(Password,NULL,PasswordW,ASIZE(PasswordW)); + Data->Cmd.Password.Set(PasswordW); + cleandata(PasswordW,sizeof(PasswordW)); +} +#endif + + +int PASCAL RARGetDllVersion() +{ + return(RAR_DLL_VERSION); +} + + +static int RarErrorToDll(RAR_EXIT ErrCode) +{ + switch(ErrCode) + { + case RARX_FATAL: + return(ERAR_EREAD); + case RARX_CRC: + return(ERAR_BAD_DATA); + case RARX_WRITE: + return(ERAR_EWRITE); + case RARX_OPEN: + return(ERAR_EOPEN); + case RARX_CREATE: + return(ERAR_ECREATE); + case RARX_MEMORY: + return(ERAR_NO_MEMORY); + case RARX_SUCCESS: + return(0); + default: + return(ERAR_UNKNOWN); + } +} diff --git a/src/unrar/dll.def b/src/unrar/dll.def new file mode 100644 index 0000000000..660f69bf6e --- /dev/null +++ b/src/unrar/dll.def @@ -0,0 +1,12 @@ +EXPORTS + RAROpenArchive + RAROpenArchiveEx + RARCloseArchive + RARReadHeader + RARReadHeaderEx + RARProcessFile + RARSetCallback + RARSetChangeVolProc + RARSetProcessDataProc + RARSetPassword + RARGetDllVersion diff --git a/src/unrar/dll.hpp b/src/unrar/dll.hpp new file mode 100644 index 0000000000..1d929becec --- /dev/null +++ b/src/unrar/dll.hpp @@ -0,0 +1,147 @@ +#ifndef _UNRAR_DLL_ +#define _UNRAR_DLL_ + +#pragma pack(1) + +#define ERAR_END_ARCHIVE 10 +#define ERAR_NO_MEMORY 11 +#define ERAR_BAD_DATA 12 +#define ERAR_BAD_ARCHIVE 13 +#define ERAR_UNKNOWN_FORMAT 14 +#define ERAR_EOPEN 15 +#define ERAR_ECREATE 16 +#define ERAR_ECLOSE 17 +#define ERAR_EREAD 18 +#define ERAR_EWRITE 19 +#define ERAR_SMALL_BUF 20 +#define ERAR_UNKNOWN 21 +#define ERAR_MISSING_PASSWORD 22 + +#define RAR_OM_LIST 0 +#define RAR_OM_EXTRACT 1 +#define RAR_OM_LIST_INCSPLIT 2 + +#define RAR_SKIP 0 +#define RAR_TEST 1 +#define RAR_EXTRACT 2 + +#define RAR_VOL_ASK 0 +#define RAR_VOL_NOTIFY 1 + +#define RAR_DLL_VERSION 5 + +#ifdef _UNIX +#define CALLBACK +#define PASCAL +#define LONG long +#define HANDLE void * +#define LPARAM long +#define UINT unsigned int +#endif + +struct RARHeaderData +{ + char ArcName[260]; + char FileName[260]; + unsigned int Flags; + unsigned int PackSize; + unsigned int UnpSize; + unsigned int HostOS; + unsigned int FileCRC; + unsigned int FileTime; + unsigned int UnpVer; + unsigned int Method; + unsigned int FileAttr; + char *CmtBuf; + unsigned int CmtBufSize; + unsigned int CmtSize; + unsigned int CmtState; +}; + + +struct RARHeaderDataEx +{ + char ArcName[1024]; + wchar_t ArcNameW[1024]; + char FileName[1024]; + wchar_t FileNameW[1024]; + unsigned int Flags; + unsigned int PackSize; + unsigned int PackSizeHigh; + unsigned int UnpSize; + unsigned int UnpSizeHigh; + unsigned int HostOS; + unsigned int FileCRC; + unsigned int FileTime; + unsigned int UnpVer; + unsigned int Method; + unsigned int FileAttr; + char *CmtBuf; + unsigned int CmtBufSize; + unsigned int CmtSize; + unsigned int CmtState; + unsigned int Reserved[1024]; +}; + + +struct RAROpenArchiveData +{ + char *ArcName; + unsigned int OpenMode; + unsigned int OpenResult; + char *CmtBuf; + unsigned int CmtBufSize; + unsigned int CmtSize; + unsigned int CmtState; +}; + +typedef int (CALLBACK *UNRARCALLBACK)(UINT msg,LPARAM UserData,LPARAM P1,LPARAM P2); + +struct RAROpenArchiveDataEx +{ + char *ArcName; + wchar_t *ArcNameW; + unsigned int OpenMode; + unsigned int OpenResult; + char *CmtBuf; + unsigned int CmtBufSize; + unsigned int CmtSize; + unsigned int CmtState; + unsigned int Flags; + UNRARCALLBACK Callback; + LPARAM UserData; + unsigned int Reserved[28]; +}; + +enum UNRARCALLBACK_MESSAGES { + UCM_CHANGEVOLUME,UCM_PROCESSDATA,UCM_NEEDPASSWORD,UCM_CHANGEVOLUMEW, + UCM_NEEDPASSWORDW +}; + +typedef int (PASCAL *CHANGEVOLPROC)(char *ArcName,int Mode); +typedef int (PASCAL *PROCESSDATAPROC)(unsigned char *Addr,int Size); + +#ifdef __cplusplus +extern "C" { +#endif + +HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *ArchiveData); +HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *ArchiveData); +int PASCAL RARCloseArchive(HANDLE hArcData); +int PASCAL RARReadHeader(HANDLE hArcData,struct RARHeaderData *HeaderData); +int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *HeaderData); +int PASCAL RARProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName); +int PASCAL RARProcessFileW(HANDLE hArcData,int Operation,wchar_t *DestPath,wchar_t *DestName); +void PASCAL RARSetCallback(HANDLE hArcData,UNRARCALLBACK Callback,LPARAM UserData); +void PASCAL RARSetChangeVolProc(HANDLE hArcData,CHANGEVOLPROC ChangeVolProc); +void PASCAL RARSetProcessDataProc(HANDLE hArcData,PROCESSDATAPROC ProcessDataProc); +void PASCAL RARSetPassword(HANDLE hArcData,char *Password); +int PASCAL RARGetDllVersion(); + +#ifdef __cplusplus +} +#endif + +#pragma pack() + +#endif diff --git a/src/unrar/dll.rc b/src/unrar/dll.rc new file mode 100644 index 0000000000..7a93039ad5 --- /dev/null +++ b/src/unrar/dll.rc @@ -0,0 +1,28 @@ +#include +#include + +VS_VERSION_INFO VERSIONINFO +FILEVERSION 4, 20, 100, 526 +PRODUCTVERSION 4, 20, 100, 526 +FILEOS VOS__WINDOWS32 +FILETYPE VFT_APP +{ + BLOCK "StringFileInfo" + { + BLOCK "040904E4" + { + VALUE "CompanyName", "Alexander Roshal\0" + VALUE "ProductName", "RAR decompression library\0" + VALUE "FileDescription", "RAR decompression library\0" + VALUE "FileVersion", "4.20.0\0" + VALUE "ProductVersion", "4.20.0\0" + VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2012\0" + VALUE "OriginalFilename", "Unrar.dll\0" + } + } + BLOCK "VarFileInfo" + { + VALUE "Translation", 0, 0 + } +} + diff --git a/src/unrar/encname.cpp b/src/unrar/encname.cpp new file mode 100644 index 0000000000..4e42a759bf --- /dev/null +++ b/src/unrar/encname.cpp @@ -0,0 +1,57 @@ +#include "rar.hpp" + +EncodeFileName::EncodeFileName() +{ + Flags=0; + FlagBits=0; + FlagsPos=0; + DestSize=0; +} + + + + +void EncodeFileName::Decode(char *Name,byte *EncName,size_t EncSize,wchar *NameW, + size_t MaxDecSize) +{ + size_t EncPos=0,DecPos=0; + byte HighByte=EncName[EncPos++]; + while (EncPos>6) + { + case 0: + NameW[DecPos++]=EncName[EncPos++]; + break; + case 1: + NameW[DecPos++]=EncName[EncPos++]+(HighByte<<8); + break; + case 2: + NameW[DecPos++]=EncName[EncPos]+(EncName[EncPos+1]<<8); + EncPos+=2; + break; + case 3: + { + int Length=EncName[EncPos++]; + if (Length & 0x80) + { + byte Correction=EncName[EncPos++]; + for (Length=(Length&0x7f)+2;Length>0 && DecPos0 && DecPosMAX_PATH) + { + Log(NULL,St(MMaxPathLimit),MAX_PATH); + } + } +#endif +} + + +void ErrorHandler::ReadErrorMsg(const char *ArcName,const wchar *ArcNameW,const char *FileName,const wchar *FileNameW) +{ +#ifndef SILENT + ErrMsg(ArcName,St(MErrRead),FileName); + SysErrMsg(); +#endif +} + + +void ErrorHandler::WriteErrorMsg(const char *ArcName,const wchar *ArcNameW,const char *FileName,const wchar *FileNameW) +{ +#ifndef SILENT + ErrMsg(ArcName,St(MErrWrite),FileName); + SysErrMsg(); +#endif +} + + +void ErrorHandler::Exit(RAR_EXIT ExitCode) +{ +#ifndef SFX_MODULE + Alarm(); +#endif + Throw(ExitCode); +} + + +#ifndef GUI +void ErrorHandler::ErrMsg(const char *ArcName,const char *fmt,...) +{ + safebuf char Msg[NM+1024]; + va_list argptr; + va_start(argptr,fmt); + vsprintf(Msg,fmt,argptr); + va_end(argptr); +#ifdef _WIN_ALL + if (UserBreak) + Sleep(5000); +#endif + Alarm(); + if (*Msg) + { + Log(ArcName,"\n%s",Msg); + mprintf("\n%s\n",St(MProgAborted)); + } +} +#endif + + +void ErrorHandler::SetErrorCode(RAR_EXIT Code) +{ + switch(Code) + { + case RARX_WARNING: + case RARX_USERBREAK: + if (ExitCode==RARX_SUCCESS) + ExitCode=Code; + break; + case RARX_FATAL: + if (ExitCode==RARX_SUCCESS || ExitCode==RARX_WARNING) + ExitCode=RARX_FATAL; + break; + default: + ExitCode=Code; + break; + } + ErrCount++; +} + + +#if !defined(GUI) && !defined(_SFX_RTL_) +#ifdef _WIN_ALL +BOOL __stdcall ProcessSignal(DWORD SigType) +#else +#if defined(__sun) +extern "C" +#endif +void _stdfunction ProcessSignal(int SigType) +#endif +{ +#ifdef _WIN_ALL + // When a console application is run as a service, this allows the service + // to continue running after the user logs off. + if (SigType==CTRL_LOGOFF_EVENT) + return(TRUE); +#endif + UserBreak=true; + mprintf(St(MBreak)); + for (int I=0;!File::RemoveCreated() && I<3;I++) + { +#ifdef _WIN_ALL + Sleep(100); +#endif + } +#if defined(USE_RC) && !defined(SFX_MODULE) && !defined(_WIN_CE) && !defined(RARDLL) + ExtRes.UnloadDLL(); +#endif + exit(RARX_USERBREAK); +#if defined(_WIN_ALL) && !defined(_MSC_VER) + // never reached, just to avoid a compiler warning + return(TRUE); +#endif +} +#endif + + +void ErrorHandler::SetSignalHandlers(bool Enable) +{ + EnableBreak=Enable; +#if !defined(GUI) && !defined(_SFX_RTL_) +#ifdef _WIN_ALL + SetConsoleCtrlHandler(Enable ? ProcessSignal:NULL,TRUE); +// signal(SIGBREAK,Enable ? ProcessSignal:SIG_IGN); +#else + signal(SIGINT,Enable ? ProcessSignal:SIG_IGN); + signal(SIGTERM,Enable ? ProcessSignal:SIG_IGN); +#endif +#endif +} + + +void ErrorHandler::Throw(RAR_EXIT Code) +{ + if (Code==RARX_USERBREAK && !EnableBreak) + return; + ErrHandler.SetErrorCode(Code); +#ifdef ALLOW_EXCEPTIONS + throw Code; +#else + File::RemoveCreated(); + exit(Code); +#endif +} + + +void ErrorHandler::SysErrMsg() +{ +#if !defined(SFX_MODULE) && !defined(SILENT) +#ifdef _WIN_ALL + wchar *lpMsgBuf=NULL; + int ErrType=GetLastError(); + if (ErrType!=0 && FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, + NULL,ErrType,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&lpMsgBuf,0,NULL)) + { + wchar *CurMsg=lpMsgBuf; + while (CurMsg!=NULL) + { + while (*CurMsg=='\r' || *CurMsg=='\n') + CurMsg++; + if (*CurMsg==0) + break; + wchar *EndMsg=wcschr(CurMsg,'\r'); + if (EndMsg==NULL) + EndMsg=wcschr(CurMsg,'\n'); + if (EndMsg!=NULL) + { + *EndMsg=0; + EndMsg++; + } + // We use ASCII for output in Windows console, so let's convert Unicode + // message to single byte. + size_t Length=wcslen(CurMsg)*2; // Must be enough for DBCS characters. + char *MsgA=(char *)malloc(Length+2); + if (MsgA!=NULL) + { + WideToChar(CurMsg,MsgA,Length+1); + MsgA[Length]=0; + Log(NULL,"\n%s",MsgA); + free(MsgA); + } + CurMsg=EndMsg; + } + } + LocalFree( lpMsgBuf ); +#endif + +#if defined(_UNIX) || defined(_EMX) + if (errno!=0) + { + char *err=strerror(errno); + if (err!=NULL) + Log(NULL,"\n%s",err); + } +#endif + +#endif +} + + + + diff --git a/src/unrar/errhnd.hpp b/src/unrar/errhnd.hpp new file mode 100644 index 0000000000..ab84e0b546 --- /dev/null +++ b/src/unrar/errhnd.hpp @@ -0,0 +1,67 @@ +#ifndef _RAR_ERRHANDLER_ +#define _RAR_ERRHANDLER_ + +#ifndef SFX_MODULE +#define ALLOW_EXCEPTIONS +#endif + +enum RAR_EXIT // RAR exit code. +{ + RARX_SUCCESS = 0, + RARX_WARNING = 1, + RARX_FATAL = 2, + RARX_CRC = 3, + RARX_LOCK = 4, + RARX_WRITE = 5, + RARX_OPEN = 6, + RARX_USERERROR = 7, + RARX_MEMORY = 8, + RARX_CREATE = 9, + RARX_NOFILES = 10, + RARX_USERBREAK = 255 +}; + +class ErrorHandler +{ + private: + void ErrMsg(const char *ArcName,const char *fmt,...); + + RAR_EXIT ExitCode; + int ErrCount; + bool EnableBreak; + bool Silent; + bool DoShutdown; + public: + ErrorHandler(); + void Clean(); + void MemoryError(); + void OpenError(const char *FileName,const wchar *FileNameW); + void CloseError(const char *FileName,const wchar *FileNameW); + void ReadError(const char *FileName,const wchar *FileNameW); + bool AskRepeatRead(const char *FileName,const wchar *FileNameW); + void WriteError(const char *ArcName,const wchar *ArcNameW,const char *FileName,const wchar *FileNameW); + void WriteErrorFAT(const char *FileName,const wchar *FileNameW); + bool AskRepeatWrite(const char *FileName,const wchar *FileNameW,bool DiskFull); + void SeekError(const char *FileName,const wchar *FileNameW); + void GeneralErrMsg(const char *Msg); + void MemoryErrorMsg(); + void OpenErrorMsg(const char *FileName,const wchar *FileNameW=NULL); + void OpenErrorMsg(const char *ArcName,const wchar *ArcNameW,const char *FileName,const wchar *FileNameW); + void CreateErrorMsg(const char *FileName,const wchar *FileNameW=NULL); + void CreateErrorMsg(const char *ArcName,const wchar *ArcNameW,const char *FileName,const wchar *FileNameW); + void CheckLongPathErrMsg(const char *FileName,const wchar *FileNameW); + void ReadErrorMsg(const char *ArcName,const wchar *ArcNameW,const char *FileName,const wchar *FileNameW); + void WriteErrorMsg(const char *ArcName,const wchar *ArcNameW,const char *FileName,const wchar *FileNameW); + void Exit(RAR_EXIT ExitCode); + void SetErrorCode(RAR_EXIT Code); + RAR_EXIT GetErrorCode() {return(ExitCode);} + int GetErrorCount() {return(ErrCount);} + void SetSignalHandlers(bool Enable); + void Throw(RAR_EXIT Code); + void SetSilent(bool Mode) {Silent=Mode;}; + void SetShutdown(bool Mode) {DoShutdown=Mode;}; + void SysErrMsg(); +}; + + +#endif diff --git a/src/unrar/extinfo.cpp b/src/unrar/extinfo.cpp new file mode 100644 index 0000000000..538f6a7f36 --- /dev/null +++ b/src/unrar/extinfo.cpp @@ -0,0 +1,76 @@ +#include "rar.hpp" + +#ifdef _WIN_ALL +#include "win32acl.cpp" +#include "win32stm.cpp" +#endif +#ifdef _BEOS +#include "beosea.cpp" +#endif +#if defined(_EMX) && !defined(_DJGPP) +#include "os2ea.cpp" +#endif +#ifdef _UNIX +#include "uowners.cpp" +#endif + + + +#ifndef SFX_MODULE +void SetExtraInfo(CommandData *Cmd,Archive &Arc,char *Name,wchar *NameW) +{ + switch(Arc.SubBlockHead.SubType) + { +#if defined(_EMX) && !defined(_DJGPP) + case EA_HEAD: + if (Cmd->ProcessEA) + ExtractOS2EA(Arc,Name); + break; +#endif +#ifdef _UNIX + case UO_HEAD: + if (Cmd->ProcessOwners) + ExtractUnixOwner(Arc,Name); + break; +#endif +#ifdef _BEOS + case BEEA_HEAD: + if (Cmd->ProcessEA) + ExtractBeEA(Arc,Name); + break; +#endif +#ifdef _WIN_ALL + case NTACL_HEAD: + if (Cmd->ProcessOwners) + ExtractACL(Arc,Name,NameW); + break; + case STREAM_HEAD: + ExtractStreams(Arc,Name,NameW); + break; +#endif + } +} +#endif + + +void SetExtraInfoNew(CommandData *Cmd,Archive &Arc,char *Name,wchar *NameW) +{ +#if defined(_EMX) && !defined(_DJGPP) + if (Cmd->ProcessEA && Arc.SubHead.CmpName(SUBHEAD_TYPE_OS2EA)) + ExtractOS2EANew(Arc,Name); +#endif +#ifdef _UNIX + if (Cmd->ProcessOwners && Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER)) + ExtractUnixOwnerNew(Arc,Name); +#endif +#ifdef _BEOS + if (Cmd->ProcessEA && Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER)) + ExtractUnixOwnerNew(Arc,Name); +#endif +#ifdef _WIN_ALL + if (Cmd->ProcessOwners && Arc.SubHead.CmpName(SUBHEAD_TYPE_ACL)) + ExtractACLNew(Arc,Name,NameW); + if (Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM)) + ExtractStreamsNew(Arc,Name,NameW); +#endif +} diff --git a/src/unrar/extinfo.hpp b/src/unrar/extinfo.hpp new file mode 100644 index 0000000000..db7cea53f8 --- /dev/null +++ b/src/unrar/extinfo.hpp @@ -0,0 +1,8 @@ +#ifndef _RAR_EXTINFO_ +#define _RAR_EXTINFO_ + + +void SetExtraInfo(CommandData *Cmd,Archive &Arc,char *Name,wchar *NameW); +void SetExtraInfoNew(CommandData *Cmd,Archive &Arc,char *Name,wchar *NameW); + +#endif diff --git a/src/unrar/extract.cpp b/src/unrar/extract.cpp new file mode 100644 index 0000000000..ddd71a0386 --- /dev/null +++ b/src/unrar/extract.cpp @@ -0,0 +1,1026 @@ +#include "rar.hpp" + +CmdExtract::CmdExtract() +{ + *ArcName=0; + *ArcNameW=0; + + *DestFileName=0; + *DestFileNameW=0; + + TotalFileCount=0; + Password.Set(L""); + Unp=new Unpack(&DataIO); + Unp->Init(); +} + + +CmdExtract::~CmdExtract() +{ + delete Unp; +} + + +void CmdExtract::DoExtract(CommandData *Cmd) +{ + PasswordCancelled=false; + DataIO.SetCurrentCommand(*Cmd->Command); + + FindData FD; + while (Cmd->GetArcName(ArcName,ArcNameW,ASIZE(ArcName))) + if (FindFile::FastFind(ArcName,ArcNameW,&FD)) + DataIO.TotalArcSize+=FD.Size; + + Cmd->ArcNames->Rewind(); + while (Cmd->GetArcName(ArcName,ArcNameW,ASIZE(ArcName))) + { + while (true) + { + SecPassword PrevCmdPassword; + PrevCmdPassword=Cmd->Password; + + EXTRACT_ARC_CODE Code=ExtractArchive(Cmd); + + // Restore Cmd->Password, which could be changed in IsArchive() call + // for next header encrypted archive. + Cmd->Password=PrevCmdPassword; + + if (Code!=EXTRACT_ARC_REPEAT) + break; + } + if (FindFile::FastFind(ArcName,ArcNameW,&FD)) + DataIO.ProcessedArcSize+=FD.Size; + } + + if (TotalFileCount==0 && *Cmd->Command!='I') + { + if (!PasswordCancelled) + { + mprintf(St(MExtrNoFiles)); + } + ErrHandler.SetErrorCode(RARX_NOFILES); + } +#ifndef GUI + else + if (!Cmd->DisableDone) + if (*Cmd->Command=='I') + mprintf(St(MDone)); + else + if (ErrHandler.GetErrorCount()==0) + mprintf(St(MExtrAllOk)); + else + mprintf(St(MExtrTotalErr),ErrHandler.GetErrorCount()); +#endif +} + + +void CmdExtract::ExtractArchiveInit(CommandData *Cmd,Archive &Arc) +{ + DataIO.UnpArcSize=Arc.FileLength(); + + FileCount=0; + MatchedArgs=0; +#ifndef SFX_MODULE + FirstFile=true; +#endif + + PasswordAll=(Cmd->Password.IsSet()); + if (PasswordAll) + Password=Cmd->Password; + + DataIO.UnpVolume=false; + + PrevExtracted=false; + SignatureFound=false; + AllMatchesExact=true; + ReconstructDone=false; + AnySolidDataUnpackedWell=false; + + StartTime.SetCurrentTime(); +} + + +EXTRACT_ARC_CODE CmdExtract::ExtractArchive(CommandData *Cmd) +{ + Archive Arc(Cmd); + if (!Arc.WOpen(ArcName,ArcNameW)) + { + ErrHandler.SetErrorCode(RARX_OPEN); + return(EXTRACT_ARC_NEXT); + } + + if (!Arc.IsArchive(true)) + { +#ifndef GUI + mprintf(St(MNotRAR),ArcName); +#endif + if (CmpExt(ArcName,"rar")) + ErrHandler.SetErrorCode(RARX_WARNING); + return(EXTRACT_ARC_NEXT); + } + + // Archive with corrupt encrypted header can be closed in IsArchive() call. +// if (!Arc.IsOpened()) +// return(EXTRACT_ARC_NEXT); + +#ifndef SFX_MODULE + if (Arc.Volume && Arc.NotFirstVolume) + { + char FirstVolName[NM]; + VolNameToFirstName(ArcName,FirstVolName,(Arc.NewMhd.Flags & MHD_NEWNUMBERING)!=0); + + // If several volume names from same volume set are specified + // and current volume is not first in set and first volume is present + // and specified too, let's skip the current volume. + if (stricomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) && + Cmd->ArcNames->Search(FirstVolName,NULL,false)) + return(EXTRACT_ARC_NEXT); + } +#endif + + int64 VolumeSetSize=0; // Total size of volumes after the current volume. + + if (Arc.Volume) + { + // Calculate the total size of all accessible volumes. + // This size is necessary to display the correct total progress indicator. + + char NextName[NM]; + wchar NextNameW[NM]; + + strcpy(NextName,Arc.FileName); + wcscpy(NextNameW,Arc.FileNameW); + + while (true) + { + // First volume is already added to DataIO.TotalArcSize + // in initial TotalArcSize calculation in DoExtract. + // So we skip it and start from second volume. + NextVolumeName(NextName,NextNameW,ASIZE(NextName),(Arc.NewMhd.Flags & MHD_NEWNUMBERING)==0 || Arc.OldFormat); + struct FindData FD; + if (FindFile::FastFind(NextName,NextNameW,&FD)) + VolumeSetSize+=FD.Size; + else + break; + } + DataIO.TotalArcSize+=VolumeSetSize; + } + + ExtractArchiveInit(Cmd,Arc); + + if (*Cmd->Command=='T' || *Cmd->Command=='I') + Cmd->Test=true; + + +#ifndef GUI + if (*Cmd->Command=='I') + Cmd->DisablePercentage=true; + else + if (Cmd->Test) + mprintf(St(MExtrTest),ArcName); + else + mprintf(St(MExtracting),ArcName); +#endif + + Arc.ViewComment(); + + // RAR can close a corrupt encrypted archive +// if (!Arc.IsOpened()) +// return(EXTRACT_ARC_NEXT); + + + while (1) + { + size_t Size=Arc.ReadHeader(); + bool Repeat=false; + if (!ExtractCurrentFile(Cmd,Arc,Size,Repeat)) + if (Repeat) + { + // If we started extraction from not first volume and need to + // restart it from first, we must correct DataIO.TotalArcSize + // for correct total progress display. We subtract the size + // of current volume and all volumes after it and add the size + // of new (first) volume. + FindData OldArc,NewArc; + if (FindFile::FastFind(Arc.FileName,Arc.FileNameW,&OldArc) && + FindFile::FastFind(ArcName,ArcNameW,&NewArc)) + DataIO.TotalArcSize-=VolumeSetSize+OldArc.Size-NewArc.Size; + return(EXTRACT_ARC_REPEAT); + } + else + break; + } + + return(EXTRACT_ARC_NEXT); +} + + +bool CmdExtract::ExtractCurrentFile(CommandData *Cmd,Archive &Arc,size_t HeaderSize,bool &Repeat) +{ + char Command=*Cmd->Command; + if (HeaderSize==0) + if (DataIO.UnpVolume) + { +#ifdef NOVOLUME + return(false); +#else + if (!MergeArchive(Arc,&DataIO,false,Command)) + { + ErrHandler.SetErrorCode(RARX_WARNING); + return(false); + } + SignatureFound=false; +#endif + } + else + return(false); + int HeadType=Arc.GetHeaderType(); + if (HeadType!=FILE_HEAD) + { + if (HeadType==AV_HEAD || HeadType==SIGN_HEAD) + SignatureFound=true; +#if !defined(SFX_MODULE) && !defined(_WIN_CE) + if (HeadType==SUB_HEAD && PrevExtracted) + SetExtraInfo(Cmd,Arc,DestFileName,*DestFileNameW ? DestFileNameW:NULL); +#endif + if (HeadType==NEWSUB_HEAD) + { + if (Arc.SubHead.CmpName(SUBHEAD_TYPE_AV)) + SignatureFound=true; +#if !defined(NOSUBBLOCKS) && !defined(_WIN_CE) + if (PrevExtracted) + SetExtraInfoNew(Cmd,Arc,DestFileName,*DestFileNameW ? DestFileNameW:NULL); +#endif + } + if (HeadType==ENDARC_HEAD) + if (Arc.EndArcHead.Flags & EARC_NEXT_VOLUME) + { +#ifndef NOVOLUME + if (!MergeArchive(Arc,&DataIO,false,Command)) + { + ErrHandler.SetErrorCode(RARX_WARNING); + return(false); + } + SignatureFound=false; +#endif + Arc.Seek(Arc.CurBlockPos,SEEK_SET); + return(true); + } + else + return(false); + Arc.SeekToNext(); + return(true); + } + PrevExtracted=false; + + if (SignatureFound || + !Cmd->Recurse && MatchedArgs>=Cmd->FileArgs->ItemsCount() && + AllMatchesExact) + return(false); + + char ArcFileName[NM]; + IntToExt(Arc.NewLhd.FileName,Arc.NewLhd.FileName); + strcpy(ArcFileName,Arc.NewLhd.FileName); + + wchar ArcFileNameW[NM]; + *ArcFileNameW=0; + + int MatchType=MATCH_WILDSUBPATH; + + bool EqualNames=false; + int MatchNumber=Cmd->IsProcessFile(Arc.NewLhd,&EqualNames,MatchType); + bool ExactMatch=MatchNumber!=0; +#if !defined(SFX_MODULE) && !defined(_WIN_CE) + if (Cmd->ExclPath==EXCL_BASEPATH) + { + *Cmd->ArcPath=0; + if (ExactMatch) + { + Cmd->FileArgs->Rewind(); + if (Cmd->FileArgs->GetString(Cmd->ArcPath,NULL,sizeof(Cmd->ArcPath),MatchNumber-1)) + *PointToName(Cmd->ArcPath)=0; + } + } +#endif + if (ExactMatch && !EqualNames) + AllMatchesExact=false; + +#ifdef UNICODE_SUPPORTED + bool WideName=(Arc.NewLhd.Flags & LHD_UNICODE) && UnicodeEnabled(); +#else + bool WideName=false; +#endif + +#ifdef _APPLE + if (WideName) + { + // Prepare UTF-8 name for OS X. Since we are sure that destination + // is UTF-8, we can avoid calling the less reliable WideToChar function. + WideToUtf(Arc.NewLhd.FileNameW,ArcFileName,ASIZE(ArcFileName)); + WideName=false; + } +#endif + + wchar *DestNameW=WideName ? DestFileNameW:NULL; + +#ifdef UNICODE_SUPPORTED + if (WideName) + { + // Prepare the name in single byte native encoding (typically UTF-8 + // for Unix-based systems). Windows does not really need it, + // but Unix system will use this name instead of Unicode. + ConvertPath(Arc.NewLhd.FileNameW,ArcFileNameW); + char Name[NM]; + if (WideToChar(ArcFileNameW,Name) && IsNameUsable(Name)) + strcpy(ArcFileName,Name); + } +#endif + + ConvertPath(ArcFileName,ArcFileName); + + if (Arc.IsArcLabel()) + return(true); + + if (Arc.NewLhd.Flags & LHD_VERSION) + { + if (Cmd->VersionControl!=1 && !EqualNames) + { + if (Cmd->VersionControl==0) + ExactMatch=false; + int Version=ParseVersionFileName(ArcFileName,ArcFileNameW,false); + if (Cmd->VersionControl-1==Version) + ParseVersionFileName(ArcFileName,ArcFileNameW,true); + else + ExactMatch=false; + } + } + else + if (!Arc.IsArcDir() && Cmd->VersionControl>1) + ExactMatch=false; + + Arc.ConvertAttributes(); + +#if !defined(SFX_MODULE) && !defined(RARDLL) + if ((Arc.NewLhd.Flags & LHD_SPLIT_BEFORE)!=0 && FirstFile) + { + char CurVolName[NM]; + strcpy(CurVolName,ArcName); + + bool NewNumbering=(Arc.NewMhd.Flags & MHD_NEWNUMBERING)!=0; + VolNameToFirstName(ArcName,ArcName,NewNumbering); + if (*ArcNameW!=0) + VolNameToFirstName(ArcNameW,ArcNameW,NewNumbering); + + if (stricomp(ArcName,CurVolName)!=0 && FileExist(ArcName,ArcNameW)) + { + // If first volume name does not match the current name and if + // such volume name really exists, let's unpack from this first volume. + Repeat=true; + return(false); + } +#if !defined(RARDLL) && !defined(_WIN_CE) + if (!ReconstructDone) + { + ReconstructDone=true; + + RecVolumes RecVol; + if (RecVol.Restore(Cmd,Arc.FileName,Arc.FileNameW,true)) + { + Repeat=true; + return(false); + } + } +#endif + strcpy(ArcName,CurVolName); + } +#endif + + DataIO.UnpVolume=(Arc.NewLhd.Flags & LHD_SPLIT_AFTER)!=0; + DataIO.NextVolumeMissing=false; + + Arc.Seek(Arc.NextBlockPos-Arc.NewLhd.FullPackSize,SEEK_SET); + + bool TestMode=false; + bool ExtrFile=false; + bool SkipSolid=false; + +#ifndef SFX_MODULE + if (FirstFile && (ExactMatch || Arc.Solid) && (Arc.NewLhd.Flags & (LHD_SPLIT_BEFORE/*|LHD_SOLID*/))!=0) + { + if (ExactMatch) + { + Log(Arc.FileName,St(MUnpCannotMerge),ArcFileName); +#ifdef RARDLL + Cmd->DllError=ERAR_BAD_DATA; +#endif + ErrHandler.SetErrorCode(RARX_OPEN); + } + ExactMatch=false; + } + + FirstFile=false; +#endif + + if (ExactMatch || (SkipSolid=Arc.Solid)!=0) + { + if ((Arc.NewLhd.Flags & LHD_PASSWORD)!=0) +#ifndef RARDLL + if (!Password.IsSet()) +#endif + { +#ifdef RARDLL + if (!Cmd->Password.IsSet()) + { + if (Cmd->Callback!=NULL) + { + wchar PasswordW[MAXPASSWORD]; + *PasswordW=0; + if (Cmd->Callback(UCM_NEEDPASSWORDW,Cmd->UserData,(LPARAM)PasswordW,ASIZE(PasswordW))==-1) + *PasswordW=0; + if (*PasswordW==0) + { + char PasswordA[MAXPASSWORD]; + *PasswordA=0; + if (Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)PasswordA,ASIZE(PasswordA))==-1) + *PasswordA=0; + GetWideName(PasswordA,NULL,PasswordW,ASIZE(PasswordW)); + cleandata(PasswordA,sizeof(PasswordA)); + } + Cmd->Password.Set(PasswordW); + cleandata(PasswordW,sizeof(PasswordW)); + } + if (!Cmd->Password.IsSet()) + return false; + } + Password=Cmd->Password; + +#else + if (!GetPassword(PASSWORD_FILE,ArcFileName,ArcFileNameW,&Password)) + { + PasswordCancelled=true; + return(false); + } +#endif + } +#if !defined(GUI) && !defined(SILENT) + else + if (!PasswordAll && (!Arc.Solid || Arc.NewLhd.UnpVer>=20 && (Arc.NewLhd.Flags & LHD_SOLID)==0)) + { + eprintf(St(MUseCurPsw),ArcFileName); + switch(Cmd->AllYes ? 1:Ask(St(MYesNoAll))) + { + case -1: + ErrHandler.Exit(RARX_USERBREAK); + case 2: + if (!GetPassword(PASSWORD_FILE,ArcFileName,ArcFileNameW,&Password)) + { + return(false); + } + break; + case 3: + PasswordAll=true; + break; + } + } +#endif + +#ifndef SFX_MODULE + if (*Cmd->ExtrPath==0 && *Cmd->ExtrPathW!=0) + WideToChar(Cmd->ExtrPathW,DestFileName); + else +#endif + strcpy(DestFileName,Cmd->ExtrPath); + + +#ifndef SFX_MODULE + if (Cmd->AppendArcNameToPath) + { + strcat(DestFileName,PointToName(Arc.FirstVolumeName)); + SetExt(DestFileName,NULL); + AddEndSlash(DestFileName); + } +#endif + + char *ExtrName=ArcFileName; + + bool EmptyName=false; +#ifndef SFX_MODULE + size_t Length=strlen(Cmd->ArcPath); + if (Length>1 && IsPathDiv(Cmd->ArcPath[Length-1]) && + strlen(ArcFileName)==Length-1) + Length--; + if (Length>0 && strnicomp(Cmd->ArcPath,ArcFileName,Length)==0) + { + ExtrName+=Length; + while (*ExtrName==CPATHDIVIDER) + ExtrName++; + if (*ExtrName==0) + EmptyName=true; + } +#endif + + // Use -ep3 only in systems, where disk letters are exist, not in Unix. + bool AbsPaths=Cmd->ExclPath==EXCL_ABSPATH && Command=='X' && IsDriveDiv(':'); + + // We do not use any user specified destination paths when extracting + // absolute paths in -ep3 mode. + if (AbsPaths) + *DestFileName=0; + + if (Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH) + strcat(DestFileName,PointToName(ExtrName)); + else + strcat(DestFileName,ExtrName); + + char DiskLetter=etoupper(DestFileName[0]); + + if (AbsPaths) + { + if (DestFileName[1]=='_' && IsPathDiv(DestFileName[2]) && + DiskLetter>='A' && DiskLetter<='Z') + DestFileName[1]=':'; + else + if (DestFileName[0]=='_' && DestFileName[1]=='_') + { + // Convert __server\share to \\server\share. + DestFileName[0]=CPATHDIVIDER; + DestFileName[1]=CPATHDIVIDER; + } + } + +#ifndef SFX_MODULE + if (!WideName && *Cmd->ExtrPathW!=0) + { + DestNameW=DestFileNameW; + WideName=true; + CharToWide(ArcFileName,ArcFileNameW); + } +#endif + + if (WideName) + { + if (*Cmd->ExtrPathW!=0) + wcscpy(DestFileNameW,Cmd->ExtrPathW); + else + CharToWide(Cmd->ExtrPath,DestFileNameW); + +#ifndef SFX_MODULE + if (Cmd->AppendArcNameToPath) + { + wchar FileNameW[NM]; + if (*Arc.FirstVolumeNameW!=0) + wcscpy(FileNameW,Arc.FirstVolumeNameW); + else + CharToWide(Arc.FirstVolumeName,FileNameW); + wcscat(DestFileNameW,PointToName(FileNameW)); + SetExt(DestFileNameW,NULL); + AddEndSlash(DestFileNameW); + } +#endif + wchar *ExtrNameW=ArcFileNameW; +#ifndef SFX_MODULE + if (Length>0) + { + wchar ArcPathW[NM]; + GetWideName(Cmd->ArcPath,Cmd->ArcPathW,ArcPathW,ASIZE(ArcPathW)); + Length=wcslen(ArcPathW); + } + ExtrNameW+=Length; + while (*ExtrNameW==CPATHDIVIDER) + ExtrNameW++; +#endif + + if (AbsPaths) + *DestFileNameW=0; + + if (Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH) + wcscat(DestFileNameW,PointToName(ExtrNameW)); + else + wcscat(DestFileNameW,ExtrNameW); + + if (AbsPaths && DestFileNameW[1]=='_' && IsPathDiv(DestFileNameW[2])) + DestFileNameW[1]=':'; + } + else + *DestFileNameW=0; + + ExtrFile=!SkipSolid && !EmptyName && (Arc.NewLhd.Flags & LHD_SPLIT_BEFORE)==0; + + if ((Cmd->FreshFiles || Cmd->UpdateFiles) && (Command=='E' || Command=='X')) + { + struct FindData FD; + if (FindFile::FastFind(DestFileName,DestNameW,&FD)) + { + if (FD.mtime >= Arc.NewLhd.mtime) + { + // If directory already exists and its modification time is newer + // than start of extraction, it is likely it was created + // when creating a path to one of already extracted items. + // In such case we'll better update its time even if archived + // directory is older. + + if (!FD.IsDir || FD.mtimeFreshFiles) + ExtrFile=false; + } + + // Skip encrypted file if no password is specified. + if ((Arc.NewLhd.Flags & LHD_PASSWORD)!=0 && !Password.IsSet()) + { + ErrHandler.SetErrorCode(RARX_WARNING); +#ifdef RARDLL + Cmd->DllError=ERAR_MISSING_PASSWORD; +#endif + ExtrFile=false; + } + +#ifdef RARDLL + if (*Cmd->DllDestName) + { + strncpyz(DestFileName,Cmd->DllDestName,ASIZE(DestFileName)); + *DestFileNameW=0; + if (Cmd->DllOpMode!=RAR_EXTRACT) + ExtrFile=false; + } + if (*Cmd->DllDestNameW) + { + wcsncpyz(DestFileNameW,Cmd->DllDestNameW,ASIZE(DestFileNameW)); + DestNameW=DestFileNameW; + if (Cmd->DllOpMode!=RAR_EXTRACT) + ExtrFile=false; + } +#endif + +#ifdef SFX_MODULE + if ((Arc.NewLhd.UnpVer!=UNP_VER && Arc.NewLhd.UnpVer!=29) && + Arc.NewLhd.Method!=0x30) +#else + if (Arc.NewLhd.UnpVer<13 || Arc.NewLhd.UnpVer>UNP_VER) +#endif + { +#ifndef SILENT + Log(Arc.FileName,St(MUnknownMeth),ArcFileName); +#ifndef SFX_MODULE + Log(Arc.FileName,St(MVerRequired),Arc.NewLhd.UnpVer/10,Arc.NewLhd.UnpVer%10); +#endif +#endif + ExtrFile=false; + ErrHandler.SetErrorCode(RARX_WARNING); +#ifdef RARDLL + Cmd->DllError=ERAR_UNKNOWN_FORMAT; +#endif + } + + File CurFile; + + if (!IsLink(Arc.NewLhd.FileAttr)) + if (Arc.IsArcDir()) + { + if (!ExtrFile || Command=='P' || Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH) + return(true); + if (SkipSolid) + { +#ifndef GUI + mprintf(St(MExtrSkipFile),ArcFileName); +#endif + return(true); + } + TotalFileCount++; + if (Cmd->Test) + { +#ifndef GUI + mprintf(St(MExtrTestFile),ArcFileName); + mprintf(" %s",St(MOk)); +#endif + return(true); + } + MKDIR_CODE MDCode=MakeDir(DestFileName,DestNameW,!Cmd->IgnoreGeneralAttr,Arc.NewLhd.FileAttr); + bool DirExist=false; + if (MDCode!=MKDIR_SUCCESS) + { + DirExist=FileExist(DestFileName,DestNameW); + if (DirExist && !IsDir(GetFileAttr(DestFileName,DestNameW))) + { + // File with name same as this directory exists. Propose user + // to overwrite it. + bool UserReject; + FileCreate(Cmd,NULL,DestFileName,DestNameW,Cmd->Overwrite,&UserReject,Arc.NewLhd.FullUnpSize,Arc.NewLhd.FileTime); + DirExist=false; + } + if (!DirExist) + { + CreatePath(DestFileName,DestNameW,true); + MDCode=MakeDir(DestFileName,DestNameW,!Cmd->IgnoreGeneralAttr,Arc.NewLhd.FileAttr); + } + } + if (MDCode==MKDIR_SUCCESS) + { +#ifndef GUI + mprintf(St(MCreatDir),DestFileName); + mprintf(" %s",St(MOk)); +#endif + PrevExtracted=true; + } + else + if (DirExist) + { + if (!Cmd->IgnoreGeneralAttr) + SetFileAttr(DestFileName,DestNameW,Arc.NewLhd.FileAttr); + PrevExtracted=true; + } + else + { + Log(Arc.FileName,St(MExtrErrMkDir),DestFileName); + ErrHandler.CheckLongPathErrMsg(DestFileName,DestNameW); + ErrHandler.SysErrMsg(); +#ifdef RARDLL + Cmd->DllError=ERAR_ECREATE; +#endif + ErrHandler.SetErrorCode(RARX_CREATE); + } + if (PrevExtracted) + { +#if defined(_WIN_ALL) && !defined(_WIN_CE) && !defined(SFX_MODULE) + if (Cmd->SetCompressedAttr && + (Arc.NewLhd.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0 && WinNT()) + SetFileCompression(DestFileName,DestNameW,true); +#endif + SetDirTime(DestFileName,DestNameW, + Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.NewLhd.mtime, + Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.NewLhd.ctime, + Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.NewLhd.atime); + } + return(true); + } + else + { + if (Cmd->Test && ExtrFile) + TestMode=true; +#if !defined(GUI) && !defined(SFX_MODULE) + if (Command=='P' && ExtrFile) + CurFile.SetHandleType(FILE_HANDLESTD); +#endif + if ((Command=='E' || Command=='X') && ExtrFile && !Cmd->Test) + { + bool UserReject; + // Specify "write only" mode to avoid OpenIndiana NAS problems + // with SetFileTime and read+write files. + if (!FileCreate(Cmd,&CurFile,DestFileName,DestNameW,Cmd->Overwrite,&UserReject,Arc.NewLhd.FullUnpSize,Arc.NewLhd.FileTime,true)) + { + ExtrFile=false; + if (!UserReject) + { + ErrHandler.CreateErrorMsg(Arc.FileName,Arc.FileNameW,DestFileName,DestFileNameW); + ErrHandler.SetErrorCode(RARX_CREATE); +#ifdef RARDLL + Cmd->DllError=ERAR_ECREATE; +#endif + if (!IsNameUsable(DestFileName) && (!WideName || !IsNameUsable(DestNameW))) + { + Log(Arc.FileName,St(MCorrectingName)); + char OrigName[ASIZE(DestFileName)]; + wchar OrigNameW[ASIZE(DestFileNameW)]; + strncpyz(OrigName,DestFileName,ASIZE(OrigName)); + wcsncpyz(OrigNameW,NullToEmpty(DestNameW),ASIZE(OrigNameW)); + + MakeNameUsable(DestFileName,true); + + if (WideName) + MakeNameUsable(DestNameW,true); + + CreatePath(DestFileName,DestNameW,true); + if (FileCreate(Cmd,&CurFile,DestFileName,DestNameW,Cmd->Overwrite,&UserReject,Arc.NewLhd.FullUnpSize,Arc.NewLhd.FileTime,true)) + { +#ifndef SFX_MODULE + Log(Arc.FileName,St(MRenaming),OrigName,DestFileName); +#endif + ExtrFile=true; + } + else + ErrHandler.CreateErrorMsg(Arc.FileName,Arc.FileNameW,DestFileName,DestFileNameW); + } + } + } + } + } + + if (!ExtrFile && Arc.Solid) + { + SkipSolid=true; + TestMode=true; + ExtrFile=true; + + } + if (ExtrFile) + { + + if (!SkipSolid) + { + if (!TestMode && Command!='P' && CurFile.IsDevice()) + { + Log(Arc.FileName,St(MInvalidName),DestFileName); + ErrHandler.WriteError(Arc.FileName,Arc.FileNameW,DestFileName,DestFileNameW); + } + TotalFileCount++; + } + FileCount++; +#ifndef GUI + if (Command!='I') + if (SkipSolid) + mprintf(St(MExtrSkipFile),ArcFileName); + else + switch(Cmd->Test ? 'T':Command) + { + case 'T': + mprintf(St(MExtrTestFile),ArcFileName); + break; +#ifndef SFX_MODULE + case 'P': + mprintf(St(MExtrPrinting),ArcFileName); + break; +#endif + case 'X': + case 'E': + mprintf(St(MExtrFile),DestFileName); + break; + } + if (!Cmd->DisablePercentage) + mprintf(" "); +#endif + DataIO.CurUnpRead=0; + DataIO.CurUnpWrite=0; + DataIO.UnpFileCRC=Arc.OldFormat ? 0 : 0xffffffff; + DataIO.PackedCRC=0xffffffff; + + SecPassword FilePassword; +#ifdef _WIN_ALL + if (Arc.NewLhd.HostOS==HOST_MSDOS/* && Arc.NewLhd.UnpVer<=25*/) + { + // We need the password in OEM encoding if file was encrypted by + // native RAR/DOS (not extender based). Let's make the conversion. + wchar PlainPsw[MAXPASSWORD]; + Password.Get(PlainPsw,ASIZE(PlainPsw)); + char PswA[MAXPASSWORD]; + CharToOemBuffW(PlainPsw,PswA,ASIZE(PswA)); + PswA[ASIZE(PswA)-1]=0; + CharToWide(PswA,PlainPsw,ASIZE(PlainPsw)); + PlainPsw[ASIZE(PlainPsw)-1]=0; + FilePassword.Set(PlainPsw); + cleandata(PlainPsw,sizeof(PlainPsw)); + cleandata(PswA,sizeof(PswA)); + } + else +#endif + FilePassword=Password; + + DataIO.SetEncryption( + (Arc.NewLhd.Flags & LHD_PASSWORD)!=0 ? Arc.NewLhd.UnpVer:0,&FilePassword, + (Arc.NewLhd.Flags & LHD_SALT)!=0 ? Arc.NewLhd.Salt:NULL,false, + Arc.NewLhd.UnpVer>=36); + DataIO.SetPackedSizeToRead(Arc.NewLhd.FullPackSize); + DataIO.SetFiles(&Arc,&CurFile); + DataIO.SetTestMode(TestMode); + DataIO.SetSkipUnpCRC(SkipSolid); +#ifndef _WIN_CE + if (!TestMode && !Arc.BrokenFileHeader && + (Arc.NewLhd.FullPackSize<<11)>Arc.NewLhd.FullUnpSize && + (Arc.NewLhd.FullUnpSize<100000000 || Arc.FileLength()>Arc.NewLhd.FullPackSize)) + CurFile.Prealloc(Arc.NewLhd.FullUnpSize); +#endif + + CurFile.SetAllowDelete(!Cmd->KeepBroken); + + bool LinkCreateMode=!Cmd->Test && !SkipSolid; + if (ExtractLink(DataIO,Arc,DestFileName,DataIO.UnpFileCRC,LinkCreateMode)) + PrevExtracted=LinkCreateMode; + else + if ((Arc.NewLhd.Flags & LHD_SPLIT_BEFORE)==0) + if (Arc.NewLhd.Method==0x30) + UnstoreFile(DataIO,Arc.NewLhd.FullUnpSize); + else + { + Unp->SetDestSize(Arc.NewLhd.FullUnpSize); +#ifndef SFX_MODULE + if (Arc.NewLhd.UnpVer<=15) + Unp->DoUnpack(15,FileCount>1 && Arc.Solid); + else +#endif + Unp->DoUnpack(Arc.NewLhd.UnpVer,(Arc.NewLhd.Flags & LHD_SOLID)!=0); + } + +// if (Arc.IsOpened()) + Arc.SeekToNext(); + + bool ValidCRC=Arc.OldFormat && GET_UINT32(DataIO.UnpFileCRC)==GET_UINT32(Arc.NewLhd.FileCRC) || + !Arc.OldFormat && GET_UINT32(DataIO.UnpFileCRC)==GET_UINT32(Arc.NewLhd.FileCRC^0xffffffff); + + // We set AnySolidDataUnpackedWell to true if we found at least one + // valid non-zero solid file in preceding solid stream. If it is true + // and if current encrypted file is broken, we do not need to hint + // about a wrong password and can report CRC error only. + if ((Arc.NewLhd.Flags & LHD_SOLID)==0) + AnySolidDataUnpackedWell=false; // Reset the flag, because non-solid file is found. + else + if (Arc.NewLhd.Method!=0x30 && Arc.NewLhd.FullUnpSize>0 && ValidCRC) + AnySolidDataUnpackedWell=true; + + bool BrokenFile=false; + if (!SkipSolid) + { + if (ValidCRC) + { +#ifndef GUI + if (Command!='P' && Command!='I') + mprintf("%s%s ",Cmd->DisablePercentage ? " ":"\b\b\b\b\b ",St(MOk)); +#endif + } + else + { + if ((Arc.NewLhd.Flags & LHD_PASSWORD)!=0 && !AnySolidDataUnpackedWell) + { + Log(Arc.FileName,St(MEncrBadCRC),ArcFileName); + } + else + { + Log(Arc.FileName,St(MCRCFailed),ArcFileName); + } + BrokenFile=true; + ErrHandler.SetErrorCode(RARX_CRC); +#ifdef RARDLL + // If we already have ERAR_EOPEN as result of missing volume, + // we should not replace it with less precise ERAR_BAD_DATA. + if (Cmd->DllError!=ERAR_EOPEN) + Cmd->DllError=ERAR_BAD_DATA; +#endif + Alarm(); + } + } +#ifndef GUI + else + mprintf("\b\b\b\b\b "); +#endif + + if (!TestMode && (Command=='X' || Command=='E') && + !IsLink(Arc.NewLhd.FileAttr)) + { +#if defined(_WIN_ALL) || defined(_EMX) + if (Cmd->ClearArc) + Arc.NewLhd.FileAttr&=~FA_ARCH; +#endif + if (!BrokenFile || Cmd->KeepBroken) + { + if (BrokenFile) + CurFile.Truncate(); + CurFile.SetOpenFileTime( + Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.NewLhd.mtime, + Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.NewLhd.ctime, + Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.NewLhd.atime); + CurFile.Close(); +#if defined(_WIN_ALL) && !defined(_WIN_CE) && !defined(SFX_MODULE) + if (Cmd->SetCompressedAttr && + (Arc.NewLhd.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0 && WinNT()) + SetFileCompression(CurFile.FileName,CurFile.FileNameW,true); +#endif + CurFile.SetCloseFileTime( + Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.NewLhd.mtime, + Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.NewLhd.atime); + if (!Cmd->IgnoreGeneralAttr) + SetFileAttr(CurFile.FileName,CurFile.FileNameW,Arc.NewLhd.FileAttr); + PrevExtracted=true; + } + } + } + } + if (ExactMatch) + MatchedArgs++; + if (DataIO.NextVolumeMissing/* || !Arc.IsOpened()*/) + return(false); + if (!ExtrFile) + if (!Arc.Solid) + Arc.SeekToNext(); + else + if (!SkipSolid) + return(false); + return(true); +} + + +void CmdExtract::UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize) +{ + Array Buffer(0x10000); + while (1) + { + uint Code=DataIO.UnpRead(&Buffer[0],Buffer.Size()); + if (Code==0 || (int)Code==-1) + break; + Code=Code=0) + DestUnpSize-=Code; + } +} + diff --git a/src/unrar/extract.hpp b/src/unrar/extract.hpp new file mode 100644 index 0000000000..0995e8edae --- /dev/null +++ b/src/unrar/extract.hpp @@ -0,0 +1,49 @@ +#ifndef _RAR_EXTRACT_ +#define _RAR_EXTRACT_ + +enum EXTRACT_ARC_CODE {EXTRACT_ARC_NEXT,EXTRACT_ARC_REPEAT}; + +class CmdExtract +{ + private: + EXTRACT_ARC_CODE ExtractArchive(CommandData *Cmd); + RarTime StartTime; // time when extraction started + + ComprDataIO DataIO; + Unpack *Unp; + unsigned long TotalFileCount; + + unsigned long FileCount; + unsigned long MatchedArgs; + bool FirstFile; + bool AllMatchesExact; + bool ReconstructDone; + + // If any non-zero solid file was successfully unpacked before current. + // If true and if current encrypted file is broken, obviously + // the password is correct and we can report broken CRC without + // any wrong password hints. + bool AnySolidDataUnpackedWell; + + char ArcName[NM]; + wchar ArcNameW[NM]; + + SecPassword Password; + bool PasswordAll; + bool PrevExtracted; + char DestFileName[NM]; + wchar DestFileNameW[NM]; + bool PasswordCancelled; + public: + CmdExtract(); + ~CmdExtract(); + void DoExtract(CommandData *Cmd); + void ExtractArchiveInit(CommandData *Cmd,Archive &Arc); + bool ExtractCurrentFile(CommandData *Cmd,Archive &Arc,size_t HeaderSize, + bool &Repeat); + static void UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize); + + bool SignatureFound; +}; + +#endif diff --git a/src/unrar/filcreat.cpp b/src/unrar/filcreat.cpp new file mode 100644 index 0000000000..2bd176f79b --- /dev/null +++ b/src/unrar/filcreat.cpp @@ -0,0 +1,260 @@ +#include "rar.hpp" + +bool FileCreate(RAROptions *Cmd,File *NewFile,char *Name,wchar *NameW, + OVERWRITE_MODE Mode,bool *UserReject,int64 FileSize, + uint FileTime,bool WriteOnly) +{ + if (UserReject!=NULL) + *UserReject=false; +#if defined(_WIN_ALL) && !defined(_WIN_CE) + bool ShortNameChanged=false; +#endif + while (FileExist(Name,NameW)) + { +#if defined(_WIN_ALL) && !defined(_WIN_CE) + if (!ShortNameChanged) + { + // Avoid the infinite loop if UpdateExistingShortName returns + // the same name. + ShortNameChanged=true; + + // Maybe our long name matches the short name of existing file. + // Let's check if we can change the short name. + wchar WideName[NM]; + GetWideName(Name,NameW,WideName,ASIZE(WideName)); + if (UpdateExistingShortName(WideName)) + { + if (Name!=NULL && *Name!=0) + WideToChar(WideName,Name); + if (NameW!=NULL && *NameW!=0) + wcscpy(NameW,WideName); + continue; + } + } + // Allow short name check again. It is necessary, because rename and + // autorename below can change the name, so we need to check it again. + ShortNameChanged=false; +#endif + if (Mode==OVERWRITE_NONE) + { + if (UserReject!=NULL) + *UserReject=true; + return(false); + } + + // Must be before Cmd->AllYes check or -y switch would override -or. + if (Mode==OVERWRITE_AUTORENAME) + { + if (!GetAutoRenamedName(Name,NameW)) + Mode=OVERWRITE_DEFAULT; + continue; + } + +#ifdef SILENT + Mode=OVERWRITE_ALL; +#endif + + // This check must be after OVERWRITE_AUTORENAME processing or -y switch + // would override -or. + if (Cmd->AllYes || Mode==OVERWRITE_ALL) + break; + + if (Mode==OVERWRITE_DEFAULT || Mode==OVERWRITE_FORCE_ASK) + { + char NewName[NM]; + wchar NewNameW[NM]; + *NewNameW=0; + eprintf(St(MFileExists),Name); + int Choice=Ask(St(MYesNoAllRenQ)); + if (Choice==1) + break; + if (Choice==2) + { + if (UserReject!=NULL) + *UserReject=true; + return(false); + } + if (Choice==3) + { + Cmd->Overwrite=OVERWRITE_ALL; + break; + } + if (Choice==4) + { + if (UserReject!=NULL) + *UserReject=true; + Cmd->Overwrite=OVERWRITE_NONE; + return(false); + } + if (Choice==5) + { +#ifndef GUI + mprintf(St(MAskNewName)); + +#ifdef _WIN_ALL + File SrcFile; + SrcFile.SetHandleType(FILE_HANDLESTD); + int Size=SrcFile.Read(NewName,sizeof(NewName)-1); + NewName[Size]=0; + OemToCharA(NewName,NewName); +#else + if (fgets(NewName,sizeof(NewName),stdin)==NULL) + { + // Process fgets failure as if user answered 'No'. + if (UserReject!=NULL) + *UserReject=true; + return(false); + } +#endif + RemoveLF(NewName); +#endif + if (PointToName(NewName)==NewName) + strcpy(PointToName(Name),NewName); + else + strcpy(Name,NewName); + + if (NameW!=NULL) + if (PointToName(NewNameW)==NewNameW) + wcscpy(PointToName(NameW),NewNameW); + else + wcscpy(NameW,NewNameW); + continue; + } + if (Choice==6) + ErrHandler.Exit(RARX_USERBREAK); + } + } + uint FileMode=WriteOnly ? FMF_WRITE|FMF_SHAREREAD:FMF_UPDATE|FMF_SHAREREAD; + if (NewFile!=NULL && NewFile->Create(Name,NameW,FileMode)) + return(true); + PrepareToDelete(Name,NameW); + CreatePath(Name,NameW,true); + return(NewFile!=NULL ? NewFile->Create(Name,NameW,FileMode):DelFile(Name,NameW)); +} + + +bool GetAutoRenamedName(char *Name,wchar *NameW) +{ + char NewName[NM]; + wchar NewNameW[NM]; + + if (Name!=NULL && strlen(Name)>ASIZE(NewName)-10 || + NameW!=NULL && wcslen(NameW)>ASIZE(NewNameW)-10) + return(false); + char *Ext=NULL; + if (Name!=NULL && *Name!=0) + { + Ext=GetExt(Name); + if (Ext==NULL) + Ext=Name+strlen(Name); + } + wchar *ExtW=NULL; + if (NameW!=NULL && *NameW!=0) + { + ExtW=GetExt(NameW); + if (ExtW==NULL) + ExtW=NameW+wcslen(NameW); + } + *NewName=0; + *NewNameW=0; + for (int FileVer=1;;FileVer++) + { + if (Name!=NULL && *Name!=0) + sprintf(NewName,"%.*s(%d)%s",int(Ext-Name),Name,FileVer,Ext); + if (NameW!=NULL && *NameW!=0) + sprintfw(NewNameW,ASIZE(NewNameW),L"%.*s(%d)%s",int(ExtW-NameW),NameW,FileVer,ExtW); + if (!FileExist(NewName,NewNameW)) + { + if (Name!=NULL && *Name!=0) + strcpy(Name,NewName); + if (NameW!=NULL && *NameW!=0) + wcscpy(NameW,NewNameW); + break; + } + if (FileVer>=1000000) + return(false); + } + return(true); +} + + +#if defined(_WIN_ALL) && !defined(_WIN_CE) +// If we find a file, which short name is equal to 'Name', we try to change +// its short name, while preserving the long name. It helps when unpacking +// an archived file, which long name is equal to short name of already +// existing file. Otherwise we would overwrite the already existing file, +// even though its long name does not match the name of unpacking file. +bool UpdateExistingShortName(wchar *Name) +{ + // 'Name' is the name of file which we want to create. Let's check + // if file with such name is exist. If it does not, we return. + FindData fd; + if (!FindFile::FastFind(NULL,Name,&fd)) + return(false); + + // We continue only if file has a short name, which does not match its + // long name, and this short name is equal to name of file which we need + // to create. + if (*fd.ShortName==0 || wcsicomp(PointToName(fd.NameW),fd.ShortName)==0 || + wcsicomp(PointToName(Name),fd.ShortName)!=0) + return(false); + + // Generate the temporary new name for existing file. + wchar NewName[NM]; + *NewName=0; + for (int I=0;I<10000 && *NewName==0;I+=123) + { + // Here we copy the path part of file to create. We'll make the temporary + // file in the same folder. + wcsncpyz(NewName,Name,ASIZE(NewName)); + + // Here we set the random name part. + sprintfw(PointToName(NewName),ASIZE(NewName),L"rtmp%d",I); + + // If such file is already exist, try next random name. + if (FileExist(NULL,NewName)) + *NewName=0; + } + + // If we could not generate the name not used by any other file, we return. + if (*NewName==0) + return(false); + + // FastFind returns the name without path, but we need the fully qualified + // name for renaming, so we use the path from file to create and long name + // from existing file. + wchar FullName[NM]; + wcsncpyz(FullName,Name,ASIZE(FullName)); + wcscpy(PointToName(FullName),PointToName(fd.NameW)); + + // Rename the existing file to randomly generated name. Normally it changes + // the short name too. + if (!MoveFileW(FullName,NewName)) + return(false); + + // Now we need to create the temporary empty file with same name as + // short name of our already existing file. We do it to occupy its previous + // short name and not allow to use it again when renaming the file back to + // its original long name. + File KeepShortFile; + bool Created=false; + if (!FileExist(NULL,Name)) + Created=KeepShortFile.Create(NULL,Name); + + // Now we rename the existing file from temporary name to original long name. + // Since its previous short name is occupied by another file, it should + // get another short name. + MoveFileW(NewName,FullName); + + if (Created) + { + // Delete the temporary zero length file occupying the short name, + KeepShortFile.Close(); + KeepShortFile.Delete(); + } + // We successfully changed the short name. Maybe sometimes we'll simplify + // this function by use of SetFileShortName Windows API call. + // But SetFileShortName is not available in older Windows. + return(true); +} +#endif diff --git a/src/unrar/filcreat.hpp b/src/unrar/filcreat.hpp new file mode 100644 index 0000000000..7e758bfc7f --- /dev/null +++ b/src/unrar/filcreat.hpp @@ -0,0 +1,13 @@ +#ifndef _RAR_FILECREATE_ +#define _RAR_FILECREATE_ + +bool FileCreate(RAROptions *Cmd,File *NewFile,char *Name,wchar *NameW, + OVERWRITE_MODE Mode,bool *UserReject,int64 FileSize=INT64NDF, + uint FileTime=0,bool WriteOnly=false); +bool GetAutoRenamedName(char *Name,wchar *NameW); + +#if defined(_WIN_ALL) && !defined(_WIN_CE) +bool UpdateExistingShortName(wchar *Name); +#endif + +#endif diff --git a/src/unrar/file.cpp b/src/unrar/file.cpp new file mode 100644 index 0000000000..dcf4e612af --- /dev/null +++ b/src/unrar/file.cpp @@ -0,0 +1,710 @@ +#include "rar.hpp" + +static File *CreatedFiles[256]; +static int RemoveCreatedActive=0; + +File::File() +{ + hFile=BAD_HANDLE; + *FileName=0; + *FileNameW=0; + NewFile=false; + LastWrite=false; + HandleType=FILE_HANDLENORMAL; + SkipClose=false; + IgnoreReadErrors=false; + ErrorType=FILE_SUCCESS; + OpenShared=false; + AllowDelete=true; + CloseCount=0; + AllowExceptions=true; +#ifdef _WIN_ALL + NoSequentialRead=false; + CreateMode=FMF_UNDEFINED; +#endif +} + + +File::~File() +{ + if (hFile!=BAD_HANDLE && !SkipClose) + if (NewFile) + Delete(); + else + Close(); +} + + +void File::operator = (File &SrcFile) +{ + hFile=SrcFile.hFile; + strcpy(FileName,SrcFile.FileName); + NewFile=SrcFile.NewFile; + LastWrite=SrcFile.LastWrite; + HandleType=SrcFile.HandleType; + SrcFile.SkipClose=true; +} + + +bool File::Open(const char *Name,const wchar *NameW,uint Mode) +{ + ErrorType=FILE_SUCCESS; + FileHandle hNewFile; + bool OpenShared=File::OpenShared || (Mode & FMF_OPENSHARED)!=0; + bool UpdateMode=(Mode & FMF_UPDATE)!=0; + bool WriteMode=(Mode & FMF_WRITE)!=0; +#ifdef _WIN_ALL + uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ; + if (UpdateMode) + Access|=GENERIC_WRITE; + uint ShareMode=FILE_SHARE_READ; + if (OpenShared) + ShareMode|=FILE_SHARE_WRITE; + uint Flags=NoSequentialRead ? 0:FILE_FLAG_SEQUENTIAL_SCAN; + if (WinNT() && NameW!=NULL && *NameW!=0) + hNewFile=CreateFileW(NameW,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL); + else + hNewFile=CreateFileA(Name,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL); + + if (hNewFile==BAD_HANDLE && GetLastError()==ERROR_FILE_NOT_FOUND) + ErrorType=FILE_NOTFOUND; +#else + int flags=UpdateMode ? O_RDWR:(WriteMode ? O_WRONLY:O_RDONLY); +#ifdef O_BINARY + flags|=O_BINARY; +#if defined(_AIX) && defined(_LARGE_FILE_API) + flags|=O_LARGEFILE; +#endif +#endif +#if defined(_EMX) && !defined(_DJGPP) + int sflags=OpenShared ? SH_DENYNO:SH_DENYWR; + int handle=sopen(Name,flags,sflags); +#else + int handle=open(Name,flags); +#ifdef LOCK_EX + +#ifdef _OSF_SOURCE + extern "C" int flock(int, int); +#endif + + if (!OpenShared && UpdateMode && handle>=0 && flock(handle,LOCK_EX|LOCK_NB)==-1) + { + close(handle); + return(false); + } +#endif +#endif + hNewFile=handle==-1 ? BAD_HANDLE:fdopen(handle,UpdateMode ? UPDATEBINARY:READBINARY); + if (hNewFile==BAD_HANDLE && errno==ENOENT) + ErrorType=FILE_NOTFOUND; +#endif + NewFile=false; + HandleType=FILE_HANDLENORMAL; + SkipClose=false; + bool Success=hNewFile!=BAD_HANDLE; + if (Success) + { + hFile=hNewFile; + + // We use memove instead of strcpy and wcscpy to avoid problems + // with overlapped buffers. While we do not call this function with + // really overlapped buffers yet, we do call it with Name equal to + // FileName like Arc.Open(Arc.FileName,Arc.FileNameW,...). + if (NameW!=NULL) + memmove(FileNameW,NameW,(wcslen(NameW)+1)*sizeof(*NameW)); + else + *FileNameW=0; + if (Name!=NULL) + memmove(FileName,Name,strlen(Name)+1); + else + WideToChar(NameW,FileName); + AddFileToList(hFile); + } + return(Success); +} + + +#if !defined(SHELL_EXT) && !defined(SFX_MODULE) +void File::TOpen(const char *Name,const wchar *NameW) +{ + if (!WOpen(Name,NameW)) + ErrHandler.Exit(RARX_OPEN); +} +#endif + + +bool File::WOpen(const char *Name,const wchar *NameW) +{ + if (Open(Name,NameW)) + return(true); + ErrHandler.OpenErrorMsg(Name,NameW); + return(false); +} + + +bool File::Create(const char *Name,const wchar *NameW,uint Mode) +{ + // OpenIndiana based NAS and CIFS shares fail to set the file time if file + // was created in read+write mode and some data was written and not flushed + // before SetFileTime call. So we should use the write only mode if we plan + // SetFileTime call and do not need to read from file. + bool WriteMode=(Mode & FMF_WRITE)!=0; + bool ShareRead=(Mode & FMF_SHAREREAD)!=0 || File::OpenShared; +#ifdef _WIN_ALL + CreateMode=Mode; + uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ|GENERIC_WRITE; + DWORD ShareMode=ShareRead ? FILE_SHARE_READ:0; + if (WinNT() && NameW!=NULL && *NameW!=0) + hFile=CreateFileW(NameW,Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL); + else + hFile=CreateFileA(Name,Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL); +#else + hFile=fopen(Name,WriteMode ? WRITEBINARY:CREATEBINARY); +#endif + NewFile=true; + HandleType=FILE_HANDLENORMAL; + SkipClose=false; + if (NameW!=NULL) + wcscpy(FileNameW,NameW); + else + *FileNameW=0; + if (Name!=NULL) + strcpy(FileName,Name); + else + WideToChar(NameW,FileName); + AddFileToList(hFile); + return(hFile!=BAD_HANDLE); +} + + +void File::AddFileToList(FileHandle hFile) +{ + if (hFile!=BAD_HANDLE) + for (int I=0;ISize && FilePos-Size<=0xffffffff && FilePos+Size>0xffffffff) + ErrHandler.WriteErrorFAT(FileName,FileNameW); +#endif + if (ErrHandler.AskRepeatWrite(FileName,FileNameW,false)) + { +#ifndef _WIN_ALL + clearerr(hFile); +#endif + if (Written0) + Seek(Tell()-Written,SEEK_SET); + continue; + } + ErrHandler.WriteError(NULL,NULL,FileName,FileNameW); + } + break; + } + LastWrite=true; +} + + +int File::Read(void *Data,size_t Size) +{ + int64 FilePos=0; // Initialized only to suppress some compilers warning. + + if (IgnoreReadErrors) + FilePos=Tell(); + int ReadSize; + while (true) + { + ReadSize=DirectRead(Data,Size); + if (ReadSize==-1) + { + ErrorType=FILE_READERROR; + if (AllowExceptions) + if (IgnoreReadErrors) + { + ReadSize=0; + for (size_t I=0;IMaxDeviceRead) + Size=MaxDeviceRead; + hFile=GetStdHandle(STD_INPUT_HANDLE); +#else + hFile=stdin; +#endif + } +#endif +#ifdef _WIN_ALL + DWORD Read; + if (!ReadFile(hFile,Data,(DWORD)Size,&Read,NULL)) + { + if (IsDevice() && Size>MaxDeviceRead) + return(DirectRead(Data,MaxDeviceRead)); + if (HandleType==FILE_HANDLESTD && GetLastError()==ERROR_BROKEN_PIPE) + return(0); + return(-1); + } + return(Read); +#else + if (LastWrite) + { + fflush(hFile); + LastWrite=false; + } + clearerr(hFile); + size_t ReadSize=fread(Data,1,Size,hFile); + if (ferror(hFile)) + return(-1); + return((int)ReadSize); +#endif +} + + +void File::Seek(int64 Offset,int Method) +{ + if (!RawSeek(Offset,Method) && AllowExceptions) + ErrHandler.SeekError(FileName,FileNameW); +} + + +bool File::RawSeek(int64 Offset,int Method) +{ + if (hFile==BAD_HANDLE) + return(true); + if (Offset<0 && Method!=SEEK_SET) + { + Offset=(Method==SEEK_CUR ? Tell():FileLength())+Offset; + Method=SEEK_SET; + } +#ifdef _WIN_ALL + LONG HighDist=(LONG)(Offset>>32); + if (SetFilePointer(hFile,(LONG)Offset,&HighDist,Method)==0xffffffff && + GetLastError()!=NO_ERROR) + return(false); +#else + LastWrite=false; +#if defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE) && !defined(__VMS) + if (fseeko(hFile,Offset,Method)!=0) +#else + if (fseek(hFile,(long)Offset,Method)!=0) +#endif + return(false); +#endif + return(true); +} + + +int64 File::Tell() +{ + if (hFile==BAD_HANDLE) + if (AllowExceptions) + ErrHandler.SeekError(FileName,FileNameW); + else + return(-1); +#ifdef _WIN_ALL + LONG HighDist=0; + uint LowDist=SetFilePointer(hFile,0,&HighDist,FILE_CURRENT); + if (LowDist==0xffffffff && GetLastError()!=NO_ERROR) + if (AllowExceptions) + ErrHandler.SeekError(FileName,FileNameW); + else + return(-1); + return(INT32TO64(HighDist,LowDist)); +#else +#if defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE) + return(ftello(hFile)); +#else + return(ftell(hFile)); +#endif +#endif +} + + +void File::Prealloc(int64 Size) +{ +#ifdef _WIN_ALL + if (RawSeek(Size,SEEK_SET)) + { + Truncate(); + Seek(0,SEEK_SET); + } +#endif + +#if defined(_UNIX) && defined(USE_FALLOCATE) + // fallocate is rather new call. Only latest kernels support it. + // So we are not using it by default yet. + int fd = fileno(hFile); + if (fd >= 0) + fallocate(fd, 0, 0, Size); +#endif +} + + +byte File::GetByte() +{ + byte Byte=0; + Read(&Byte,1); + return(Byte); +} + + +void File::PutByte(byte Byte) +{ + Write(&Byte,1); +} + + +bool File::Truncate() +{ +#ifdef _WIN_ALL + return(SetEndOfFile(hFile)==TRUE); +#else + return(false); +#endif +} + + +void File::SetOpenFileTime(RarTime *ftm,RarTime *ftc,RarTime *fta) +{ +#ifdef _WIN_ALL + // Workaround for OpenIndiana NAS time bug. If we cannot create a file + // in write only mode, we need to flush the write buffer before calling + // SetFileTime or file time will not be changed. + if (CreateMode!=FMF_UNDEFINED && (CreateMode & FMF_WRITE)==0) + FlushFileBuffers(hFile); + + bool sm=ftm!=NULL && ftm->IsSet(); + bool sc=ftc!=NULL && ftc->IsSet(); + bool sa=fta!=NULL && fta->IsSet(); + FILETIME fm,fc,fa; + if (sm) + ftm->GetWin32(&fm); + if (sc) + ftc->GetWin32(&fc); + if (sa) + fta->GetWin32(&fa); + SetFileTime(hFile,sc ? &fc:NULL,sa ? &fa:NULL,sm ? &fm:NULL); +#endif +} + + +void File::SetCloseFileTime(RarTime *ftm,RarTime *fta) +{ +#if defined(_UNIX) || defined(_EMX) + SetCloseFileTimeByName(FileName,ftm,fta); +#endif +} + + +void File::SetCloseFileTimeByName(const char *Name,RarTime *ftm,RarTime *fta) +{ +#if defined(_UNIX) || defined(_EMX) + bool setm=ftm!=NULL && ftm->IsSet(); + bool seta=fta!=NULL && fta->IsSet(); + if (setm || seta) + { + utimbuf ut; + if (setm) + ut.modtime=ftm->GetUnix(); + else + ut.modtime=fta->GetUnix(); + if (seta) + ut.actime=fta->GetUnix(); + else + ut.actime=ut.modtime; + utime(Name,&ut); + } +#endif +} + + +void File::GetOpenFileTime(RarTime *ft) +{ +#ifdef _WIN_ALL + FILETIME FileTime; + GetFileTime(hFile,NULL,NULL,&FileTime); + *ft=FileTime; +#endif +#if defined(_UNIX) || defined(_EMX) + struct stat st; + fstat(fileno(hFile),&st); + *ft=st.st_mtime; +#endif +} + + +int64 File::FileLength() +{ + SaveFilePos SavePos(*this); + Seek(0,SEEK_END); + return(Tell()); +} + + +void File::SetHandleType(FILE_HANDLETYPE Type) +{ + HandleType=Type; +} + + +bool File::IsDevice() +{ + if (hFile==BAD_HANDLE) + return(false); +#ifdef _WIN_ALL + uint Type=GetFileType(hFile); + return(Type==FILE_TYPE_CHAR || Type==FILE_TYPE_PIPE); +#else + return(isatty(fileno(hFile))); +#endif +} + + +#ifndef SFX_MODULE +void File::fprintf(const char *fmt,...) +{ + va_list argptr; + va_start(argptr,fmt); + safebuf char Msg[2*NM+1024],OutMsg[2*NM+1024]; + vsprintf(Msg,fmt,argptr); +#ifdef _WIN_ALL + for (int Src=0,Dest=0;;Src++) + { + char CurChar=Msg[Src]; + if (CurChar=='\n') + OutMsg[Dest++]='\r'; + OutMsg[Dest++]=CurChar; + if (CurChar==0) + break; + } +#else + strcpy(OutMsg,Msg); +#endif + Write(OutMsg,strlen(OutMsg)); + va_end(argptr); +} +#endif + + +bool File::RemoveCreated() +{ + RemoveCreatedActive++; + bool RetCode=true; + for (int I=0;ISetExceptions(false); + bool Success; + if (CreatedFiles[I]->NewFile) + Success=CreatedFiles[I]->Delete(); + else + Success=CreatedFiles[I]->Close(); + if (Success) + CreatedFiles[I]=NULL; + else + RetCode=false; + } + RemoveCreatedActive--; + return(RetCode); +} + + +#ifndef SFX_MODULE +int64 File::Copy(File &Dest,int64 Length) +{ + Array Buffer(0x10000); + int64 CopySize=0; + bool CopyAll=(Length==INT64NDF); + + while (CopyAll || Length>0) + { + Wait(); + size_t SizeToRead=(!CopyAll && Length<(int64)Buffer.Size()) ? (size_t)Length:Buffer.Size(); + int ReadSize=Read(&Buffer[0],SizeToRead); + if (ReadSize==0) + break; + Dest.Write(&Buffer[0],ReadSize); + CopySize+=ReadSize; + if (!CopyAll) + Length-=ReadSize; + } + return(CopySize); +} +#endif diff --git a/src/unrar/file.hpp b/src/unrar/file.hpp new file mode 100644 index 0000000000..666b5196e9 --- /dev/null +++ b/src/unrar/file.hpp @@ -0,0 +1,120 @@ +#ifndef _RAR_FILE_ +#define _RAR_FILE_ + +#ifdef _WIN_ALL +typedef HANDLE FileHandle; +#define BAD_HANDLE INVALID_HANDLE_VALUE +#else +typedef FILE* FileHandle; +#define BAD_HANDLE NULL +#endif + +class RAROptions; + +enum FILE_HANDLETYPE {FILE_HANDLENORMAL,FILE_HANDLESTD,FILE_HANDLEERR}; + +enum FILE_ERRORTYPE {FILE_SUCCESS,FILE_NOTFOUND,FILE_READERROR}; + +struct FileStat +{ + uint FileAttr; + uint FileTime; + int64 FileSize; + bool IsDir; +}; + + +enum FILE_MODE_FLAGS { + // Request read only access to file. Default for Open. + FMF_READ=0, + + // Request both read and write access to file. Default for Create. + FMF_UPDATE=1, + + // Request write only access to file. + FMF_WRITE=2, + + // Open files which are already opened for write by other programs. + FMF_OPENSHARED=4, + + // Provide read access to created file for other programs. + FMF_SHAREREAD=8, + + // Mode flags are not defined yet. + FMF_UNDEFINED=256 +}; + + +class File +{ + private: + void AddFileToList(FileHandle hFile); + + FileHandle hFile; + bool LastWrite; + FILE_HANDLETYPE HandleType; + bool SkipClose; + bool IgnoreReadErrors; + bool NewFile; + bool AllowDelete; + bool AllowExceptions; +#ifdef _WIN_ALL + bool NoSequentialRead; + uint CreateMode; +#endif + protected: + bool OpenShared; // Set by 'Archive' class. + public: + char FileName[NM]; + wchar FileNameW[NM]; + + FILE_ERRORTYPE ErrorType; + + uint CloseCount; + public: + File(); + virtual ~File(); + void operator = (File &SrcFile); + bool Open(const char *Name,const wchar *NameW=NULL,uint Mode=FMF_READ); + void TOpen(const char *Name,const wchar *NameW=NULL); + bool WOpen(const char *Name,const wchar *NameW=NULL); + bool Create(const char *Name,const wchar *NameW=NULL,uint Mode=FMF_UPDATE|FMF_SHAREREAD); + void TCreate(const char *Name,const wchar *NameW=NULL,uint Mode=FMF_UPDATE|FMF_SHAREREAD); + bool WCreate(const char *Name,const wchar *NameW=NULL,uint Mode=FMF_UPDATE|FMF_SHAREREAD); + bool Close(); + void Flush(); + bool Delete(); + bool Rename(const char *NewName,const wchar *NewNameW=NULL); + void Write(const void *Data,size_t Size); + int Read(void *Data,size_t Size); + int DirectRead(void *Data,size_t Size); + void Seek(int64 Offset,int Method); + bool RawSeek(int64 Offset,int Method); + int64 Tell(); + void Prealloc(int64 Size); + byte GetByte(); + void PutByte(byte Byte); + bool Truncate(); + void SetOpenFileTime(RarTime *ftm,RarTime *ftc=NULL,RarTime *fta=NULL); + void SetCloseFileTime(RarTime *ftm,RarTime *fta=NULL); + static void SetCloseFileTimeByName(const char *Name,RarTime *ftm,RarTime *fta); + void GetOpenFileTime(RarTime *ft); + bool IsOpened() {return(hFile!=BAD_HANDLE);}; + int64 FileLength(); + void SetHandleType(FILE_HANDLETYPE Type); + FILE_HANDLETYPE GetHandleType() {return(HandleType);}; + bool IsDevice(); + void fprintf(const char *fmt,...); + static bool RemoveCreated(); + FileHandle GetHandle() {return(hFile);}; + void SetIgnoreReadErrors(bool Mode) {IgnoreReadErrors=Mode;}; + char *GetName() {return(FileName);} + int64 Copy(File &Dest,int64 Length=INT64NDF); + void SetAllowDelete(bool Allow) {AllowDelete=Allow;} + void SetExceptions(bool Allow) {AllowExceptions=Allow;} +#ifdef _WIN_ALL + void RemoveSequentialFlag() {NoSequentialRead=true;} +#endif +}; + +#endif diff --git a/src/unrar/filefn.cpp b/src/unrar/filefn.cpp new file mode 100644 index 0000000000..e7cc8fd6b0 --- /dev/null +++ b/src/unrar/filefn.cpp @@ -0,0 +1,564 @@ +#include "rar.hpp" + +MKDIR_CODE MakeDir(const char *Name,const wchar *NameW,bool SetAttr,uint Attr) +{ +#ifdef _WIN_ALL + BOOL RetCode; + if (WinNT() && NameW!=NULL && *NameW!=0) + RetCode=CreateDirectoryW(NameW,NULL); + else + if (Name!=NULL) + RetCode=CreateDirectoryA(Name,NULL); + else + return(MKDIR_BADPATH); + if (RetCode!=0) // Non-zero return code means success for CreateDirectory. + { + if (SetAttr) + SetFileAttr(Name,NameW,Attr); + return(MKDIR_SUCCESS); + } + int ErrCode=GetLastError(); + if (ErrCode==ERROR_FILE_NOT_FOUND || ErrCode==ERROR_PATH_NOT_FOUND) + return(MKDIR_BADPATH); + return(MKDIR_ERROR); +#else + + // No Unicode in the rest of function, so Name must be not NULL. + if (Name==NULL) + return(MKDIR_BADPATH); +#endif + +#ifdef _EMX + #ifdef _DJGPP + if (mkdir(Name,(Attr & FA_RDONLY) ? 0:S_IWUSR)==0) + #else + if (__mkdir(Name)==0) + #endif + { + if (SetAttr) + SetFileAttr(Name,NameW,Attr); + return(MKDIR_SUCCESS); + } + return(errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR); +#endif + +#ifdef _UNIX + mode_t uattr=SetAttr ? (mode_t)Attr:0777; + int ErrCode=mkdir(Name,uattr); + if (ErrCode==-1) + return(errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR); + return(MKDIR_SUCCESS); +#endif +} + + +bool CreatePath(const char *Path,bool SkipLastName) +{ + if (Path==NULL || *Path==0) + return(false); + +#if defined(_WIN_ALL) || defined(_EMX) + uint DirAttr=0; +#else + uint DirAttr=0777; +#endif + + bool Success=true; + + for (const char *s=Path;*s!=0;s=charnext(s)) + { + if (s-Path>=NM) + break; + + // Process all kinds of path separators, so user can enter Unix style + // path in Windows or Windows in Unix. + if (IsPathDiv(*s)) + { + char DirName[NM]; + strncpy(DirName,Path,s-Path); + DirName[s-Path]=0; + + if (MakeDir(DirName,NULL,true,DirAttr)==MKDIR_SUCCESS) + { +#ifndef GUI + mprintf(St(MCreatDir),DirName); + mprintf(" %s",St(MOk)); +#endif + } + else + Success=false; + } + } + if (!SkipLastName) + if (!IsPathDiv(*PointToLastChar(Path))) + if (MakeDir(Path,NULL,true,DirAttr)!=MKDIR_SUCCESS) + Success=false; + return(Success); +} + + +bool CreatePath(const wchar *Path,bool SkipLastName) +{ + if (Path==NULL || *Path==0) + return(false); + +#if defined(_WIN_ALL) || defined(_EMX) + uint DirAttr=0; +#else + uint DirAttr=0777; +#endif + + bool Success=true; + + for (const wchar *s=Path;*s!=0;s++) + { + if (s-Path>=NM) + break; + + // Process all kinds of path separators, so user can enter Unix style + // path in Windows or Windows in Unix. + if (IsPathDiv(*s)) + { + wchar DirName[NM]; + wcsncpy(DirName,Path,s-Path); + DirName[s-Path]=0; + + if (MakeDir(NULL,DirName,true,DirAttr)==MKDIR_SUCCESS) + { +#ifndef GUI + char DirNameA[NM]; + WideToChar(DirName,DirNameA,ASIZE(DirNameA)); + DirNameA[ASIZE(DirNameA)-1]=0; + mprintf(St(MCreatDir),DirNameA); + mprintf(" %s",St(MOk)); +#endif + } + else + Success=false; + } + } + if (!SkipLastName) + if (!IsPathDiv(*PointToLastChar(Path))) + if (MakeDir(NULL,Path,true,DirAttr)!=MKDIR_SUCCESS) + Success=false; + return(Success); +} + + +bool CreatePath(const char *Path,const wchar *PathW,bool SkipLastName) +{ +#ifdef _WIN_ALL + // If we are in Windows, let's try Unicode path first. In Unix we do not + // need it (Unix MakeDir will fails with Unicode only name). + if (PathW!=NULL && *PathW!=0) + return(CreatePath(PathW,SkipLastName)); +#endif + if (Path!=NULL && *Path!=0) + return(CreatePath(Path,SkipLastName)); + return(false); +} + + +void SetDirTime(const char *Name,const wchar *NameW,RarTime *ftm,RarTime *ftc,RarTime *fta) +{ +#ifdef _WIN_ALL + if (!WinNT()) + return; + + bool sm=ftm!=NULL && ftm->IsSet(); + bool sc=ftc!=NULL && ftc->IsSet(); + bool sa=fta!=NULL && fta->IsSet(); + + unsigned int DirAttr=GetFileAttr(Name,NameW); + bool ResetAttr=(DirAttr!=0xffffffff && (DirAttr & FA_RDONLY)!=0); + if (ResetAttr) + SetFileAttr(Name,NameW,0); + + wchar DirNameW[NM]; + GetWideName(Name,NameW,DirNameW,ASIZE(DirNameW)); + HANDLE hFile=CreateFileW(DirNameW,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL); + if (hFile==INVALID_HANDLE_VALUE) + return; + FILETIME fm,fc,fa; + if (sm) + ftm->GetWin32(&fm); + if (sc) + ftc->GetWin32(&fc); + if (sa) + fta->GetWin32(&fa); + SetFileTime(hFile,sc ? &fc:NULL,sa ? &fa:NULL,sm ? &fm:NULL); + CloseHandle(hFile); + if (ResetAttr) + SetFileAttr(Name,NameW,DirAttr); +#endif +#if defined(_UNIX) || defined(_EMX) + File::SetCloseFileTimeByName(Name,ftm,fta); +#endif +} + + +bool IsRemovable(const char *Name) +{ +#ifdef _WIN_ALL + char Root[NM]; + GetPathRoot(Name,Root); + int Type=GetDriveTypeA(*Root!=0 ? Root:NULL); + return(Type==DRIVE_REMOVABLE || Type==DRIVE_CDROM); +#elif defined(_EMX) + char Drive=etoupper(Name[0]); + return((Drive=='A' || Drive=='B') && Name[1]==':'); +#else + return(false); +#endif +} + + + + +#ifndef SFX_MODULE +int64 GetFreeDisk(const char *Name) +{ +#ifdef _WIN_ALL + char Root[NM]; + GetPathRoot(Name,Root); + + typedef BOOL (WINAPI *GETDISKFREESPACEEX)( + LPCSTR,PULARGE_INTEGER,PULARGE_INTEGER,PULARGE_INTEGER + ); + static GETDISKFREESPACEEX pGetDiskFreeSpaceEx=NULL; + + if (pGetDiskFreeSpaceEx==NULL) + { + HMODULE hKernel=GetModuleHandleW(L"kernel32.dll"); + if (hKernel!=NULL) + pGetDiskFreeSpaceEx=(GETDISKFREESPACEEX)GetProcAddress(hKernel,"GetDiskFreeSpaceExA"); + } + if (pGetDiskFreeSpaceEx!=NULL) + { + GetFilePath(Name,Root,ASIZE(Root)); + ULARGE_INTEGER uiTotalSize,uiTotalFree,uiUserFree; + uiUserFree.u.LowPart=uiUserFree.u.HighPart=0; + if (pGetDiskFreeSpaceEx(*Root ? Root:NULL,&uiUserFree,&uiTotalSize,&uiTotalFree) && + uiUserFree.u.HighPart<=uiTotalFree.u.HighPart) + return(INT32TO64(uiUserFree.u.HighPart,uiUserFree.u.LowPart)); + } + + // We are here if we failed to load GetDiskFreeSpaceExA. + DWORD SectorsPerCluster,BytesPerSector,FreeClusters,TotalClusters; + if (!GetDiskFreeSpaceA(*Root ? Root:NULL,&SectorsPerCluster,&BytesPerSector,&FreeClusters,&TotalClusters)) + return(1457664); + int64 FreeSize=SectorsPerCluster*BytesPerSector; + FreeSize=FreeSize*FreeClusters; + return(FreeSize); +#elif defined(_BEOS) + char Root[NM]; + GetFilePath(Name,Root,ASIZE(Root)); + dev_t Dev=dev_for_path(*Root ? Root:"."); + if (Dev<0) + return(1457664); + fs_info Info; + if (fs_stat_dev(Dev,&Info)!=0) + return(1457664); + int64 FreeSize=Info.block_size; + FreeSize=FreeSize*Info.free_blocks; + return(FreeSize); +#elif defined(_UNIX) + return(1457664); +#elif defined(_EMX) + int Drive=IsDiskLetter(Name) ? etoupper(Name[0])-'A'+1:0; +#ifndef _DJGPP + if (_osmode == OS2_MODE) + { + FSALLOCATE fsa; + if (DosQueryFSInfo(Drive,1,&fsa,sizeof(fsa))!=0) + return(1457664); + int64 FreeSize=fsa.cSectorUnit*fsa.cbSector; + FreeSize=FreeSize*fsa.cUnitAvail; + return(FreeSize); + } + else +#endif + { + union REGS regs,outregs; + memset(®s,0,sizeof(regs)); + regs.h.ah=0x36; + regs.h.dl=Drive; +#ifdef _DJGPP + int86 (0x21,®s,&outregs); +#else + _int86 (0x21,®s,&outregs); +#endif + if (outregs.x.ax==0xffff) + return(1457664); + int64 FreeSize=outregs.x.ax*outregs.x.cx; + FreeSize=FreeSize*outregs.x.bx; + return(FreeSize); + } +#else + #define DISABLEAUTODETECT + return(1457664); +#endif +} +#endif + + + + + +bool FileExist(const char *Name,const wchar *NameW) +{ +#ifdef _WIN_ALL + if (WinNT() && NameW!=NULL && *NameW!=0) + return(GetFileAttributesW(NameW)!=0xffffffff); + else + return(Name!=NULL && GetFileAttributesA(Name)!=0xffffffff); +#elif defined(ENABLE_ACCESS) + return(access(Name,0)==0); +#else + FindData FD; + return(FindFile::FastFind(Name,NameW,&FD)); +#endif +} + + +bool FileExist(const wchar *Name) +{ + return FileExist(NULL,Name); +} + + +bool WildFileExist(const char *Name,const wchar *NameW) +{ + if (IsWildcard(Name,NameW)) + { + FindFile Find; + Find.SetMask(Name); + Find.SetMaskW(NameW); + FindData fd; + return(Find.Next(&fd)); + } + return(FileExist(Name,NameW)); +} + + +bool IsDir(uint Attr) +{ +#if defined (_WIN_ALL) || defined(_EMX) + return(Attr!=0xffffffff && (Attr & 0x10)!=0); +#endif +#if defined(_UNIX) + return((Attr & 0xF000)==0x4000); +#endif +} + + +bool IsUnreadable(uint Attr) +{ +#if defined(_UNIX) && defined(S_ISFIFO) && defined(S_ISSOCK) && defined(S_ISCHR) + return(S_ISFIFO(Attr) || S_ISSOCK(Attr) || S_ISCHR(Attr)); +#endif + return(false); +} + + +bool IsLabel(uint Attr) +{ +#if defined (_WIN_ALL) || defined(_EMX) + return((Attr & 8)!=0); +#else + return(false); +#endif +} + + +bool IsLink(uint Attr) +{ +#ifdef _UNIX + return((Attr & 0xF000)==0xA000); +#else + return(false); +#endif +} + + + + + + +bool IsDeleteAllowed(uint FileAttr) +{ +#if defined(_WIN_ALL) || defined(_EMX) + return((FileAttr & (FA_RDONLY|FA_SYSTEM|FA_HIDDEN))==0); +#else + return((FileAttr & (S_IRUSR|S_IWUSR))==(S_IRUSR|S_IWUSR)); +#endif +} + + +void PrepareToDelete(const char *Name,const wchar *NameW) +{ +#if defined(_WIN_ALL) || defined(_EMX) + SetFileAttr(Name,NameW,0); +#endif +#ifdef _UNIX + if (Name!=NULL) + chmod(Name,S_IRUSR|S_IWUSR|S_IXUSR); +#endif +} + + +uint GetFileAttr(const char *Name,const wchar *NameW) +{ +#ifdef _WIN_ALL + if (WinNT() && NameW!=NULL && *NameW!=0) + return(GetFileAttributesW(NameW)); + else + return(GetFileAttributesA(Name)); +#elif defined(_DJGPP) + return(_chmod(Name,0)); +#else + struct stat st; + if (stat(Name,&st)!=0) + return(0); +#ifdef _EMX + return(st.st_attr); +#else + return(st.st_mode); +#endif +#endif +} + + +bool SetFileAttr(const char *Name,const wchar *NameW,uint Attr) +{ + bool Success; +#ifdef _WIN_ALL + if (WinNT() && NameW!=NULL && *NameW!=0) + Success=SetFileAttributesW(NameW,Attr)!=0; + else + if (Name!=NULL) + Success=SetFileAttributesA(Name,Attr)!=0; + else + Success=false; +#elif defined(_DJGPP) + Success=_chmod(Name,1,Attr)!=-1; +#elif defined(_EMX) + Success=__chmod(Name,1,Attr)!=-1; +#elif defined(_UNIX) + Success=chmod(Name,(mode_t)Attr)==0; +#else + Success=false; +#endif + return(Success); +} + + + + +#ifndef SFX_MODULE +uint CalcFileCRC(File *SrcFile,int64 Size,CALCCRC_SHOWMODE ShowMode) +{ + SaveFilePos SavePos(*SrcFile); + const size_t BufSize=0x10000; + Array Data(BufSize); + int64 BlockCount=0; + uint DataCRC=0xffffffff; + +#if !defined(SILENT) && !defined(_WIN_CE) + int64 FileLength=SrcFile->FileLength(); + if (ShowMode!=CALCCRC_SHOWNONE) + { + mprintf(St(MCalcCRC)); + mprintf(" "); + } + +#endif + + SrcFile->Seek(0,SEEK_SET); + while (true) + { + size_t SizeToRead; + if (Size==INT64NDF) // If we process the entire file. + SizeToRead=BufSize; // Then always attempt to read the entire buffer. + else + SizeToRead=(size_t)Min((int64)BufSize,Size); + int ReadSize=SrcFile->Read(&Data[0],SizeToRead); + if (ReadSize==0) + break; + + ++BlockCount; + if ((BlockCount & 15)==0) + { +#if !defined(SILENT) && !defined(_WIN_CE) + if (ShowMode==CALCCRC_SHOWALL) + mprintf("\b\b\b\b%3d%%",ToPercent(BlockCount*int64(BufSize),FileLength)); +#endif + Wait(); + } + DataCRC=CRC(DataCRC,&Data[0],ReadSize); + if (Size!=INT64NDF) + Size-=ReadSize; + } +#if !defined(SILENT) && !defined(_WIN_CE) + if (ShowMode==CALCCRC_SHOWALL) + mprintf("\b\b\b\b "); +#endif + return(DataCRC^0xffffffff); +} +#endif + + +bool RenameFile(const char *SrcName,const wchar *SrcNameW,const char *DestName,const wchar *DestNameW) +{ + return(rename(SrcName,DestName)==0); +} + + +bool DelFile(const char *Name) +{ + return(DelFile(Name,NULL)); +} + + + + +bool DelFile(const char *Name,const wchar *NameW) +{ + return(Name!=NULL && remove(Name)==0); +} + + + + + + + + +#if defined(_WIN_ALL) && !defined(_WIN_CE) && !defined(SFX_MODULE) +bool SetFileCompression(char *Name,wchar *NameW,bool State) +{ + wchar FileNameW[NM]; + GetWideName(Name,NameW,FileNameW,ASIZE(FileNameW)); + HANDLE hFile=CreateFileW(FileNameW,FILE_READ_DATA|FILE_WRITE_DATA, + FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL); + if (hFile==INVALID_HANDLE_VALUE) + return(false); + SHORT NewState=State ? COMPRESSION_FORMAT_DEFAULT:COMPRESSION_FORMAT_NONE; + DWORD Result; + int RetCode=DeviceIoControl(hFile,FSCTL_SET_COMPRESSION,&NewState, + sizeof(NewState),NULL,0,&Result,NULL); + CloseHandle(hFile); + return(RetCode!=0); +} +#endif + + + + + + + + + diff --git a/src/unrar/filefn.hpp b/src/unrar/filefn.hpp new file mode 100644 index 0000000000..bdf193e80d --- /dev/null +++ b/src/unrar/filefn.hpp @@ -0,0 +1,48 @@ +#ifndef _RAR_FILEFN_ +#define _RAR_FILEFN_ + +enum MKDIR_CODE {MKDIR_SUCCESS,MKDIR_ERROR,MKDIR_BADPATH}; + +MKDIR_CODE MakeDir(const char *Name,const wchar *NameW,bool SetAttr,uint Attr); +bool CreatePath(const char *Path,bool SkipLastName); +bool CreatePath(const wchar *Path,bool SkipLastName); +bool CreatePath(const char *Path,const wchar *PathW,bool SkipLastName); +void SetDirTime(const char *Name,const wchar *NameW,RarTime *ftm,RarTime *ftc,RarTime *fta); +bool IsRemovable(const char *Name); + +#ifndef SFX_MODULE +int64 GetFreeDisk(const char *Name); +#endif + + +bool FileExist(const char *Name,const wchar *NameW=NULL); +bool FileExist(const wchar *Name); +bool WildFileExist(const char *Name,const wchar *NameW=NULL); +bool IsDir(uint Attr); +bool IsUnreadable(uint Attr); +bool IsLabel(uint Attr); +bool IsLink(uint Attr); +void SetSFXMode(const char *FileName); +void EraseDiskContents(const char *FileName); +bool IsDeleteAllowed(uint FileAttr); +void PrepareToDelete(const char *Name,const wchar *NameW=NULL); +uint GetFileAttr(const char *Name,const wchar *NameW=NULL); +bool SetFileAttr(const char *Name,const wchar *NameW,uint Attr); + +enum CALCCRC_SHOWMODE {CALCCRC_SHOWNONE,CALCCRC_SHOWTEXT,CALCCRC_SHOWALL}; +uint CalcFileCRC(File *SrcFile,int64 Size=INT64NDF,CALCCRC_SHOWMODE ShowMode=CALCCRC_SHOWNONE); + +bool RenameFile(const char *SrcName,const wchar *SrcNameW,const char *DestName,const wchar *DestNameW); +bool DelFile(const char *Name); +bool DelFile(const char *Name,const wchar *NameW); +bool DelDir(const char *Name); +bool DelDir(const char *Name,const wchar *NameW); + +#if defined(_WIN_ALL) && !defined(_WIN_CE) +bool SetFileCompression(char *Name,wchar *NameW,bool State); +#endif + + + + +#endif diff --git a/src/unrar/filestr.cpp b/src/unrar/filestr.cpp new file mode 100644 index 0000000000..49e865b9ae --- /dev/null +++ b/src/unrar/filestr.cpp @@ -0,0 +1,208 @@ +#include "rar.hpp" + +static bool IsUnicode(byte *Data,int Size); + +bool ReadTextFile( + const char *Name, + const wchar *NameW, + StringList *List, + bool Config, + bool AbortOnError, + RAR_CHARSET SrcCharset, + bool Unquote, + bool SkipComments, + bool ExpandEnvStr) +{ + char FileName[NM]; + *FileName=0; + if (Name!=NULL) + if (Config) + GetConfigName(Name,FileName,true); + else + strcpy(FileName,Name); + + wchar FileNameW[NM]; + *FileNameW=0; + +#ifdef _WIN_ALL + if (NameW!=NULL) + if (Config) + GetConfigName(NameW,FileNameW,true); + else + wcscpy(FileNameW,NameW); +#endif + + File SrcFile; + if (FileName!=NULL && *FileName!=0 || FileNameW!=NULL && *FileNameW!=0) + { + bool OpenCode=AbortOnError ? SrcFile.WOpen(FileName,FileNameW):SrcFile.Open(FileName,FileNameW,0); + + if (!OpenCode) + { + if (AbortOnError) + ErrHandler.Exit(RARX_OPEN); + return(false); + } + } + else + SrcFile.SetHandleType(FILE_HANDLESTD); + + unsigned int DataSize=0,ReadSize; + const int ReadBlock=1024; + Array Data(ReadBlock+5); + while ((ReadSize=SrcFile.Read(&Data[DataSize],ReadBlock))!=0) + { + DataSize+=ReadSize; + Data.Add(ReadSize); + } + + memset(&Data[DataSize],0,5); + + if (SrcCharset==RCH_UNICODE || + SrcCharset==RCH_DEFAULT && IsUnicode((byte *)&Data[0],DataSize)) + { + // Unicode in native system format, can be more than 2 bytes per character. + Array DataW(Data.Size()/2+1); + for (size_t I=2;I AnsiName; + + while (*CurStr!=0) + { + wchar *NextStr=CurStr,*CmtPtr=NULL; + while (*NextStr!='\r' && *NextStr!='\n' && *NextStr!=0) + { + if (SkipComments && NextStr[0]=='/' && NextStr[1]=='/') + { + *NextStr=0; + CmtPtr=NextStr; + } + NextStr++; + } + *NextStr=0; + for (wchar *SpacePtr=(CmtPtr ? CmtPtr:NextStr)-1;SpacePtr>=CurStr;SpacePtr--) + { + if (*SpacePtr!=' ' && *SpacePtr!='\t') + break; + *SpacePtr=0; + } + if (*CurStr) + { + // Length and AddSize must be defined as signed, because AddSize + // can be negative. + int Length=(int)wcslen(CurStr); + int AddSize=4*(Length-(int)AnsiName.Size()+1); + + if (AddSize>0) + AnsiName.Add(AddSize); + if (Unquote && *CurStr=='\"' && CurStr[Length-1]=='\"') + { + CurStr[Length-1]=0; + CurStr++; + } + WideToChar(CurStr,&AnsiName[0],AnsiName.Size()); + + bool Expanded=false; +#if defined(_WIN_ALL) && !defined(_WIN_CE) + if (ExpandEnvStr && *CurStr=='%') + { + // Expanding environment variables in Windows version. + + char ExpName[NM]; + wchar ExpNameW[NM]; + *ExpNameW=0; + int ret,retw=1; + ret=ExpandEnvironmentStringsA(&AnsiName[0],ExpName,ASIZE(ExpName)); + if (ret!=0 && WinNT()) + retw=ExpandEnvironmentStringsW(CurStr,ExpNameW,ASIZE(ExpNameW)); + Expanded=ret!=0 && retAddString(ExpName,ExpNameW); + } +#endif + if (!Expanded) + List->AddString(&AnsiName[0],CurStr); + } + CurStr=NextStr+1; + while (*CurStr=='\r' || *CurStr=='\n') + CurStr++; + } + } + else + { + char *CurStr=&Data[0]; + while (*CurStr!=0) + { + char *NextStr=CurStr,*CmtPtr=NULL; + while (*NextStr!='\r' && *NextStr!='\n' && *NextStr!=0) + { + if (SkipComments && NextStr[0]=='/' && NextStr[1]=='/') + { + *NextStr=0; + CmtPtr=NextStr; + } + NextStr++; + } + *NextStr=0; + for (char *SpacePtr=(CmtPtr ? CmtPtr:NextStr)-1;SpacePtr>=CurStr;SpacePtr--) + { + if (*SpacePtr!=' ' && *SpacePtr!='\t') + break; + *SpacePtr=0; + } + if (*CurStr) + { + if (Unquote && *CurStr=='\"') + { + size_t Length=strlen(CurStr); + if (CurStr[Length-1]=='\"') + { + CurStr[Length-1]=0; + CurStr++; + } + } +#if defined(_WIN_ALL) + if (SrcCharset==RCH_OEM) + OemToCharA(CurStr,CurStr); +#endif + + bool Expanded=false; +#if defined(_WIN_ALL) && !defined(_WIN_CE) + if (ExpandEnvStr && *CurStr=='%') + { + // Expanding environment variables in Windows version. + char ExpName[NM]; + int ret=ExpandEnvironmentStringsA(CurStr,ExpName,ASIZE(ExpName)); + Expanded=ret!=0 && retAddString(ExpName); + } +#endif + if (!Expanded) + List->AddString(CurStr); + } + CurStr=NextStr+1; + while (*CurStr=='\r' || *CurStr=='\n') + CurStr++; + } + } + return(true); +} + + +bool IsUnicode(byte *Data,int Size) +{ + if (Size<4 || Data[0]!=0xff || Data[1]!=0xfe) + return(false); + for (int I=2;IError=false; + if (*FindMask==0) + return(false); +#ifdef _WIN_ALL + if (FirstCall) + { + if ((hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,FindMaskW,fd))==INVALID_HANDLE_VALUE) + return(false); + } + else + if (Win32Find(hFind,FindMask,FindMaskW,fd)==INVALID_HANDLE_VALUE) + return(false); +#else + if (FirstCall) + { + char DirName[NM]; + strcpy(DirName,FindMask); + RemoveNameFromPath(DirName); + if (*DirName==0) + strcpy(DirName,"."); + if ((dirp=opendir(DirName))==NULL) + { + fd->Error=(errno!=ENOENT); + return(false); + } + } + while (1) + { + struct dirent *ent=readdir(dirp); + if (ent==NULL) + return(false); + if (strcmp(ent->d_name,".")==0 || strcmp(ent->d_name,"..")==0) + continue; + if (CmpName(FindMask,ent->d_name,MATCH_NAMES)) + { + char FullName[NM]; + strcpy(FullName,FindMask); + *PointToName(FullName)=0; + if (strlen(FullName)+strlen(ent->d_name)>=ASIZE(FullName)-1) + { +#ifndef SILENT + Log(NULL,"\n%s%s",FullName,ent->d_name); + Log(NULL,St(MPathTooLong)); +#endif + return(false); + } + strcat(FullName,ent->d_name); + if (!FastFind(FullName,NULL,fd,GetSymLink)) + { + ErrHandler.OpenErrorMsg(FullName); + continue; + } + strcpy(fd->Name,FullName); + break; + } + } + *fd->NameW=0; +#ifdef _APPLE + if (!LowAscii(fd->Name)) + UtfToWide(fd->Name,fd->NameW,sizeof(fd->NameW)); +#elif defined(UNICODE_SUPPORTED) + if (!LowAscii(fd->Name) && UnicodeEnabled()) + CharToWide(fd->Name,fd->NameW); +#endif +#endif + fd->Flags=0; + fd->IsDir=IsDir(fd->FileAttr); + FirstCall=false; + char *Name=PointToName(fd->Name); + if (strcmp(Name,".")==0 || strcmp(Name,"..")==0) + return(Next(fd)); + return(true); +} + + +bool FindFile::FastFind(const char *FindMask,const wchar *FindMaskW,FindData *fd,bool GetSymLink) +{ + fd->Error=false; +#ifndef _UNIX + if (IsWildcard(FindMask,FindMaskW)) + return(false); +#endif +#ifdef _WIN_ALL + HANDLE hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,FindMaskW,fd); + if (hFind==INVALID_HANDLE_VALUE) + return(false); + FindClose(hFind); +#else + struct stat st; + if (GetSymLink) + { +#ifdef SAVE_LINKS + if (lstat(FindMask,&st)!=0) +#else + if (stat(FindMask,&st)!=0) +#endif + { + fd->Error=(errno!=ENOENT); + return(false); + } + } + else + if (stat(FindMask,&st)!=0) + { + fd->Error=(errno!=ENOENT); + return(false); + } +#ifdef _DJGPP + fd->FileAttr=_chmod(FindMask,0); +#elif defined(_EMX) + fd->FileAttr=st.st_attr; +#else + fd->FileAttr=st.st_mode; +#endif + fd->IsDir=IsDir(st.st_mode); + fd->Size=st.st_size; + fd->mtime=st.st_mtime; + fd->atime=st.st_atime; + fd->ctime=st.st_ctime; + fd->FileTime=fd->mtime.GetDos(); + strcpy(fd->Name,FindMask); + + *fd->NameW=0; +#ifdef _APPLE + if (!LowAscii(fd->Name)) + UtfToWide(fd->Name,fd->NameW,sizeof(fd->NameW)); +#elif defined(UNICODE_SUPPORTED) + if (!LowAscii(fd->Name) && UnicodeEnabled()) + CharToWide(fd->Name,fd->NameW); +#endif +#endif + fd->Flags=0; + fd->IsDir=IsDir(fd->FileAttr); + return(true); +} + + +#ifdef _WIN_ALL +HANDLE FindFile::Win32Find(HANDLE hFind,const char *Mask,const wchar *MaskW,FindData *fd) +{ +#ifndef _WIN_CE + if (WinNT()) +#endif + { + wchar WideMask[NM]; + if (MaskW!=NULL && *MaskW!=0) + wcscpy(WideMask,MaskW); + else + CharToWide(Mask,WideMask); + + WIN32_FIND_DATAW FindData; + if (hFind==INVALID_HANDLE_VALUE) + { + hFind=FindFirstFileW(WideMask,&FindData); + if (hFind==INVALID_HANDLE_VALUE) + { + int SysErr=GetLastError(); + fd->Error=(SysErr!=ERROR_FILE_NOT_FOUND && + SysErr!=ERROR_PATH_NOT_FOUND && + SysErr!=ERROR_NO_MORE_FILES); + } + } + else + if (!FindNextFileW(hFind,&FindData)) + { + hFind=INVALID_HANDLE_VALUE; + fd->Error=GetLastError()!=ERROR_NO_MORE_FILES; + } + + if (hFind!=INVALID_HANDLE_VALUE) + { + wcscpy(fd->NameW,WideMask); + wcscpy(PointToName(fd->NameW),FindData.cFileName); + WideToChar(fd->NameW,fd->Name); + fd->Size=INT32TO64(FindData.nFileSizeHigh,FindData.nFileSizeLow); + fd->FileAttr=FindData.dwFileAttributes; + wcscpy(fd->ShortName,FindData.cAlternateFileName); + fd->ftCreationTime=FindData.ftCreationTime; + fd->ftLastAccessTime=FindData.ftLastAccessTime; + fd->ftLastWriteTime=FindData.ftLastWriteTime; + fd->mtime=FindData.ftLastWriteTime; + fd->ctime=FindData.ftCreationTime; + fd->atime=FindData.ftLastAccessTime; + fd->FileTime=fd->mtime.GetDos(); + +#ifndef _WIN_CE +// if (LowAscii(fd->NameW)) +// *fd->NameW=0; +#endif + } + } +#ifndef _WIN_CE + else + { + char CharMask[NM]; + if (Mask!=NULL && *Mask!=0) + strcpy(CharMask,Mask); + else + WideToChar(MaskW,CharMask); + + WIN32_FIND_DATAA FindData; + if (hFind==INVALID_HANDLE_VALUE) + { + hFind=FindFirstFileA(CharMask,&FindData); + if (hFind==INVALID_HANDLE_VALUE) + { + int SysErr=GetLastError(); + fd->Error=SysErr!=ERROR_FILE_NOT_FOUND && SysErr!=ERROR_PATH_NOT_FOUND; + } + } + else + if (!FindNextFileA(hFind,&FindData)) + { + hFind=INVALID_HANDLE_VALUE; + fd->Error=GetLastError()!=ERROR_NO_MORE_FILES; + } + + if (hFind!=INVALID_HANDLE_VALUE) + { + strcpy(fd->Name,CharMask); + strcpy(PointToName(fd->Name),FindData.cFileName); + CharToWide(fd->Name,fd->NameW); + fd->Size=INT32TO64(FindData.nFileSizeHigh,FindData.nFileSizeLow); + fd->FileAttr=FindData.dwFileAttributes; + CharToWide(FindData.cAlternateFileName,fd->ShortName); + fd->ftCreationTime=FindData.ftCreationTime; + fd->ftLastAccessTime=FindData.ftLastAccessTime; + fd->ftLastWriteTime=FindData.ftLastWriteTime; + fd->mtime=FindData.ftLastWriteTime; + fd->ctime=FindData.ftCreationTime; + fd->atime=FindData.ftLastAccessTime; + fd->FileTime=fd->mtime.GetDos(); +// if (LowAscii(fd->Name)) +// *fd->NameW=0; + } + } +#endif + fd->Flags=0; + return(hFind); +} +#endif + diff --git a/src/unrar/find.hpp b/src/unrar/find.hpp new file mode 100644 index 0000000000..05f5d7f0e5 --- /dev/null +++ b/src/unrar/find.hpp @@ -0,0 +1,53 @@ +#ifndef _RAR_FINDDATA_ +#define _RAR_FINDDATA_ + +enum FINDDATA_FLAGS { + FDDF_SECONDDIR=1 // Second encounter of same directory in SCAN_GETDIRSTWICE ScanTree mode. +}; + +struct FindData +{ + char Name[NM]; + wchar NameW[NM]; + int64 Size; + uint FileAttr; + uint FileTime; + bool IsDir; + RarTime mtime; + RarTime ctime; + RarTime atime; +#ifdef _WIN_ALL + wchar ShortName[NM]; + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; +#endif + uint Flags; + bool Error; +}; + +class FindFile +{ + private: +#ifdef _WIN_ALL + static HANDLE Win32Find(HANDLE hFind,const char *Mask,const wchar *MaskW,struct FindData *fd); +#endif + + char FindMask[NM]; + wchar FindMaskW[NM]; + bool FirstCall; +#ifdef _WIN_ALL + HANDLE hFind; +#else + DIR *dirp; +#endif + public: + FindFile(); + ~FindFile(); + void SetMask(const char *FindMask); + void SetMaskW(const wchar *FindMaskW); + bool Next(FindData *fd,bool GetSymLink=false); + static bool FastFind(const char *FindMask,const wchar *FindMaskW,FindData *fd,bool GetSymLink=false); +}; + +#endif diff --git a/src/unrar/getbits.cpp b/src/unrar/getbits.cpp new file mode 100644 index 0000000000..02b3a0b46b --- /dev/null +++ b/src/unrar/getbits.cpp @@ -0,0 +1,35 @@ +#include "rar.hpp" + +BitInput::BitInput() +{ + // getbits attempts to read data from InAddr, InAddr+1, InAddr+2 positions. + // So let's allocate two additional bytes for situation, when we need to + // read only 1 byte from the last position of buffer and avoid a crash + // from access to next 2 bytes, which contents we do not need. + size_t BufSize=MAX_SIZE+2; + InBuf=new byte[BufSize]; + + // Ensure that we get predictable results when accessing bytes in area + // not filled with read data. + memset(InBuf,0,BufSize); +} + + +BitInput::~BitInput() +{ + delete[] InBuf; +} + + +void BitInput::faddbits(uint Bits) +{ + // Function wrapped version of inline addbits to save code size. + addbits(Bits); +} + + +uint BitInput::fgetbits() +{ + // Function wrapped version of inline getbits to save code size. + return(getbits()); +} diff --git a/src/unrar/getbits.hpp b/src/unrar/getbits.hpp new file mode 100644 index 0000000000..d44fb9f060 --- /dev/null +++ b/src/unrar/getbits.hpp @@ -0,0 +1,51 @@ +#ifndef _RAR_GETBITS_ +#define _RAR_GETBITS_ + +class BitInput +{ + public: + enum BufferSize {MAX_SIZE=0x8000}; // Size of input buffer. + protected: + int InAddr; // Curent byte position in the buffer. + int InBit; // Current bit position in the current byte. + public: + BitInput(); + ~BitInput(); + + byte *InBuf; // Dynamically allocated input buffer. + + void InitBitInput() + { + InAddr=InBit=0; + } + + // Move forward by 'Bits' bits. + void addbits(uint Bits) + { + Bits+=InBit; + InAddr+=Bits>>3; + InBit=Bits&7; + } + + // Return 16 bits from current position in the buffer. + // Bit at (InAddr,InBit) has the highest position in returning data. + uint getbits() + { + uint BitField=(uint)InBuf[InAddr] << 16; + BitField|=(uint)InBuf[InAddr+1] << 8; + BitField|=(uint)InBuf[InAddr+2]; + BitField >>= (8-InBit); + return(BitField & 0xffff); + } + + void faddbits(uint Bits); + uint fgetbits(); + + // Check if buffer has enough space for IncPtr bytes. Returns 'true' + // if buffer will be overflown. + bool Overflow(uint IncPtr) + { + return(InAddr+IncPtr>=MAX_SIZE); + } +}; +#endif diff --git a/src/unrar/global.cpp b/src/unrar/global.cpp new file mode 100644 index 0000000000..593a057cbf --- /dev/null +++ b/src/unrar/global.cpp @@ -0,0 +1,4 @@ +#define INCLUDEGLOBAL + + +#include "rar.hpp" diff --git a/src/unrar/global.hpp b/src/unrar/global.hpp new file mode 100644 index 0000000000..30eff082a6 --- /dev/null +++ b/src/unrar/global.hpp @@ -0,0 +1,15 @@ +#ifndef _RAR_GLOBAL_ +#define _RAR_GLOBAL_ + +#ifdef INCLUDEGLOBAL + #define EXTVAR +#else + #define EXTVAR extern +#endif + +EXTVAR ErrorHandler ErrHandler; + + + + +#endif diff --git a/src/unrar/headers.hpp b/src/unrar/headers.hpp new file mode 100644 index 0000000000..75343a258e --- /dev/null +++ b/src/unrar/headers.hpp @@ -0,0 +1,327 @@ +#ifndef _RAR_HEADERS_ +#define _RAR_HEADERS_ + +#define SIZEOF_MARKHEAD 7 +#define SIZEOF_OLDMHD 7 +#define SIZEOF_NEWMHD 13 +#define SIZEOF_OLDLHD 21 +#define SIZEOF_NEWLHD 32 +#define SIZEOF_SHORTBLOCKHEAD 7 +#define SIZEOF_LONGBLOCKHEAD 11 +#define SIZEOF_SUBBLOCKHEAD 14 +#define SIZEOF_COMMHEAD 13 +#define SIZEOF_PROTECTHEAD 26 +#define SIZEOF_AVHEAD 14 +#define SIZEOF_SIGNHEAD 15 +#define SIZEOF_UOHEAD 18 +#define SIZEOF_MACHEAD 22 +#define SIZEOF_EAHEAD 24 +#define SIZEOF_BEEAHEAD 24 +#define SIZEOF_STREAMHEAD 26 + +#define PACK_VER 29 +#define PACK_CRYPT_VER 29 +#define UNP_VER 36 +#define CRYPT_VER 29 +#define AV_VER 20 +#define PROTECT_VER 20 + + +#define MHD_VOLUME 0x0001U + +// Old style main archive comment embed into main archive header. Must not +// be used in new archives anymore. Must never be used with MHD_ENCRYPTVER +// or other flags changing the size of main header. RAR expects the fixed +// size of main header preceding the comment if MHD_COMMENT is found. +#define MHD_COMMENT 0x0002U + +#define MHD_LOCK 0x0004U +#define MHD_SOLID 0x0008U +#define MHD_PACK_COMMENT 0x0010U +#define MHD_NEWNUMBERING 0x0010U +#define MHD_AV 0x0020U +#define MHD_PROTECT 0x0040U +#define MHD_PASSWORD 0x0080U +#define MHD_FIRSTVOLUME 0x0100U +#define MHD_ENCRYPTVER 0x0200U + +#define LHD_SPLIT_BEFORE 0x0001U +#define LHD_SPLIT_AFTER 0x0002U +#define LHD_PASSWORD 0x0004U + +// Old style file comment embed into file header. Must not be used +// in new archives anymore. +#define LHD_COMMENT 0x0008U + +#define LHD_SOLID 0x0010U + + +#define LHD_WINDOWMASK 0x00e0U +#define LHD_WINDOW64 0x0000U +#define LHD_WINDOW128 0x0020U +#define LHD_WINDOW256 0x0040U +#define LHD_WINDOW512 0x0060U +#define LHD_WINDOW1024 0x0080U +#define LHD_WINDOW2048 0x00a0U +#define LHD_WINDOW4096 0x00c0U +#define LHD_DIRECTORY 0x00e0U + +#define LHD_LARGE 0x0100U +#define LHD_UNICODE 0x0200U +#define LHD_SALT 0x0400U +#define LHD_VERSION 0x0800U +#define LHD_EXTTIME 0x1000U +#define LHD_EXTAREA 0x2000U + +#define SKIP_IF_UNKNOWN 0x4000U +#define LONG_BLOCK 0x8000U + +#define EARC_NEXT_VOLUME 0x0001U // Not last volume. +#define EARC_DATACRC 0x0002U // Store CRC32 of RAR archive (now is used only in volumes). +#define EARC_REVSPACE 0x0004U // Reserve space for end of REV file 7 byte record. +#define EARC_VOLNUMBER 0x0008U // Store a number of current volume. + +enum HEADER_TYPE { + MARK_HEAD=0x72,MAIN_HEAD=0x73,FILE_HEAD=0x74,COMM_HEAD=0x75,AV_HEAD=0x76, + SUB_HEAD=0x77,PROTECT_HEAD=0x78,SIGN_HEAD=0x79,NEWSUB_HEAD=0x7a, + ENDARC_HEAD=0x7b +}; + +enum { EA_HEAD=0x100,UO_HEAD=0x101,MAC_HEAD=0x102,BEEA_HEAD=0x103, + NTACL_HEAD=0x104,STREAM_HEAD=0x105 }; + +enum HOST_SYSTEM { + HOST_MSDOS=0,HOST_OS2=1,HOST_WIN32=2,HOST_UNIX=3,HOST_MACOS=4, + HOST_BEOS=5,HOST_MAX +}; + +#define SUBHEAD_TYPE_CMT "CMT" +#define SUBHEAD_TYPE_ACL "ACL" +#define SUBHEAD_TYPE_STREAM "STM" +#define SUBHEAD_TYPE_UOWNER "UOW" +#define SUBHEAD_TYPE_AV "AV" +#define SUBHEAD_TYPE_RR "RR" +#define SUBHEAD_TYPE_OS2EA "EA2" +#define SUBHEAD_TYPE_BEOSEA "EABE" + +/* new file inherits a subblock when updating a host file */ +#define SUBHEAD_FLAGS_INHERITED 0x80000000 + +#define SUBHEAD_FLAGS_CMT_UNICODE 0x00000001 + +struct OldMainHeader +{ + byte Mark[4]; + ushort HeadSize; + byte Flags; +}; + + +struct OldFileHeader +{ + uint PackSize; + uint UnpSize; + ushort FileCRC; + ushort HeadSize; + uint FileTime; + byte FileAttr; + byte Flags; + byte UnpVer; + byte NameSize; + byte Method; +}; + + +struct MarkHeader +{ + byte Mark[7]; +}; + + +struct BaseBlock +{ + ushort HeadCRC; + HEADER_TYPE HeadType; // 1 byte. + ushort Flags; + ushort HeadSize; + + bool IsSubBlock() + { + if (HeadType==SUB_HEAD) + return(true); + if (HeadType==NEWSUB_HEAD && (Flags & LHD_SOLID)!=0) + return(true); + return(false); + } +}; + +struct BlockHeader:BaseBlock +{ + union { + uint DataSize; + uint PackSize; + }; +}; + + +struct MainHeader:BaseBlock +{ + ushort HighPosAV; + uint PosAV; + byte EncryptVer; +}; + + +#define SALT_SIZE 8 + +struct FileHeader:BlockHeader +{ + uint UnpSize; + byte HostOS; + uint FileCRC; + uint FileTime; + byte UnpVer; + byte Method; + ushort NameSize; + union { + uint FileAttr; + uint SubFlags; + }; +/* optional */ + uint HighPackSize; + uint HighUnpSize; +/* names */ + char FileName[NM]; + wchar FileNameW[NM]; +/* optional */ + Array SubData; + byte Salt[SALT_SIZE]; + + RarTime mtime; + RarTime ctime; + RarTime atime; + RarTime arctime; +/* dummy */ + int64 FullPackSize; + int64 FullUnpSize; + + void Clear(size_t SubDataSize) + { + SubData.Alloc(SubDataSize); + Flags=LONG_BLOCK; + SubFlags=0; + } + + bool CmpName(const char *Name) + { + return(strcmp(FileName,Name)==0); + } + + FileHeader& operator = (FileHeader &hd) + { + SubData.Reset(); + memcpy(this,&hd,sizeof(*this)); + SubData.CleanData(); + SubData=hd.SubData; + return(*this); + } +}; + + +struct EndArcHeader:BaseBlock +{ + // Optional CRC32 of entire archive up to start of EndArcHeader block. + // Present if EARC_DATACRC flag is set. + uint ArcDataCRC; + + // Optional number of current volume. + // Present if EARC_VOLNUMBER flag is set. + ushort VolNumber; + + // 7 additional zero bytes can be stored here if EARC_REVSPACE is set. +}; + + +// SubBlockHeader and its successors were used in RAR 2.x format. +// RAR 3.x uses FileHeader with NEWSUB_HEAD HeadType for subblocks. +struct SubBlockHeader:BlockHeader +{ + ushort SubType; + byte Level; +}; + + +struct CommentHeader:BaseBlock +{ + ushort UnpSize; + byte UnpVer; + byte Method; + ushort CommCRC; +}; + + +struct ProtectHeader:BlockHeader +{ + byte Version; + ushort RecSectors; + uint TotalBlocks; + byte Mark[8]; +}; + + +struct AVHeader:BaseBlock +{ + byte UnpVer; + byte Method; + byte AVVer; + uint AVInfoCRC; +}; + + +struct SignHeader:BaseBlock +{ + uint CreationTime; + ushort ArcNameSize; + ushort UserNameSize; +}; + + +struct UnixOwnersHeader:SubBlockHeader +{ + ushort OwnerNameSize; + ushort GroupNameSize; +/* dummy */ + char OwnerName[NM]; + char GroupName[NM]; +}; + + +struct EAHeader:SubBlockHeader +{ + uint UnpSize; + byte UnpVer; + byte Method; + uint EACRC; +}; + + +struct StreamHeader:SubBlockHeader +{ + uint UnpSize; + byte UnpVer; + byte Method; + uint StreamCRC; + ushort StreamNameSize; +/* dummy */ + byte StreamName[NM]; +}; + + +struct MacFInfoHeader:SubBlockHeader +{ + uint fileType; + uint fileCreator; +}; + + +#endif diff --git a/src/unrar/isnt.cpp b/src/unrar/isnt.cpp new file mode 100644 index 0000000000..53c722ab42 --- /dev/null +++ b/src/unrar/isnt.cpp @@ -0,0 +1,24 @@ +#include "rar.hpp" + +#ifdef _WIN_ALL +DWORD WinNT() +{ + static int dwPlatformId=-1; + static DWORD dwMajorVersion,dwMinorVersion; + if (dwPlatformId==-1) + { + OSVERSIONINFO WinVer; + WinVer.dwOSVersionInfoSize=sizeof(WinVer); + GetVersionEx(&WinVer); + dwPlatformId=WinVer.dwPlatformId; + dwMajorVersion=WinVer.dwMajorVersion; + dwMinorVersion=WinVer.dwMinorVersion; + } + DWORD Result=0; + if (dwPlatformId==VER_PLATFORM_WIN32_NT) + Result=dwMajorVersion*0x100+dwMinorVersion; + + + return(Result); +} +#endif diff --git a/src/unrar/isnt.hpp b/src/unrar/isnt.hpp new file mode 100644 index 0000000000..520ac8ad7d --- /dev/null +++ b/src/unrar/isnt.hpp @@ -0,0 +1,11 @@ +#ifndef _RAR_ISNT_ +#define _RAR_ISNT_ + +enum WINNT_VERSION { + WNT_NONE=0,WNT_NT351=0x0333,WNT_NT4=0x0400,WNT_W2000=0x0500, + WNT_WXP=0x0501,WNT_W2003=0x0502,WNT_VISTA=0x0600,WNT_W7=0x0601 +}; + +DWORD WinNT(); + +#endif diff --git a/src/unrar/license.txt b/src/unrar/license.txt new file mode 100644 index 0000000000..0811276a13 --- /dev/null +++ b/src/unrar/license.txt @@ -0,0 +1,42 @@ + ****** ***** ****** UnRAR - free utility for RAR archives + ** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ****** ******* ****** License for use and distribution of + ** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ** ** ** ** ** ** FREE portable version + ~~~~~~~~~~~~~~~~~~~~~ + + The source code of UnRAR utility is freeware. This means: + + 1. All copyrights to RAR and the utility UnRAR are exclusively + owned by the author - Alexander Roshal. + + 2. UnRAR source code may be used in any software to handle + RAR archives without limitations free of charge, but cannot be + used to develop RAR (WinRAR) compatible archiver and to + re-create RAR compression algorithm, which is proprietary. + Distribution of modified UnRAR source code in separate form + or as a part of other software is permitted, provided that + full text of this paragraph, starting from "UnRAR source code" + words, is included in license, or in documentation if license + is not available, and in source code comments of resulting package. + + 3. The UnRAR utility may be freely distributed. It is allowed + to distribute UnRAR inside of other software packages. + + 4. THE RAR ARCHIVER AND THE UnRAR UTILITY ARE DISTRIBUTED "AS IS". + NO WARRANTY OF ANY KIND IS EXPRESSED OR IMPLIED. YOU USE AT + YOUR OWN RISK. THE AUTHOR WILL NOT BE LIABLE FOR DATA LOSS, + DAMAGES, LOSS OF PROFITS OR ANY OTHER KIND OF LOSS WHILE USING + OR MISUSING THIS SOFTWARE. + + 5. Installing and using the UnRAR utility signifies acceptance of + these terms and conditions of the license. + + 6. If you don't agree with terms of the license you must remove + UnRAR files from your storage devices and cease to use the + utility. + + Thank you for your interest in RAR and UnRAR. + + + Alexander L. Roshal diff --git a/src/unrar/list.cpp b/src/unrar/list.cpp new file mode 100644 index 0000000000..000e92d3f8 --- /dev/null +++ b/src/unrar/list.cpp @@ -0,0 +1,391 @@ +#include "rar.hpp" + +static void ListFileHeader(FileHeader &hd,bool Verbose,bool Technical,bool &TitleShown,bool Bare); +static void ListSymLink(Archive &Arc); +static void ListFileAttr(uint A,int HostOS); +static void ListOldSubHeader(Archive &Arc); +static void ListNewSubHeader(CommandData *Cmd,Archive &Arc,bool Technical); + +void ListArchive(CommandData *Cmd) +{ + int64 SumPackSize=0,SumUnpSize=0; + uint ArcCount=0,SumFileCount=0; + bool Technical=(Cmd->Command[1]=='T'); + bool Bare=(Cmd->Command[1]=='B'); + bool Verbose=(*Cmd->Command=='V'); + + char ArcName[NM]; + wchar ArcNameW[NM]; + + while (Cmd->GetArcName(ArcName,ArcNameW,sizeof(ArcName))) + { + Archive Arc(Cmd); +#ifdef _WIN_ALL + Arc.RemoveSequentialFlag(); +#endif + if (!Arc.WOpen(ArcName,ArcNameW)) + continue; + bool FileMatched=true; + while (1) + { + int64 TotalPackSize=0,TotalUnpSize=0; + uint FileCount=0; + if (Arc.IsArchive(true)) + { +// if (!Arc.IsOpened()) +// break; + bool TitleShown=false; + if (!Bare) + { + Arc.ViewComment(); + mprintf("\n"); + if (Arc.Solid) + mprintf(St(MListSolid)); + if (Arc.SFXSize>0) + mprintf(St(MListSFX)); + if (Arc.Volume) + if (Arc.Solid) + mprintf(St(MListVol1)); + else + mprintf(St(MListVol2)); + else + if (Arc.Solid) + mprintf(St(MListArc1)); + else + mprintf(St(MListArc2)); + mprintf(" %s\n",Arc.FileName); + if (Technical) + { + if (Arc.Protected) + mprintf(St(MListRecRec)); + if (Arc.Locked) + mprintf(St(MListLock)); + } + } + while(Arc.ReadHeader()>0) + { + int HeaderType=Arc.GetHeaderType(); + if (HeaderType==ENDARC_HEAD) + break; + switch(HeaderType) + { + case FILE_HEAD: + IntToExt(Arc.NewLhd.FileName,Arc.NewLhd.FileName); + FileMatched=Cmd->IsProcessFile(Arc.NewLhd)!=0; + if (FileMatched) + { + ListFileHeader(Arc.NewLhd,Verbose,Technical,TitleShown,Bare); + if (!(Arc.NewLhd.Flags & LHD_SPLIT_BEFORE)) + { + TotalUnpSize+=Arc.NewLhd.FullUnpSize; + FileCount++; + } + TotalPackSize+=Arc.NewLhd.FullPackSize; + if (Technical) + ListSymLink(Arc); +#ifndef SFX_MODULE + if (Verbose) + Arc.ViewFileComment(); +#endif + } + break; +#ifndef SFX_MODULE + case SUB_HEAD: + if (Technical && FileMatched && !Bare) + ListOldSubHeader(Arc); + break; +#endif + case NEWSUB_HEAD: + if (FileMatched && !Bare) + { + if (Technical) + ListFileHeader(Arc.SubHead,Verbose,true,TitleShown,false); + ListNewSubHeader(Cmd,Arc,Technical); + } + break; + } + Arc.SeekToNext(); + } + if (!Bare) + if (TitleShown) + { + mprintf("\n"); + for (int I=0;I<79;I++) + mprintf("-"); + char UnpSizeText[20]; + itoa(TotalUnpSize,UnpSizeText); + + char PackSizeText[20]; + itoa(TotalPackSize,PackSizeText); + + mprintf("\n%5lu %16s %8s %3d%%",FileCount,UnpSizeText, + PackSizeText,ToPercentUnlim(TotalPackSize,TotalUnpSize)); + SumFileCount+=FileCount; + SumUnpSize+=TotalUnpSize; + SumPackSize+=TotalPackSize; +#ifndef SFX_MODULE + if (Arc.EndArcHead.Flags & EARC_VOLNUMBER) + { + mprintf(" "); + mprintf(St(MVolumeNumber),Arc.EndArcHead.VolNumber+1); + } +#endif + mprintf("\n"); + } + else + mprintf(St(MListNoFiles)); + + ArcCount++; + +#ifndef NOVOLUME + if (Cmd->VolSize!=0 && ((Arc.NewLhd.Flags & LHD_SPLIT_AFTER) || + Arc.GetHeaderType()==ENDARC_HEAD && + (Arc.EndArcHead.Flags & EARC_NEXT_VOLUME)!=0) && + MergeArchive(Arc,NULL,false,*Cmd->Command)) + { + Arc.Seek(0,SEEK_SET); + } + else +#endif + break; + } + else + { + if (Cmd->ArcNames->ItemsCount()<2 && !Bare) + mprintf(St(MNotRAR),Arc.FileName); + break; + } + } + } + if (ArcCount>1 && !Bare) + { + char UnpSizeText[20],PackSizeText[20]; + itoa(SumUnpSize,UnpSizeText); + itoa(SumPackSize,PackSizeText); + mprintf("\n%5lu %16s %8s %3d%%\n",SumFileCount,UnpSizeText, + PackSizeText,ToPercentUnlim(SumPackSize,SumUnpSize)); + } +} + + +void ListFileHeader(FileHeader &hd,bool Verbose,bool Technical,bool &TitleShown,bool Bare) +{ + if (!Bare) + { + if (!TitleShown) + { + if (Verbose) + mprintf(St(MListPathComm)); + else + mprintf(St(MListName)); + mprintf(St(MListTitle)); + if (Technical) + mprintf(St(MListTechTitle)); + for (int I=0;I<79;I++) + mprintf("-"); + TitleShown=true; + } + + if (hd.HeadType==NEWSUB_HEAD) + mprintf(St(MSubHeadType),hd.FileName); + + mprintf("\n%c",(hd.Flags & LHD_PASSWORD) ? '*' : ' '); + } + + char *Name=hd.FileName; + +#ifdef UNICODE_SUPPORTED + char ConvertedName[NM]; + if ((hd.Flags & LHD_UNICODE)!=0 && *hd.FileNameW!=0 && UnicodeEnabled()) + { + if (WideToChar(hd.FileNameW,ConvertedName) && *ConvertedName!=0) + Name=ConvertedName; + } +#endif + + if (Bare) + { + mprintf("%s\n",Verbose ? Name:PointToName(Name)); + return; + } + + if (Verbose) + mprintf("%s\n%12s ",Name,""); + else + mprintf("%-12s",PointToName(Name)); + + char UnpSizeText[20],PackSizeText[20]; + if (hd.FullUnpSize==INT64NDF) + strcpy(UnpSizeText,"?"); + else + itoa(hd.FullUnpSize,UnpSizeText); + itoa(hd.FullPackSize,PackSizeText); + + mprintf(" %8s %8s ",UnpSizeText,PackSizeText); + + if ((hd.Flags & LHD_SPLIT_BEFORE) && (hd.Flags & LHD_SPLIT_AFTER)) + mprintf(" <->"); + else + if (hd.Flags & LHD_SPLIT_BEFORE) + mprintf(" <--"); + else + if (hd.Flags & LHD_SPLIT_AFTER) + mprintf(" -->"); + else + mprintf("%3d%%",ToPercentUnlim(hd.FullPackSize,hd.FullUnpSize)); + + char DateStr[50]; + hd.mtime.GetText(DateStr,false); + mprintf(" %s ",DateStr); + + if (hd.HeadType==NEWSUB_HEAD) + mprintf(" %c....B ",(hd.SubFlags & SUBHEAD_FLAGS_INHERITED) ? 'I' : '.'); + else + ListFileAttr(hd.FileAttr,hd.HostOS); + + mprintf(" %8.8X",hd.FileCRC); + mprintf(" m%d",hd.Method-0x30); + if ((hd.Flags & LHD_WINDOWMASK)<=6*32) + mprintf("%c",((hd.Flags&LHD_WINDOWMASK)>>5)+'a'); + else + mprintf(" "); + mprintf(" %d.%d",hd.UnpVer/10,hd.UnpVer%10); + + static const char *RarOS[]={ + "DOS","OS/2","Windows","Unix","Mac OS","BeOS","WinCE","","","" + }; + + if (Technical) + mprintf("\n%22s %8s %4s", + (hd.HostOS",FileName); + } + else + { + // Link data are encrypted. We would need to ask for password + // and initialize decryption routine to display the link target. + mprintf("\n%22s %s","-->","*<-?->"); + } +} + + +void ListFileAttr(uint A,int HostOS) +{ + switch(HostOS) + { + case HOST_MSDOS: + case HOST_OS2: + case HOST_WIN32: + case HOST_MACOS: + mprintf(" %c%c%c%c%c%c%c ", + (A & 0x08) ? 'V' : '.', + (A & 0x10) ? 'D' : '.', + (A & 0x01) ? 'R' : '.', + (A & 0x02) ? 'H' : '.', + (A & 0x04) ? 'S' : '.', + (A & 0x20) ? 'A' : '.', + (A & 0x800) ? 'C' : '.'); + break; + case HOST_UNIX: + case HOST_BEOS: + switch (A & 0xF000) + { + case 0x4000: + mprintf("d"); + break; + case 0xA000: + mprintf("l"); + break; + default: + mprintf("-"); + break; + } + mprintf("%c%c%c%c%c%c%c%c%c", + (A & 0x0100) ? 'r' : '-', + (A & 0x0080) ? 'w' : '-', + (A & 0x0040) ? ((A & 0x0800) ? 's':'x'):((A & 0x0800) ? 'S':'-'), + (A & 0x0020) ? 'r' : '-', + (A & 0x0010) ? 'w' : '-', + (A & 0x0008) ? ((A & 0x0400) ? 's':'x'):((A & 0x0400) ? 'S':'-'), + (A & 0x0004) ? 'r' : '-', + (A & 0x0002) ? 'w' : '-', + (A & 0x0001) ? 'x' : '-'); + break; + } +} + + +#ifndef SFX_MODULE +void ListOldSubHeader(Archive &Arc) +{ + switch(Arc.SubBlockHead.SubType) + { + case EA_HEAD: + mprintf(St(MListEAHead)); + break; + case UO_HEAD: + mprintf(St(MListUOHead),Arc.UOHead.OwnerName,Arc.UOHead.GroupName); + break; + case MAC_HEAD: + mprintf(St(MListMACHead1),Arc.MACHead.fileType>>24,Arc.MACHead.fileType>>16,Arc.MACHead.fileType>>8,Arc.MACHead.fileType); + mprintf(St(MListMACHead2),Arc.MACHead.fileCreator>>24,Arc.MACHead.fileCreator>>16,Arc.MACHead.fileCreator>>8,Arc.MACHead.fileCreator); + break; + case BEEA_HEAD: + mprintf(St(MListBeEAHead)); + break; + case NTACL_HEAD: + mprintf(St(MListNTACLHead)); + break; + case STREAM_HEAD: + mprintf(St(MListStrmHead),Arc.StreamHead.StreamName); + break; + default: + mprintf(St(MListUnkHead),Arc.SubBlockHead.SubType); + break; + } +} +#endif + + +void ListNewSubHeader(CommandData *Cmd,Archive &Arc,bool Technical) +{ + if (Arc.SubHead.CmpName(SUBHEAD_TYPE_CMT) && + (Arc.SubHead.Flags & LHD_SPLIT_BEFORE)==0 && !Cmd->DisableComment) + { + Array CmtData; + size_t ReadSize=Arc.ReadCommentData(&CmtData,NULL); + if (ReadSize!=0) + { + mprintf(St(MFileComment)); + OutComment((char *)&CmtData[0],ReadSize); + } + } + if (Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM) && + (Arc.SubHead.Flags & LHD_SPLIT_BEFORE)==0) + { + size_t DestSize=Arc.SubHead.SubData.Size()/2; + wchar DestNameW[NM]; + char DestName[NM]; + if (DestSize - - " +#define MUNRARTitle1 "\nUsage: unrar - - " +#define MRARTitle2 "\n <@listfiles...> " +#define MCHelpCmd "\n\n" +#define MCHelpCmdA "\n a Add files to archive" +#define MCHelpCmdC "\n c Add archive comment" +#define MCHelpCmdCF "\n cf Add files comment" +#define MCHelpCmdCH "\n ch Change archive parameters" +#define MCHelpCmdCW "\n cw Write archive comment to file" +#define MCHelpCmdD "\n d Delete files from archive" +#define MCHelpCmdE "\n e Extract files to current directory" +#define MCHelpCmdF "\n f Freshen files in archive" +#define MCHelpCmdI "\n i[par]= Find string in archives" +#define MCHelpCmdK "\n k Lock archive" +#define MCHelpCmdL "\n l[t,b] List archive [technical, bare]" +#define MCHelpCmdM "\n m[f] Move to archive [files only]" +#define MCHelpCmdP "\n p Print file to stdout" +#define MCHelpCmdR "\n r Repair archive" +#define MCHelpCmdRC "\n rc Reconstruct missing volumes" +#define MCHelpCmdRN "\n rn Rename archived files" +#define MCHelpCmdRR "\n rr[N] Add data recovery record" +#define MCHelpCmdRV "\n rv[N] Create recovery volumes" +#define MCHelpCmdS "\n s[name|-] Convert archive to or from SFX" +#define MCHelpCmdT "\n t Test archive files" +#define MCHelpCmdU "\n u Update files in archive" +#define MCHelpCmdV "\n v[t,b] Verbosely list archive [technical,bare]" +#define MCHelpCmdX "\n x Extract files with full path" +#define MCHelpSw "\n\n" +#define MCHelpSwm "\n - Stop switches scanning" +#define MCHelpSwAT "\n @[+] Disable [enable] file lists" +#define MCHelpSwAC "\n ac Clear Archive attribute after compression or extraction" +#define MCHelpSwAD "\n ad Append archive name to destination path" +#define MCHelpSwAG "\n ag[format] Generate archive name using the current date" +#define MCHelpSwAI "\n ai Ignore file attributes" +#define MCHelpSwAO "\n ao Add files with Archive attribute set" +#define MCHelpSwAP "\n ap Set path inside archive" +#define MCHelpSwAS "\n as Synchronize archive contents" +#define MCHelpSwAV "\n av Put authenticity verification (registered versions only)" +#define MCHelpSwAVm "\n av- Disable authenticity verification check" +#define MCHelpSwCm "\n c- Disable comments show" +#define MCHelpSwCFGm "\n cfg- Disable read configuration" +#define MCHelpSwCL "\n cl Convert names to lower case" +#define MCHelpSwCU "\n cu Convert names to upper case" +#define MCHelpSwDF "\n df Delete files after archiving" +#define MCHelpSwDH "\n dh Open shared files" +#define MCHelpSwDR "\n dr Delete files to Recycle Bin" +#define MCHelpSwDS "\n ds Disable name sort for solid archive" +#define MCHelpSwDW "\n dw Wipe files after archiving" +#define MCHelpSwEa "\n e[+] Set file exclude and include attributes" +#define MCHelpSwED "\n ed Do not add empty directories" +#define MCHelpSwEE "\n ee Do not save and extract extended attributes" +#define MCHelpSwEN "\n en Do not put 'end of archive' block" +#define MCHelpSwEP "\n ep Exclude paths from names" +#define MCHelpSwEP1 "\n ep1 Exclude base directory from names" +#define MCHelpSwEP2 "\n ep2 Expand paths to full" +#define MCHelpSwEP3 "\n ep3 Expand paths to full including the drive letter" +#define MCHelpSwF "\n f Freshen files" +#define MCHelpSwHP "\n hp[password] Encrypt both file data and headers" +#define MCHelpSwIDP "\n id[c,d,p,q] Disable messages" +#define MCHelpSwIEML "\n ieml[addr] Send archive by email" +#define MCHelpSwIERR "\n ierr Send all messages to stderr" +#define MCHelpSwILOG "\n ilog[name] Log errors to file (registered versions only)" +#define MCHelpSwINUL "\n inul Disable all messages" +#define MCHelpSwIOFF "\n ioff Turn PC off after completing an operation" +#define MCHelpSwISND "\n isnd Enable sound" +#define MCHelpSwK "\n k Lock archive" +#define MCHelpSwKB "\n kb Keep broken extracted files" +#define MCHelpSwLog "\n log[f][=name] Write names to log file" +#define MCHelpSwMn "\n m<0..5> Set compression level (0-store...3-default...5-maximal)" +#define MCHelpSwMC "\n mc Set advanced compression parameters" +#define MCHelpSwMD "\n md Dictionary size in KB (64,128,256,512,1024,2048,4096 or A-G)" +#define MCHelpSwMS "\n ms[ext;ext] Specify file types to store" +#define MCHelpSwMT "\n mt Set the number of threads" +#define MCHelpSwN "\n n Include only specified file" +#define MCHelpSwNa "\n n@ Read file names to include from stdin" +#define MCHelpSwNal "\n n@ Include files listed in specified list file" +#define MCHelpSwO "\n o[+|-] Set the overwrite mode" +#define MCHelpSwOC "\n oc Set NTFS Compressed attribute" +#define MCHelpSwOL "\n ol Save symbolic links as the link instead of the file" +#define MCHelpSwOR "\n or Rename files automatically" +#define MCHelpSwOS "\n os Save NTFS streams" +#define MCHelpSwOW "\n ow Save or restore file owner and group" +#define MCHelpSwP "\n p[password] Set password" +#define MCHelpSwPm "\n p- Do not query password" +#define MCHelpSwR "\n r Recurse subdirectories" +#define MCHelpSwRm "\n r- Disable recursion" +#define MCHelpSwR0 "\n r0 Recurse subdirectories for wildcard names only" +#define MCHelpSwRI "\n ri

[:] Set priority (0-default,1-min..15-max) and sleep time in ms" +#define MCHelpSwRR "\n rr[N] Add data recovery record" +#define MCHelpSwRV "\n rv[N] Create recovery volumes" +#define MCHelpSwS "\n s[,v[-],e] Create solid archive" +#define MCHelpSwSm "\n s- Disable solid archiving" +#define MCHelpSwSC "\n sc[obj] Specify the character set" +#define MCHelpSwSFX "\n sfx[name] Create SFX archive" +#define MCHelpSwSI "\n si[name] Read data from standard input (stdin)" +#define MCHelpSwSL "\n sl Process files with size less than specified" +#define MCHelpSwSM "\n sm Process files with size more than specified" +#define MCHelpSwT "\n t Test files after archiving" +#define MCHelpSwTK "\n tk Keep original archive time" +#define MCHelpSwTL "\n tl Set archive time to latest file" +#define MCHelpSwTN "\n tn