1
0

archive.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. #include "rar.hpp"
  2. #include "arccmt.cpp"
  3. Archive::Archive(RAROptions *InitCmd)
  4. {
  5. Cmd=NULL; // Just in case we'll have an exception in 'new' below.
  6. DummyCmd=(InitCmd==NULL);
  7. Cmd=DummyCmd ? (new RAROptions):InitCmd;
  8. OpenShared=Cmd->OpenShared;
  9. Format=RARFMT15;
  10. Solid=false;
  11. Volume=false;
  12. MainComment=false;
  13. Locked=false;
  14. Signed=false;
  15. FirstVolume=false;
  16. NewNumbering=false;
  17. SFXSize=0;
  18. LatestTime.Reset();
  19. Protected=false;
  20. Encrypted=false;
  21. FailedHeaderDecryption=false;
  22. BrokenHeader=false;
  23. LastReadBlock=0;
  24. CurBlockPos=0;
  25. NextBlockPos=0;
  26. memset(&MainHead,0,sizeof(MainHead));
  27. memset(&CryptHead,0,sizeof(CryptHead));
  28. memset(&EndArcHead,0,sizeof(EndArcHead));
  29. VolNumber=0;
  30. VolWrite=0;
  31. AddingFilesSize=0;
  32. AddingHeadersSize=0;
  33. *FirstVolumeName=0;
  34. Splitting=false;
  35. NewArchive=false;
  36. SilentOpen=false;
  37. #ifdef USE_QOPEN
  38. ProhibitQOpen=false;
  39. #endif
  40. }
  41. Archive::~Archive()
  42. {
  43. if (DummyCmd)
  44. delete Cmd;
  45. }
  46. void Archive::CheckArc(bool EnableBroken)
  47. {
  48. if (!IsArchive(EnableBroken))
  49. {
  50. // If FailedHeaderDecryption is set, we already reported that archive
  51. // password is incorrect.
  52. if (!FailedHeaderDecryption)
  53. uiMsg(UIERROR_BADARCHIVE,FileName);
  54. ErrHandler.Exit(RARX_FATAL);
  55. }
  56. }
  57. #if !defined(SFX_MODULE)
  58. void Archive::CheckOpen(const wchar *Name)
  59. {
  60. TOpen(Name);
  61. CheckArc(false);
  62. }
  63. #endif
  64. bool Archive::WCheckOpen(const wchar *Name)
  65. {
  66. if (!WOpen(Name))
  67. return false;
  68. if (!IsArchive(false))
  69. {
  70. uiMsg(UIERROR_BADARCHIVE,FileName);
  71. Close();
  72. return false;
  73. }
  74. return true;
  75. }
  76. RARFORMAT Archive::IsSignature(const byte *D,size_t Size)
  77. {
  78. RARFORMAT Type=RARFMT_NONE;
  79. if (Size>=1 && D[0]==0x52)
  80. #ifndef SFX_MODULE
  81. if (Size>=4 && D[1]==0x45 && D[2]==0x7e && D[3]==0x5e)
  82. Type=RARFMT14;
  83. else
  84. #endif
  85. if (Size>=7 && D[1]==0x61 && D[2]==0x72 && D[3]==0x21 && D[4]==0x1a && D[5]==0x07)
  86. {
  87. // We check the last signature byte, so we can return a sensible
  88. // warning in case we'll want to change the archive format
  89. // sometimes in the future.
  90. if (D[6]==0)
  91. Type=RARFMT15;
  92. else
  93. if (D[6]==1)
  94. Type=RARFMT50;
  95. else
  96. if (D[6]>1 && D[6]<5)
  97. Type=RARFMT_FUTURE;
  98. }
  99. return Type;
  100. }
  101. bool Archive::IsArchive(bool EnableBroken)
  102. {
  103. Encrypted=false;
  104. BrokenHeader=false; // Might be left from previous volume.
  105. #ifndef SFX_MODULE
  106. if (IsDevice())
  107. {
  108. uiMsg(UIERROR_INVALIDNAME,FileName,FileName);
  109. return false;
  110. }
  111. #endif
  112. if (Read(MarkHead.Mark,SIZEOF_MARKHEAD3)!=SIZEOF_MARKHEAD3)
  113. return false;
  114. SFXSize=0;
  115. RARFORMAT Type;
  116. if ((Type=IsSignature(MarkHead.Mark,SIZEOF_MARKHEAD3))!=RARFMT_NONE)
  117. {
  118. Format=Type;
  119. if (Format==RARFMT14)
  120. Seek(Tell()-SIZEOF_MARKHEAD3,SEEK_SET);
  121. }
  122. else
  123. {
  124. Array<char> Buffer(MAXSFXSIZE);
  125. long CurPos=(long)Tell();
  126. int ReadSize=Read(&Buffer[0],Buffer.Size()-16);
  127. for (int I=0;I<ReadSize;I++)
  128. if (Buffer[I]==0x52 && (Type=IsSignature((byte *)&Buffer[I],ReadSize-I))!=RARFMT_NONE)
  129. {
  130. Format=Type;
  131. if (Format==RARFMT14 && I>0 && CurPos<28 && ReadSize>31)
  132. {
  133. char *D=&Buffer[28-CurPos];
  134. if (D[0]!=0x52 || D[1]!=0x53 || D[2]!=0x46 || D[3]!=0x58)
  135. continue;
  136. }
  137. SFXSize=CurPos+I;
  138. Seek(SFXSize,SEEK_SET);
  139. if (Format==RARFMT15 || Format==RARFMT50)
  140. Read(MarkHead.Mark,SIZEOF_MARKHEAD3);
  141. break;
  142. }
  143. if (SFXSize==0)
  144. return false;
  145. }
  146. if (Format==RARFMT_FUTURE)
  147. {
  148. uiMsg(UIERROR_NEWRARFORMAT,FileName);
  149. return false;
  150. }
  151. if (Format==RARFMT50) // RAR 5.0 signature is by one byte longer.
  152. {
  153. if (Read(MarkHead.Mark+SIZEOF_MARKHEAD3,1)!=1 || MarkHead.Mark[SIZEOF_MARKHEAD3]!=0)
  154. return false;
  155. MarkHead.HeadSize=SIZEOF_MARKHEAD5;
  156. }
  157. else
  158. MarkHead.HeadSize=SIZEOF_MARKHEAD3;
  159. #ifdef RARDLL
  160. // If callback function is not set, we cannot get the password,
  161. // so we skip the initial header processing for encrypted header archive.
  162. // It leads to skipped archive comment, but the rest of archive data
  163. // is processed correctly.
  164. if (Cmd->Callback==NULL)
  165. SilentOpen=true;
  166. #endif
  167. bool HeadersLeft; // Any headers left to read.
  168. bool StartFound=false; // Main or encryption headers found.
  169. // Skip the archive encryption header if any and read the main header.
  170. while ((HeadersLeft=(ReadHeader()!=0))==true) // Additional parentheses to silence Clang.
  171. {
  172. SeekToNext();
  173. HEADER_TYPE Type=GetHeaderType();
  174. // In RAR 5.0 we need to quit after reading HEAD_CRYPT if we wish to
  175. // avoid the password prompt.
  176. StartFound=Type==HEAD_MAIN || SilentOpen && Type==HEAD_CRYPT;
  177. if (StartFound)
  178. break;
  179. }
  180. // We should not do it for EnableBroken or we'll get 'not RAR archive'
  181. // messages when extracting encrypted archives with wrong password.
  182. if (FailedHeaderDecryption && !EnableBroken)
  183. return false;
  184. if (BrokenHeader || !StartFound) // Main archive header is corrupt or missing.
  185. {
  186. if (!FailedHeaderDecryption) // If not reported a wrong password already.
  187. uiMsg(UIERROR_MHEADERBROKEN,FileName);
  188. if (!EnableBroken)
  189. return false;
  190. }
  191. MainComment=MainHead.CommentInHeader;
  192. // If we process non-encrypted archive or can request a password,
  193. // we set 'first volume' flag based on file attributes below.
  194. // It is necessary for RAR 2.x archives, which did not have 'first volume'
  195. // flag in main header. Also for all RAR formats we need to scan until
  196. // first file header to set "comment" flag when reading service header.
  197. // Unless we are in silent mode, we need to know about presence of comment
  198. // immediately after IsArchive call.
  199. if (HeadersLeft && (!SilentOpen || !Encrypted) && IsSeekable())
  200. {
  201. int64 SavePos=Tell();
  202. int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos;
  203. HEADER_TYPE SaveCurHeaderType=CurHeaderType;
  204. while (ReadHeader()!=0)
  205. {
  206. HEADER_TYPE HeaderType=GetHeaderType();
  207. if (HeaderType==HEAD_SERVICE)
  208. {
  209. // If we have a split service headers, it surely indicates non-first
  210. // volume. But not split service header does not guarantee the first
  211. // volume, because we can have split file after non-split archive
  212. // comment. So we do not quit from loop here.
  213. FirstVolume=Volume && !SubHead.SplitBefore;
  214. }
  215. else
  216. if (HeaderType==HEAD_FILE)
  217. {
  218. FirstVolume=Volume && !FileHead.SplitBefore;
  219. break;
  220. }
  221. else
  222. if (HeaderType==HEAD_ENDARC) // Might happen if archive contains only a split service header.
  223. break;
  224. SeekToNext();
  225. }
  226. CurBlockPos=SaveCurBlockPos;
  227. NextBlockPos=SaveNextBlockPos;
  228. CurHeaderType=SaveCurHeaderType;
  229. Seek(SavePos,SEEK_SET);
  230. }
  231. if (!Volume || FirstVolume)
  232. wcsncpyz(FirstVolumeName,FileName,ASIZE(FirstVolumeName));
  233. return true;
  234. }
  235. void Archive::SeekToNext()
  236. {
  237. Seek(NextBlockPos,SEEK_SET);
  238. }
  239. // Calculate the block size including encryption fields and padding if any.
  240. uint Archive::FullHeaderSize(size_t Size)
  241. {
  242. if (Encrypted)
  243. {
  244. Size = ALIGN_VALUE(Size, CRYPT_BLOCK_SIZE); // Align to encryption block size.
  245. if (Format == RARFMT50)
  246. Size += SIZE_INITV;
  247. else
  248. Size += SIZE_SALT30;
  249. }
  250. return uint(Size);
  251. }
  252. #ifdef USE_QOPEN
  253. bool Archive::Open(const wchar *Name,uint Mode)
  254. {
  255. // Important if we reuse Archive object and it has virtual QOpen
  256. // file position not matching real. For example, for 'l -v volname'.
  257. QOpen.Unload();
  258. return File::Open(Name,Mode);
  259. }
  260. int Archive::Read(void *Data,size_t Size)
  261. {
  262. size_t Result;
  263. if (QOpen.Read(Data,Size,Result))
  264. return (int)Result;
  265. return File::Read(Data,Size);
  266. }
  267. void Archive::Seek(int64 Offset,int Method)
  268. {
  269. if (!QOpen.Seek(Offset,Method))
  270. File::Seek(Offset,Method);
  271. }
  272. int64 Archive::Tell()
  273. {
  274. int64 QPos;
  275. if (QOpen.Tell(&QPos))
  276. return QPos;
  277. return File::Tell();
  278. }
  279. #endif