ZipEntry.Write.cs 112 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601
  1. //#define Trace
  2. // ZipEntry.Write.cs
  3. // ------------------------------------------------------------------
  4. //
  5. // Copyright (c) 2009-2011 Dino Chiesa
  6. // All rights reserved.
  7. //
  8. // This code module is part of DotNetZip, a zipfile class library.
  9. //
  10. // ------------------------------------------------------------------
  11. //
  12. // This code is licensed under the Microsoft Public License.
  13. // See the file License.txt for the license details.
  14. // More info on: http://dotnetzip.codeplex.com
  15. //
  16. // ------------------------------------------------------------------
  17. //
  18. // Last Saved: <2011-July-30 14:55:47>
  19. //
  20. // ------------------------------------------------------------------
  21. //
  22. // This module defines logic for writing (saving) the ZipEntry into a
  23. // zip file.
  24. //
  25. // ------------------------------------------------------------------
  26. using System;
  27. using System.IO;
  28. using RE = System.Text.RegularExpressions;
  29. namespace Ionic.Zip
  30. {
  31. public partial class ZipEntry
  32. {
  33. internal void WriteCentralDirectoryEntry(Stream s)
  34. {
  35. //CDE header size: 46 + extra field length + filename length + comment length
  36. byte[] bytes = new byte[8192];
  37. int i = 0;
  38. // signature
  39. bytes[i++] = (byte)(ZipConstants.ZipDirEntrySignature & 0x000000FF);
  40. bytes[i++] = (byte)((ZipConstants.ZipDirEntrySignature & 0x0000FF00) >> 8);
  41. bytes[i++] = (byte)((ZipConstants.ZipDirEntrySignature & 0x00FF0000) >> 16);
  42. bytes[i++] = (byte)((ZipConstants.ZipDirEntrySignature & 0xFF000000) >> 24);
  43. // Version Made By
  44. // workitem 7071
  45. // We must not overwrite the VersionMadeBy field when writing out a zip
  46. // archive. The VersionMadeBy tells the zip reader the meaning of the
  47. // File attributes. Overwriting the VersionMadeBy will result in
  48. // inconsistent metadata. Consider the scenario where the application
  49. // opens and reads a zip file that had been created on Linux. Then the
  50. // app adds one file to the Zip archive, and saves it. The file
  51. // attributes for all the entries added on Linux will be significant for
  52. // Linux. Therefore the VersionMadeBy for those entries must not be
  53. // changed. Only the entries that are actually created on Windows NTFS
  54. // should get the VersionMadeBy indicating Windows/NTFS.
  55. bytes[i++] = (byte)(_VersionMadeBy & 0x00FF);
  56. bytes[i++] = (byte)((_VersionMadeBy & 0xFF00) >> 8);
  57. // Apparently we want to duplicate the extra field here; we cannot
  58. // simply zero it out and assume tools and apps will use the right one.
  59. ////Int16 extraFieldLengthSave = (short)(_EntryHeader[28] + _EntryHeader[29] * 256);
  60. ////_EntryHeader[28] = 0;
  61. ////_EntryHeader[29] = 0;
  62. // Version Needed, Bitfield, compression method, lastmod,
  63. // crc, compressed and uncompressed sizes, filename length and extra field length.
  64. // These are all present in the local file header, but they may be zero values there.
  65. // So we cannot just copy them.
  66. // workitem 11969: Version Needed To Extract in central directory must be
  67. // the same as the local entry or MS .NET System.IO.Zip fails read.
  68. Int16 vNeeded = (Int16)(VersionNeeded != 0 ? VersionNeeded : 20);
  69. // workitem 12964
  70. if (_OutputUsesZip64==null)
  71. {
  72. // a zipentry in a zipoutputstream, with zero bytes written
  73. _OutputUsesZip64 = new Nullable<bool>(_container.Zip64 == Zip64Option.Always);
  74. }
  75. Int16 versionNeededToExtract = (Int16)(_OutputUsesZip64.Value ? 45 : vNeeded);
  76. #if BZIP
  77. if (this.CompressionMethod == Ionic.Zip.CompressionMethod.BZip2)
  78. versionNeededToExtract = 46;
  79. #endif
  80. bytes[i++] = (byte)(versionNeededToExtract & 0x00FF);
  81. bytes[i++] = (byte)((versionNeededToExtract & 0xFF00) >> 8);
  82. bytes[i++] = (byte)(_BitField & 0x00FF);
  83. bytes[i++] = (byte)((_BitField & 0xFF00) >> 8);
  84. bytes[i++] = (byte)(_CompressionMethod & 0x00FF);
  85. bytes[i++] = (byte)((_CompressionMethod & 0xFF00) >> 8);
  86. #if AESCRYPTO
  87. if (Encryption == EncryptionAlgorithm.WinZipAes128 ||
  88. Encryption == EncryptionAlgorithm.WinZipAes256)
  89. {
  90. i -= 2;
  91. bytes[i++] = 0x63;
  92. bytes[i++] = 0;
  93. }
  94. #endif
  95. bytes[i++] = (byte)(_TimeBlob & 0x000000FF);
  96. bytes[i++] = (byte)((_TimeBlob & 0x0000FF00) >> 8);
  97. bytes[i++] = (byte)((_TimeBlob & 0x00FF0000) >> 16);
  98. bytes[i++] = (byte)((_TimeBlob & 0xFF000000) >> 24);
  99. bytes[i++] = (byte)(_Crc32 & 0x000000FF);
  100. bytes[i++] = (byte)((_Crc32 & 0x0000FF00) >> 8);
  101. bytes[i++] = (byte)((_Crc32 & 0x00FF0000) >> 16);
  102. bytes[i++] = (byte)((_Crc32 & 0xFF000000) >> 24);
  103. int j = 0;
  104. if (_OutputUsesZip64.Value)
  105. {
  106. // CompressedSize (Int32) and UncompressedSize - all 0xFF
  107. for (j = 0; j < 8; j++)
  108. bytes[i++] = 0xFF;
  109. }
  110. else
  111. {
  112. bytes[i++] = (byte)(_CompressedSize & 0x000000FF);
  113. bytes[i++] = (byte)((_CompressedSize & 0x0000FF00) >> 8);
  114. bytes[i++] = (byte)((_CompressedSize & 0x00FF0000) >> 16);
  115. bytes[i++] = (byte)((_CompressedSize & 0xFF000000) >> 24);
  116. bytes[i++] = (byte)(_UncompressedSize & 0x000000FF);
  117. bytes[i++] = (byte)((_UncompressedSize & 0x0000FF00) >> 8);
  118. bytes[i++] = (byte)((_UncompressedSize & 0x00FF0000) >> 16);
  119. bytes[i++] = (byte)((_UncompressedSize & 0xFF000000) >> 24);
  120. }
  121. byte[] fileNameBytes = GetEncodedFileNameBytes();
  122. Int16 filenameLength = (Int16)fileNameBytes.Length;
  123. bytes[i++] = (byte)(filenameLength & 0x00FF);
  124. bytes[i++] = (byte)((filenameLength & 0xFF00) >> 8);
  125. // do this again because now we have real data
  126. _presumeZip64 = _OutputUsesZip64.Value;
  127. // workitem 11131
  128. //
  129. // cannot generate the extra field again, here's why: In the case of a
  130. // zero-byte entry, which uses encryption, DotNetZip will "remove" the
  131. // encryption from the entry. It does this in PostProcessOutput; it
  132. // modifies the entry header, and rewrites it, resetting the Bitfield
  133. // (one bit indicates encryption), and potentially resetting the
  134. // compression method - for AES the Compression method is 0x63, and it
  135. // would get reset to zero (no compression). It then calls SetLength()
  136. // to truncate the stream to remove the encryption header (12 bytes for
  137. // AES256). But, it leaves the previously-generated "Extra Field"
  138. // metadata (11 bytes) for AES in the entry header. This extra field
  139. // data is now "orphaned" - it refers to AES encryption when in fact no
  140. // AES encryption is used. But no problem, the PKWARE spec says that
  141. // unrecognized extra fields can just be ignored. ok. After "removal"
  142. // of AES encryption, the length of the Extra Field can remains the
  143. // same; it's just that there will be 11 bytes in there that previously
  144. // pertained to AES which are now unused. Even the field code is still
  145. // there, but it will be unused by readers, as the encryption bit is not
  146. // set.
  147. //
  148. // Re-calculating the Extra field now would produce a block that is 11
  149. // bytes shorter, and that mismatch - between the extra field in the
  150. // local header and the extra field in the Central Directory - would
  151. // cause problems. (where? why? what problems?) So we can't do
  152. // that. It's all good though, because though the content may have
  153. // changed, the length definitely has not. Also, the _EntryHeader
  154. // contains the "updated" extra field (after PostProcessOutput) at
  155. // offset (30 + filenameLength).
  156. _Extra = ConstructExtraField(true);
  157. Int16 extraFieldLength = (Int16)((_Extra == null) ? 0 : _Extra.Length);
  158. bytes[i++] = (byte)(extraFieldLength & 0x00FF);
  159. bytes[i++] = (byte)((extraFieldLength & 0xFF00) >> 8);
  160. // File (entry) Comment Length
  161. // the _CommentBytes private field was set during WriteHeader()
  162. int commentLength = (_CommentBytes == null) ? 0 : _CommentBytes.Length;
  163. // skip comment length because we set it at the end
  164. i += 2;
  165. // Disk number start
  166. bool segmented = (this._container.ZipFile != null) &&
  167. (this._container.ZipFile.MaxOutputSegmentSize != 0);
  168. if (segmented) // workitem 13915
  169. {
  170. // Emit nonzero disknumber only if saving segmented archive.
  171. bytes[i++] = (byte)(_diskNumber & 0x00FF);
  172. bytes[i++] = (byte)((_diskNumber & 0xFF00) >> 8);
  173. }
  174. else
  175. {
  176. // If reading a segmneted archive and saving to a regular archive,
  177. // ZipEntry._diskNumber will be non-zero but it should be saved as
  178. // zero.
  179. bytes[i++] = 0;
  180. bytes[i++] = 0;
  181. }
  182. // internal file attrs
  183. // workitem 7801
  184. bytes[i++] = (byte)((_IsText) ? 1 : 0); // lo bit: filetype hint. 0=bin, 1=txt.
  185. bytes[i++] = 0;
  186. // external file attrs
  187. // workitem 7071
  188. bytes[i++] = (byte)(_ExternalFileAttrs & 0x000000FF);
  189. bytes[i++] = (byte)((_ExternalFileAttrs & 0x0000FF00) >> 8);
  190. bytes[i++] = (byte)((_ExternalFileAttrs & 0x00FF0000) >> 16);
  191. bytes[i++] = (byte)((_ExternalFileAttrs & 0xFF000000) >> 24);
  192. // workitem 11131
  193. // relative offset of local header.
  194. //
  195. // If necessary to go to 64-bit value, then emit 0xFFFFFFFF,
  196. // else write out the value.
  197. //
  198. // Even if zip64 is required for other reasons - number of the entry
  199. // > 65534, or uncompressed size of the entry > MAX_INT32, the ROLH
  200. // need not be stored in a 64-bit field .
  201. if (_RelativeOffsetOfLocalHeader > 0xFFFFFFFFL) // _OutputUsesZip64.Value
  202. {
  203. bytes[i++] = 0xFF;
  204. bytes[i++] = 0xFF;
  205. bytes[i++] = 0xFF;
  206. bytes[i++] = 0xFF;
  207. }
  208. else
  209. {
  210. bytes[i++] = (byte)(_RelativeOffsetOfLocalHeader & 0x000000FF);
  211. bytes[i++] = (byte)((_RelativeOffsetOfLocalHeader & 0x0000FF00) >> 8);
  212. bytes[i++] = (byte)((_RelativeOffsetOfLocalHeader & 0x00FF0000) >> 16);
  213. bytes[i++] = (byte)((_RelativeOffsetOfLocalHeader & 0xFF000000) >> 24);
  214. }
  215. // actual filename
  216. Buffer.BlockCopy(fileNameBytes, 0, bytes, i, filenameLength);
  217. i += filenameLength;
  218. // "Extra field"
  219. if (_Extra != null)
  220. {
  221. // workitem 11131
  222. //
  223. // copy from EntryHeader if available - it may have been updated.
  224. // if not, copy from Extra. This would be unnecessary if I just
  225. // updated the Extra field when updating EntryHeader, in
  226. // PostProcessOutput.
  227. //?? I don't understand why I wouldn't want to just use
  228. // the recalculated Extra field. ??
  229. // byte[] h = _EntryHeader ?? _Extra;
  230. // int offx = (h == _EntryHeader) ? 30 + filenameLength : 0;
  231. // Buffer.BlockCopy(h, offx, bytes, i, extraFieldLength);
  232. // i += extraFieldLength;
  233. byte[] h = _Extra;
  234. int offx = 0;
  235. Buffer.BlockCopy(h, offx, bytes, i, extraFieldLength);
  236. i += extraFieldLength;
  237. }
  238. // file (entry) comment
  239. if (commentLength != 0)
  240. {
  241. // the size of our buffer defines the max length of the comment we can write
  242. if (commentLength + i > bytes.Length)
  243. commentLength = bytes.Length - i;
  244. // now actually write the comment itself into the byte buffer
  245. Buffer.BlockCopy(_CommentBytes, 0, bytes, i, commentLength);
  246. // for (j = 0; (j < commentLength) && (i + j < bytes.Length); j++)
  247. // bytes[i + j] = _CommentBytes[j];
  248. i += commentLength;
  249. }
  250. bytes[32] = (byte)(commentLength & 0x00FF);
  251. bytes[33] = (byte)((commentLength & 0xFF00) >> 8);
  252. s.Write(bytes, 0, i);
  253. }
  254. #if INFOZIP_UTF8
  255. static private bool FileNameIsUtf8(char[] FileNameChars)
  256. {
  257. bool isUTF8 = false;
  258. bool isUnicode = false;
  259. for (int j = 0; j < FileNameChars.Length; j++)
  260. {
  261. byte[] b = System.BitConverter.GetBytes(FileNameChars[j]);
  262. isUnicode |= (b.Length != 2);
  263. isUnicode |= (b[1] != 0);
  264. isUTF8 |= ((b[0] & 0x80) != 0);
  265. }
  266. return isUTF8;
  267. }
  268. #endif
  269. private byte[] ConstructExtraField(bool forCentralDirectory)
  270. {
  271. var listOfBlocks = new System.Collections.Generic.List<byte[]>();
  272. byte[] block;
  273. // Conditionally emit an extra field with Zip64 information. If the
  274. // Zip64 option is Always, we emit the field, before knowing that it's
  275. // necessary. Later, if it turns out this entry does not need zip64,
  276. // we'll set the header ID to rubbish and the data will be ignored.
  277. // This results in additional overhead metadata in the zip file, but
  278. // it will be small in comparison to the entry data.
  279. //
  280. // On the other hand if the Zip64 option is AsNecessary and it's NOT
  281. // for the central directory, then we do the same thing. Or, if the
  282. // Zip64 option is AsNecessary and it IS for the central directory,
  283. // and the entry requires zip64, then emit the header.
  284. if (_container.Zip64 == Zip64Option.Always ||
  285. (_container.Zip64 == Zip64Option.AsNecessary &&
  286. (!forCentralDirectory || _entryRequiresZip64.Value)))
  287. {
  288. // add extra field for zip64 here
  289. // workitem 7924
  290. int sz = 4 + (forCentralDirectory ? 28 : 16);
  291. block = new byte[sz];
  292. int i = 0;
  293. if (_presumeZip64 || forCentralDirectory)
  294. {
  295. // HeaderId = always use zip64 extensions.
  296. block[i++] = 0x01;
  297. block[i++] = 0x00;
  298. }
  299. else
  300. {
  301. // HeaderId = dummy data now, maybe set to 0x0001 (ZIP64) later.
  302. block[i++] = 0x99;
  303. block[i++] = 0x99;
  304. }
  305. // DataSize
  306. block[i++] = (byte)(sz - 4); // decimal 28 or 16 (workitem 7924)
  307. block[i++] = 0x00;
  308. // The actual metadata - we may or may not have real values yet...
  309. // uncompressed size
  310. Array.Copy(BitConverter.GetBytes(_UncompressedSize), 0, block, i, 8);
  311. i += 8;
  312. // compressed size
  313. Array.Copy(BitConverter.GetBytes(_CompressedSize), 0, block, i, 8);
  314. i += 8;
  315. // workitem 7924 - only include this if the "extra" field is for
  316. // use in the central directory. It is unnecessary and not useful
  317. // for local header; makes WinZip choke.
  318. if (forCentralDirectory)
  319. {
  320. // relative offset
  321. Array.Copy(BitConverter.GetBytes(_RelativeOffsetOfLocalHeader), 0, block, i, 8);
  322. i += 8;
  323. // starting disk number
  324. Array.Copy(BitConverter.GetBytes(0), 0, block, i, 4);
  325. }
  326. listOfBlocks.Add(block);
  327. }
  328. #if AESCRYPTO
  329. if (Encryption == EncryptionAlgorithm.WinZipAes128 ||
  330. Encryption == EncryptionAlgorithm.WinZipAes256)
  331. {
  332. block = new byte[4 + 7];
  333. int i = 0;
  334. // extra field for WinZip AES
  335. // header id
  336. block[i++] = 0x01;
  337. block[i++] = 0x99;
  338. // data size
  339. block[i++] = 0x07;
  340. block[i++] = 0x00;
  341. // vendor number
  342. block[i++] = 0x01; // AE-1 - means "Verify CRC"
  343. block[i++] = 0x00;
  344. // vendor id "AE"
  345. block[i++] = 0x41;
  346. block[i++] = 0x45;
  347. // key strength
  348. int keystrength = GetKeyStrengthInBits(Encryption);
  349. if (keystrength == 128)
  350. block[i] = 1;
  351. else if (keystrength == 256)
  352. block[i] = 3;
  353. else
  354. block[i] = 0xFF;
  355. i++;
  356. // actual compression method
  357. block[i++] = (byte)(_CompressionMethod & 0x00FF);
  358. block[i++] = (byte)(_CompressionMethod & 0xFF00);
  359. listOfBlocks.Add(block);
  360. }
  361. #endif
  362. if (_ntfsTimesAreSet && _emitNtfsTimes)
  363. {
  364. block = new byte[32 + 4];
  365. // HeaderId 2 bytes 0x000a == NTFS times
  366. // Datasize 2 bytes 32
  367. // reserved 4 bytes ?? don't care
  368. // timetag 2 bytes 0x0001 == NTFS time
  369. // size 2 bytes 24 == 8 bytes each for ctime, mtime, atime
  370. // mtime 8 bytes win32 ticks since win32epoch
  371. // atime 8 bytes win32 ticks since win32epoch
  372. // ctime 8 bytes win32 ticks since win32epoch
  373. int i = 0;
  374. // extra field for NTFS times
  375. // header id
  376. block[i++] = 0x0a;
  377. block[i++] = 0x00;
  378. // data size
  379. block[i++] = 32;
  380. block[i++] = 0;
  381. i += 4; // reserved
  382. // time tag
  383. block[i++] = 0x01;
  384. block[i++] = 0x00;
  385. // data size (again)
  386. block[i++] = 24;
  387. block[i++] = 0;
  388. Int64 z = _Mtime.ToFileTime();
  389. Array.Copy(BitConverter.GetBytes(z), 0, block, i, 8);
  390. i += 8;
  391. z = _Atime.ToFileTime();
  392. Array.Copy(BitConverter.GetBytes(z), 0, block, i, 8);
  393. i += 8;
  394. z = _Ctime.ToFileTime();
  395. Array.Copy(BitConverter.GetBytes(z), 0, block, i, 8);
  396. i += 8;
  397. listOfBlocks.Add(block);
  398. }
  399. if (_ntfsTimesAreSet && _emitUnixTimes)
  400. {
  401. int len = 5 + 4;
  402. if (!forCentralDirectory) len += 8;
  403. block = new byte[len];
  404. // local form:
  405. // --------------
  406. // HeaderId 2 bytes 0x5455 == unix timestamp
  407. // Datasize 2 bytes 13
  408. // flags 1 byte 7 (low three bits all set)
  409. // mtime 4 bytes seconds since unix epoch
  410. // atime 4 bytes seconds since unix epoch
  411. // ctime 4 bytes seconds since unix epoch
  412. //
  413. // central directory form:
  414. //---------------------------------
  415. // HeaderId 2 bytes 0x5455 == unix timestamp
  416. // Datasize 2 bytes 5
  417. // flags 1 byte 7 (low three bits all set)
  418. // mtime 4 bytes seconds since unix epoch
  419. //
  420. int i = 0;
  421. // extra field for "unix" times
  422. // header id
  423. block[i++] = 0x55;
  424. block[i++] = 0x54;
  425. // data size
  426. block[i++] = unchecked((byte)(len - 4));
  427. block[i++] = 0;
  428. // flags
  429. block[i++] = 0x07;
  430. Int32 z = unchecked((int)((_Mtime - _unixEpoch).TotalSeconds));
  431. Array.Copy(BitConverter.GetBytes(z), 0, block, i, 4);
  432. i += 4;
  433. if (!forCentralDirectory)
  434. {
  435. z = unchecked((int)((_Atime - _unixEpoch).TotalSeconds));
  436. Array.Copy(BitConverter.GetBytes(z), 0, block, i, 4);
  437. i += 4;
  438. z = unchecked((int)((_Ctime - _unixEpoch).TotalSeconds));
  439. Array.Copy(BitConverter.GetBytes(z), 0, block, i, 4);
  440. i += 4;
  441. }
  442. listOfBlocks.Add(block);
  443. }
  444. // inject other blocks here...
  445. // concatenate any blocks we've got:
  446. byte[] aggregateBlock = null;
  447. if (listOfBlocks.Count > 0)
  448. {
  449. int totalLength = 0;
  450. int i, current = 0;
  451. for (i = 0; i < listOfBlocks.Count; i++)
  452. totalLength += listOfBlocks[i].Length;
  453. aggregateBlock = new byte[totalLength];
  454. for (i = 0; i < listOfBlocks.Count; i++)
  455. {
  456. System.Array.Copy(listOfBlocks[i], 0, aggregateBlock, current, listOfBlocks[i].Length);
  457. current += listOfBlocks[i].Length;
  458. }
  459. }
  460. return aggregateBlock;
  461. }
  462. // private System.Text.Encoding GenerateCommentBytes()
  463. // {
  464. // var getEncoding = new Func<System.Text.Encoding>({
  465. // switch (AlternateEncodingUsage)
  466. // {
  467. // case ZipOption.Always:
  468. // return AlternateEncoding;
  469. // case ZipOption.Never:
  470. // return ibm437;
  471. // }
  472. // var cb = ibm437.GetBytes(_Comment);
  473. // // need to use this form of GetString() for .NET CF
  474. // string s1 = ibm437.GetString(cb, 0, cb.Length);
  475. // if (s1 == _Comment)
  476. // return ibm437;
  477. // return AlternateEncoding;
  478. // });
  479. //
  480. // var encoding = getEncoding();
  481. // _CommentBytes = encoding.GetBytes(_Comment);
  482. // return encoding;
  483. // }
  484. private string NormalizeFileName()
  485. {
  486. // here, we need to flip the backslashes to forward-slashes,
  487. // also, we need to trim the \\server\share syntax from any UNC path.
  488. // and finally, we need to remove any leading .\
  489. string SlashFixed = FileName.Replace("\\", "/");
  490. string s1 = null;
  491. if ((_TrimVolumeFromFullyQualifiedPaths) && (FileName.Length >= 3)
  492. && (FileName[1] == ':') && (SlashFixed[2] == '/'))
  493. {
  494. // trim off volume letter, colon, and slash
  495. s1 = SlashFixed.Substring(3);
  496. }
  497. else if ((FileName.Length >= 4)
  498. && ((SlashFixed[0] == '/') && (SlashFixed[1] == '/')))
  499. {
  500. int n = SlashFixed.IndexOf('/', 2);
  501. if (n == -1)
  502. throw new ArgumentException("The path for that entry appears to be badly formatted");
  503. s1 = SlashFixed.Substring(n + 1);
  504. }
  505. else if ((FileName.Length >= 3)
  506. && ((SlashFixed[0] == '.') && (SlashFixed[1] == '/')))
  507. {
  508. // trim off dot and slash
  509. s1 = SlashFixed.Substring(2);
  510. }
  511. else
  512. {
  513. s1 = SlashFixed;
  514. }
  515. return s1;
  516. }
  517. /// <summary>
  518. /// generate and return a byte array that encodes the filename
  519. /// for the entry.
  520. /// </summary>
  521. /// <remarks>
  522. /// <para>
  523. /// side effects: generate and store into _CommentBytes the
  524. /// byte array for any comment attached to the entry. Also
  525. /// sets _actualEncoding to indicate the actual encoding
  526. /// used. The same encoding is used for both filename and
  527. /// comment.
  528. /// </para>
  529. /// </remarks>
  530. private byte[] GetEncodedFileNameBytes()
  531. {
  532. // workitem 6513
  533. var s1 = NormalizeFileName();
  534. switch(AlternateEncodingUsage)
  535. {
  536. case ZipOption.Always:
  537. if (!(_Comment == null || _Comment.Length == 0))
  538. _CommentBytes = AlternateEncoding.GetBytes(_Comment);
  539. _actualEncoding = AlternateEncoding;
  540. return AlternateEncoding.GetBytes(s1);
  541. case ZipOption.Never:
  542. if (!(_Comment == null || _Comment.Length == 0))
  543. _CommentBytes = ibm437.GetBytes(_Comment);
  544. _actualEncoding = ibm437;
  545. return ibm437.GetBytes(s1);
  546. }
  547. // arriving here means AlternateEncodingUsage is "AsNecessary"
  548. // case ZipOption.AsNecessary:
  549. // workitem 6513: when writing, use the alternative encoding
  550. // only when _actualEncoding is not yet set (it can be set
  551. // during Read), and when ibm437 will not do.
  552. byte[] result = ibm437.GetBytes(s1);
  553. // need to use this form of GetString() for .NET CF
  554. string s2 = ibm437.GetString(result, 0, result.Length);
  555. _CommentBytes = null;
  556. if (s2 != s1)
  557. {
  558. // Encoding the filename with ibm437 does not allow round-trips.
  559. // Therefore, use the alternate encoding. Assume it will work,
  560. // no checking of round trips here.
  561. result = AlternateEncoding.GetBytes(s1);
  562. if (_Comment != null && _Comment.Length != 0)
  563. _CommentBytes = AlternateEncoding.GetBytes(_Comment);
  564. _actualEncoding = AlternateEncoding;
  565. return result;
  566. }
  567. _actualEncoding = ibm437;
  568. // Using ibm437, FileName can be encoded without information
  569. // loss; now try the Comment.
  570. // if there is no comment, use ibm437.
  571. if (_Comment == null || _Comment.Length == 0)
  572. return result;
  573. // there is a comment. Get the encoded form.
  574. byte[] cbytes = ibm437.GetBytes(_Comment);
  575. string c2 = ibm437.GetString(cbytes,0,cbytes.Length);
  576. // Check for round-trip.
  577. if (c2 != Comment)
  578. {
  579. // Comment cannot correctly be encoded with ibm437. Use
  580. // the alternate encoding.
  581. result = AlternateEncoding.GetBytes(s1);
  582. _CommentBytes = AlternateEncoding.GetBytes(_Comment);
  583. _actualEncoding = AlternateEncoding;
  584. return result;
  585. }
  586. // use IBM437
  587. _CommentBytes = cbytes;
  588. return result;
  589. }
  590. private bool WantReadAgain()
  591. {
  592. if (_UncompressedSize < 0x10) return false;
  593. if (_CompressionMethod == 0x00) return false;
  594. if (CompressionLevel == Ionic.Zlib.CompressionLevel.None) return false;
  595. if (_CompressedSize < _UncompressedSize) return false;
  596. if (this._Source == ZipEntrySource.Stream && !this._sourceStream.CanSeek) return false;
  597. #if AESCRYPTO
  598. if (_aesCrypto_forWrite != null && (CompressedSize - _aesCrypto_forWrite.SizeOfEncryptionMetadata) <= UncompressedSize + 0x10) return false;
  599. #endif
  600. if (_zipCrypto_forWrite != null && (CompressedSize - 12) <= UncompressedSize) return false;
  601. return true;
  602. }
  603. private void MaybeUnsetCompressionMethodForWriting(int cycle)
  604. {
  605. // if we've already tried with compression... turn it off this time
  606. if (cycle > 1)
  607. {
  608. _CompressionMethod = 0x0;
  609. return;
  610. }
  611. // compression for directories = 0x00 (No Compression)
  612. if (IsDirectory)
  613. {
  614. _CompressionMethod = 0x0;
  615. return;
  616. }
  617. if (this._Source == ZipEntrySource.ZipFile)
  618. {
  619. return; // do nothing
  620. }
  621. // If __FileDataPosition is zero, then that means we will get the data
  622. // from a file or stream.
  623. // It is never possible to compress a zero-length file, so we check for
  624. // this condition.
  625. if (this._Source == ZipEntrySource.Stream)
  626. {
  627. // workitem 7742
  628. if (_sourceStream != null && _sourceStream.CanSeek)
  629. {
  630. // Length prop will throw if CanSeek is false
  631. long fileLength = _sourceStream.Length;
  632. if (fileLength == 0)
  633. {
  634. _CompressionMethod = 0x00;
  635. return;
  636. }
  637. }
  638. }
  639. else if ((this._Source == ZipEntrySource.FileSystem) && (SharedUtilities.GetFileLength(LocalFileName) == 0L))
  640. {
  641. _CompressionMethod = 0x00;
  642. return;
  643. }
  644. // Ok, we're getting the data to be compressed from a
  645. // non-zero-length file or stream, or a file or stream of
  646. // unknown length, and we presume that it is non-zero. In
  647. // that case we check the callback to see if the app wants
  648. // to tell us whether to compress or not.
  649. if (SetCompression != null)
  650. CompressionLevel = SetCompression(LocalFileName, _FileNameInArchive);
  651. // finally, set CompressionMethod to None if CompressionLevel is None
  652. if (CompressionLevel == (short)Ionic.Zlib.CompressionLevel.None &&
  653. CompressionMethod == Ionic.Zip.CompressionMethod.Deflate)
  654. _CompressionMethod = 0x00;
  655. return;
  656. }
  657. // write the header info for an entry
  658. internal void WriteHeader(Stream s, int cycle)
  659. {
  660. // Must remember the offset, within the output stream, of this particular
  661. // entry header.
  662. //
  663. // This is for 2 reasons:
  664. //
  665. // 1. so we can determine the RelativeOffsetOfLocalHeader (ROLH) for
  666. // use in the central directory.
  667. // 2. so we can seek backward in case there is an error opening or reading
  668. // the file, and the application decides to skip the file. In this case,
  669. // we need to seek backward in the output stream to allow the next entry
  670. // to be added to the zipfile output stream.
  671. //
  672. // Normally you would just store the offset before writing to the output
  673. // stream and be done with it. But the possibility to use split archives
  674. // makes this approach ineffective. In split archives, each file or segment
  675. // is bound to a max size limit, and each local file header must not span a
  676. // segment boundary; it must be written contiguously. If it will fit in the
  677. // current segment, then the ROLH is just the current Position in the output
  678. // stream. If it won't fit, then we need a new file (segment) and the ROLH
  679. // is zero.
  680. //
  681. // But we only can know if it is possible to write a header contiguously
  682. // after we know the size of the local header, a size that varies with
  683. // things like filename length, comments, and extra fields. We have to
  684. // compute the header fully before knowing whether it will fit.
  685. //
  686. // That takes care of item #1 above. Now, regarding #2. If an error occurs
  687. // while computing the local header, we want to just seek backward. The
  688. // exception handling logic (in the caller of WriteHeader) uses ROLH to
  689. // scroll back.
  690. //
  691. // All this means we have to preserve the starting offset before computing
  692. // the header, and also we have to compute the offset later, to handle the
  693. // case of split archives.
  694. var counter = s as CountingStream;
  695. // workitem 8098: ok (output)
  696. // This may change later, for split archives
  697. // Don't set _RelativeOffsetOfLocalHeader. Instead, set a temp variable.
  698. // This allows for re-streaming, where a zip entry might be read from a
  699. // zip archive (and maybe decrypted, and maybe decompressed) and then
  700. // written to another zip archive, with different settings for
  701. // compression method, compression level, or encryption algorithm.
  702. _future_ROLH = (counter != null)
  703. ? counter.ComputedPosition
  704. : s.Position;
  705. int j = 0, i = 0;
  706. byte[] block = new byte[30];
  707. // signature
  708. block[i++] = (byte)(ZipConstants.ZipEntrySignature & 0x000000FF);
  709. block[i++] = (byte)((ZipConstants.ZipEntrySignature & 0x0000FF00) >> 8);
  710. block[i++] = (byte)((ZipConstants.ZipEntrySignature & 0x00FF0000) >> 16);
  711. block[i++] = (byte)((ZipConstants.ZipEntrySignature & 0xFF000000) >> 24);
  712. // Design notes for ZIP64:
  713. //
  714. // The specification says that the header must include the Compressed
  715. // and Uncompressed sizes, as well as the CRC32 value. When creating
  716. // a zip via streamed processing, these quantities are not known until
  717. // after the compression is done. Thus, a typical way to do it is to
  718. // insert zeroes for these quantities, then do the compression, then
  719. // seek back to insert the appropriate values, then seek forward to
  720. // the end of the file data.
  721. //
  722. // There is also the option of using bit 3 in the GP bitfield - to
  723. // specify that there is a data descriptor after the file data
  724. // containing these three quantities.
  725. //
  726. // This works when the size of the quantities is known, either 32-bits
  727. // or 64 bits as with the ZIP64 extensions.
  728. //
  729. // With Zip64, the 4-byte fields are set to 0xffffffff, and there is a
  730. // corresponding data block in the "extra field" that contains the
  731. // actual Compressed, uncompressed sizes. (As well as an additional
  732. // field, the "Relative Offset of Local Header")
  733. //
  734. // The problem is when the app desires to use ZIP64 extensions
  735. // optionally, only when necessary. Suppose the library assumes no
  736. // zip64 extensions when writing the header, then after compression
  737. // finds that the size of the data requires zip64. At this point, the
  738. // header, already written to the file, won't have the necessary data
  739. // block in the "extra field". The size of the entry header is fixed,
  740. // so it is not possible to just "add on" the zip64 data block after
  741. // compressing the file. On the other hand, always using zip64 will
  742. // break interoperability with many other systems and apps.
  743. //
  744. // The approach we take is to insert a 32-byte dummy data block in the
  745. // extra field, whenever zip64 is to be used "as necessary". This data
  746. // block will get the actual zip64 HeaderId and zip64 metadata if
  747. // necessary. If not necessary, the data block will get a meaningless
  748. // HeaderId (0x1111), and will be filled with zeroes.
  749. //
  750. // When zip64 is actually in use, we also need to set the
  751. // VersionNeededToExtract field to 45.
  752. //
  753. // There is one additional wrinkle: using zip64 as necessary conflicts
  754. // with output to non-seekable devices. The header is emitted and
  755. // must indicate whether zip64 is in use, before we know if zip64 is
  756. // necessary. Because there is no seeking, the header can never be
  757. // changed. Therefore, on non-seekable devices,
  758. // Zip64Option.AsNecessary is the same as Zip64Option.Always.
  759. //
  760. // version needed- see AppNote.txt.
  761. //
  762. // need v5.1 for PKZIP strong encryption, or v2.0 for no encryption or
  763. // for PK encryption, 4.5 for zip64. We may reset this later, as
  764. // necessary or zip64.
  765. _presumeZip64 = (_container.Zip64 == Zip64Option.Always ||
  766. (_container.Zip64 == Zip64Option.AsNecessary && !s.CanSeek));
  767. Int16 VersionNeededToExtract = (Int16)(_presumeZip64 ? 45 : 20);
  768. #if BZIP
  769. if (this.CompressionMethod == Ionic.Zip.CompressionMethod.BZip2)
  770. VersionNeededToExtract = 46;
  771. #endif
  772. // (i==4)
  773. block[i++] = (byte)(VersionNeededToExtract & 0x00FF);
  774. block[i++] = (byte)((VersionNeededToExtract & 0xFF00) >> 8);
  775. // Get byte array. Side effect: sets ActualEncoding.
  776. // Must determine encoding before setting the bitfield.
  777. // workitem 6513
  778. byte[] fileNameBytes = GetEncodedFileNameBytes();
  779. Int16 filenameLength = (Int16)fileNameBytes.Length;
  780. // general purpose bitfield
  781. // In the current implementation, this library uses only these bits
  782. // in the GP bitfield:
  783. // bit 0 = if set, indicates the entry is encrypted
  784. // bit 3 = if set, indicates the CRC, C and UC sizes follow the file data.
  785. // bit 6 = strong encryption - for pkware's meaning of strong encryption
  786. // bit 11 = UTF-8 encoding is used in the comment and filename
  787. // Here we set or unset the encryption bit.
  788. // _BitField may already be set, as with a ZipEntry added into ZipOutputStream, which
  789. // has bit 3 always set. We only want to set one bit
  790. if (_Encryption == EncryptionAlgorithm.None)
  791. _BitField &= ~1; // encryption bit OFF
  792. else
  793. _BitField |= 1; // encryption bit ON
  794. // workitem 7941: WinZip does not the "strong encryption" bit when using AES.
  795. // This "Strong Encryption" is a PKWare Strong encryption thing.
  796. // _BitField |= 0x0020;
  797. // set the UTF8 bit if necessary
  798. #if SILVERLIGHT
  799. if (_actualEncoding.WebName == "utf-8")
  800. #else
  801. if (_actualEncoding.CodePage == System.Text.Encoding.UTF8.CodePage)
  802. #endif
  803. _BitField |= 0x0800;
  804. // The PKZIP spec says that if bit 3 is set (0x0008) in the General
  805. // Purpose BitField, then the CRC, Compressed size, and uncompressed
  806. // size are written directly after the file data.
  807. //
  808. // These 3 quantities are normally present in the regular zip entry
  809. // header. But, they are not knowable until after the compression is
  810. // done. So, in the normal case, we
  811. //
  812. // - write the header, using zeros for these quantities
  813. // - compress the data, and incidentally compute these quantities.
  814. // - seek back and write the correct values them into the header.
  815. //
  816. // This is nice because, while it is more complicated to write the zip
  817. // file, it is simpler and less error prone to read the zip file, and
  818. // as a result more applications can read zip files produced this way,
  819. // with those 3 quantities in the header.
  820. //
  821. // But if seeking in the output stream is not possible, then we need
  822. // to set the appropriate bitfield and emit these quantities after the
  823. // compressed file data in the output.
  824. //
  825. // workitem 7216 - having trouble formatting a zip64 file that is
  826. // readable by WinZip. not sure why! What I found is that setting
  827. // bit 3 and following all the implications, the zip64 file is
  828. // readable by WinZip 12. and Perl's IO::Compress::Zip . Perl takes
  829. // an interesting approach - it always sets bit 3 if ZIP64 in use.
  830. // DotNetZip now does the same; this gives better compatibility with
  831. // WinZip 12.
  832. if (IsDirectory || cycle == 99)
  833. {
  834. // (cycle == 99) indicates a zero-length entry written by ZipOutputStream
  835. _BitField &= ~0x0008; // unset bit 3 - no "data descriptor" - ever
  836. _BitField &= ~0x0001; // unset bit 1 - no encryption - ever
  837. Encryption = EncryptionAlgorithm.None;
  838. Password = null;
  839. }
  840. else if (!s.CanSeek)
  841. _BitField |= 0x0008;
  842. #if DONT_GO_THERE
  843. else if (this.Encryption == EncryptionAlgorithm.PkzipWeak &&
  844. this._Source != ZipEntrySource.ZipFile)
  845. {
  846. // Set bit 3 to avoid the double-read perf issue.
  847. //
  848. // When PKZIP encryption is used, byte 11 of the encryption header is
  849. // used as a consistency check. It is normally set to the MSByte of the
  850. // CRC. But this means the cRC must be known ebfore compression and
  851. // encryption, which means the entire stream has to be read twice. To
  852. // avoid that, the high-byte of the time blob (when in DOS format) can
  853. // be used for the consistency check (byte 11 in the encryption header).
  854. // But this means the entry must have bit 3 set.
  855. //
  856. // Previously I used a more complex arrangement - using the methods like
  857. // FigureCrc32(), PrepOutputStream() and others, in order to manage the
  858. // seek-back in the source stream. Why? Because bit 3 is not always
  859. // friendly with third-party zip tools, like those on the Mac.
  860. //
  861. // This is why this code is still ifdef'd out.
  862. //
  863. // Might consider making this yet another programmable option -
  864. // AlwaysUseBit3ForPkzip. But that's for another day.
  865. //
  866. _BitField |= 0x0008;
  867. }
  868. #endif
  869. // (i==6)
  870. block[i++] = (byte)(_BitField & 0x00FF);
  871. block[i++] = (byte)((_BitField & 0xFF00) >> 8);
  872. // Here, we want to set values for Compressed Size, Uncompressed Size,
  873. // and CRC. If we have __FileDataPosition as not -1 (zero is a valid
  874. // FDP), then that means we are reading this zip entry from a zip
  875. // file, and we have good values for those quantities.
  876. //
  877. // If _FileDataPosition is -1, then we are constructing this Entry
  878. // from nothing. We zero those quantities now, and we will compute
  879. // actual values for the three quantities later, when we do the
  880. // compression, and then seek back to write them into the appropriate
  881. // place in the header.
  882. if (this.__FileDataPosition == -1)
  883. {
  884. //_UncompressedSize = 0; // do not unset - may need this value for restream
  885. // _Crc32 = 0; // ditto
  886. _CompressedSize = 0;
  887. _crcCalculated = false;
  888. }
  889. // set compression method here
  890. MaybeUnsetCompressionMethodForWriting(cycle);
  891. // (i==8) compression method
  892. block[i++] = (byte)(_CompressionMethod & 0x00FF);
  893. block[i++] = (byte)((_CompressionMethod & 0xFF00) >> 8);
  894. if (cycle == 99)
  895. {
  896. // (cycle == 99) indicates a zero-length entry written by ZipOutputStream
  897. SetZip64Flags();
  898. }
  899. #if AESCRYPTO
  900. else if (Encryption == EncryptionAlgorithm.WinZipAes128 || Encryption == EncryptionAlgorithm.WinZipAes256)
  901. {
  902. i -= 2;
  903. block[i++] = 0x63;
  904. block[i++] = 0;
  905. }
  906. #endif
  907. // LastMod
  908. if (_dontEmitLastModified)
  909. {
  910. _TimeBlob = 0;
  911. }
  912. else
  913. {
  914. _TimeBlob = Ionic.Zip.SharedUtilities.DateTimeToPacked(LastModified);
  915. }
  916. // (i==10) time blob
  917. block[i++] = (byte)(_TimeBlob & 0x000000FF);
  918. block[i++] = (byte)((_TimeBlob & 0x0000FF00) >> 8);
  919. block[i++] = (byte)((_TimeBlob & 0x00FF0000) >> 16);
  920. block[i++] = (byte)((_TimeBlob & 0xFF000000) >> 24);
  921. // (i==14) CRC - if source==filesystem, this is zero now, actual value
  922. // will be calculated later. if source==archive, this is a bonafide
  923. // value.
  924. block[i++] = (byte)(_Crc32 & 0x000000FF);
  925. block[i++] = (byte)((_Crc32 & 0x0000FF00) >> 8);
  926. block[i++] = (byte)((_Crc32 & 0x00FF0000) >> 16);
  927. block[i++] = (byte)((_Crc32 & 0xFF000000) >> 24);
  928. if (_presumeZip64)
  929. {
  930. // (i==18) CompressedSize (Int32) and UncompressedSize - all 0xFF for now
  931. for (j = 0; j < 8; j++)
  932. block[i++] = 0xFF;
  933. }
  934. else
  935. {
  936. // (i==18) CompressedSize (Int32) - this value may or may not be
  937. // bonafide. if source == filesystem, then it is zero, and we'll
  938. // learn it after we compress. if source == archive, then it is
  939. // bonafide data.
  940. block[i++] = (byte)(_CompressedSize & 0x000000FF);
  941. block[i++] = (byte)((_CompressedSize & 0x0000FF00) >> 8);
  942. block[i++] = (byte)((_CompressedSize & 0x00FF0000) >> 16);
  943. block[i++] = (byte)((_CompressedSize & 0xFF000000) >> 24);
  944. // (i==22) UncompressedSize (Int32) - this value may or may not be
  945. // bonafide.
  946. block[i++] = (byte)(_UncompressedSize & 0x000000FF);
  947. block[i++] = (byte)((_UncompressedSize & 0x0000FF00) >> 8);
  948. block[i++] = (byte)((_UncompressedSize & 0x00FF0000) >> 16);
  949. block[i++] = (byte)((_UncompressedSize & 0xFF000000) >> 24);
  950. }
  951. // (i==26) filename length (Int16)
  952. block[i++] = (byte)(filenameLength & 0x00FF);
  953. block[i++] = (byte)((filenameLength & 0xFF00) >> 8);
  954. _Extra = ConstructExtraField(false);
  955. // (i==28) extra field length (short)
  956. Int16 extraFieldLength = (Int16)((_Extra == null) ? 0 : _Extra.Length);
  957. block[i++] = (byte)(extraFieldLength & 0x00FF);
  958. block[i++] = (byte)((extraFieldLength & 0xFF00) >> 8);
  959. // workitem 13542
  960. byte[] bytes = new byte[i + filenameLength + extraFieldLength];
  961. // get the fixed portion
  962. Buffer.BlockCopy(block, 0, bytes, 0, i);
  963. //for (j = 0; j < i; j++) bytes[j] = block[j];
  964. // The filename written to the archive.
  965. Buffer.BlockCopy(fileNameBytes, 0, bytes, i, fileNameBytes.Length);
  966. // for (j = 0; j < fileNameBytes.Length; j++)
  967. // bytes[i + j] = fileNameBytes[j];
  968. i += fileNameBytes.Length;
  969. // "Extra field"
  970. if (_Extra != null)
  971. {
  972. Buffer.BlockCopy(_Extra, 0, bytes, i, _Extra.Length);
  973. // for (j = 0; j < _Extra.Length; j++)
  974. // bytes[i + j] = _Extra[j];
  975. i += _Extra.Length;
  976. }
  977. _LengthOfHeader = i;
  978. // handle split archives
  979. var zss = s as ZipSegmentedStream;
  980. if (zss != null)
  981. {
  982. zss.ContiguousWrite = true;
  983. UInt32 requiredSegment = zss.ComputeSegment(i);
  984. if (requiredSegment != zss.CurrentSegment)
  985. _future_ROLH = 0; // rollover!
  986. else
  987. _future_ROLH = zss.Position;
  988. _diskNumber = requiredSegment;
  989. }
  990. // validate the ZIP64 usage
  991. if (_container.Zip64 == Zip64Option.Never && (uint)_RelativeOffsetOfLocalHeader >= 0xFFFFFFFF)
  992. throw new ZipException("Offset within the zip archive exceeds 0xFFFFFFFF. Consider setting the UseZip64WhenSaving property on the ZipFile instance.");
  993. // finally, write the header to the stream
  994. s.Write(bytes, 0, i);
  995. // now that the header is written, we can turn off the contiguous write restriction.
  996. if (zss != null)
  997. zss.ContiguousWrite = false;
  998. // Preserve this header data, we'll use it again later.
  999. // ..when seeking backward, to write again, after we have the Crc, compressed
  1000. // and uncompressed sizes.
  1001. // ..and when writing the central directory structure.
  1002. _EntryHeader = bytes;
  1003. }
  1004. Int32 FigureCrc32()
  1005. {
  1006. if (_crcCalculated == false)
  1007. {
  1008. Stream input = null;
  1009. // get the original stream:
  1010. if (this._Source == ZipEntrySource.WriteDelegate)
  1011. {
  1012. var output = new Ionic.Crc.CrcCalculatorStream(Stream.Null);
  1013. // allow the application to write the data
  1014. this._WriteDelegate(this.FileName, output);
  1015. _Crc32 = output.Crc;
  1016. }
  1017. else if (this._Source == ZipEntrySource.ZipFile)
  1018. {
  1019. // nothing to do - the CRC is already set
  1020. }
  1021. else
  1022. {
  1023. if (this._Source == ZipEntrySource.Stream)
  1024. {
  1025. PrepSourceStream();
  1026. input = this._sourceStream;
  1027. }
  1028. else if (this._Source == ZipEntrySource.JitStream)
  1029. {
  1030. // allow the application to open the stream
  1031. if (this._sourceStream == null)
  1032. _sourceStream = this._OpenDelegate(this.FileName);
  1033. PrepSourceStream();
  1034. input = this._sourceStream;
  1035. }
  1036. else if (this._Source == ZipEntrySource.ZipOutputStream)
  1037. {
  1038. }
  1039. else
  1040. {
  1041. //input = File.OpenRead(LocalFileName);
  1042. input = File.Open(LocalFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
  1043. }
  1044. var crc32 = new Ionic.Crc.CRC32();
  1045. _Crc32 = crc32.GetCrc32(input);
  1046. if (_sourceStream == null)
  1047. {
  1048. #if NETCF
  1049. input.Close();
  1050. #else
  1051. input.Dispose();
  1052. #endif
  1053. }
  1054. }
  1055. _crcCalculated = true;
  1056. }
  1057. return _Crc32;
  1058. }
  1059. /// <summary>
  1060. /// Stores the position of the entry source stream, or, if the position is
  1061. /// already stored, seeks to that position.
  1062. /// </summary>
  1063. ///
  1064. /// <remarks>
  1065. /// <para>
  1066. /// This method is called in prep for reading the source stream. If PKZIP
  1067. /// encryption is used, then we need to calc the CRC32 before doing the
  1068. /// encryption, because the CRC is used in the 12th byte of the PKZIP
  1069. /// encryption header. So, we need to be able to seek backward in the source
  1070. /// when saving the ZipEntry. This method is called from the place that
  1071. /// calculates the CRC, and also from the method that does the encryption of
  1072. /// the file data.
  1073. /// </para>
  1074. ///
  1075. /// <para>
  1076. /// The first time through, this method sets the _sourceStreamOriginalPosition
  1077. /// field. Subsequent calls to this method seek to that position.
  1078. /// </para>
  1079. /// </remarks>
  1080. private void PrepSourceStream()
  1081. {
  1082. if (_sourceStream == null)
  1083. throw new ZipException(String.Format("The input stream is null for entry '{0}'.", FileName));
  1084. if (this._sourceStreamOriginalPosition != null)
  1085. {
  1086. // this will happen the 2nd cycle through, if the stream is seekable
  1087. this._sourceStream.Position = this._sourceStreamOriginalPosition.Value;
  1088. }
  1089. else if (this._sourceStream.CanSeek)
  1090. {
  1091. // this will happen the first cycle through, if seekable
  1092. this._sourceStreamOriginalPosition = new Nullable<Int64>(this._sourceStream.Position);
  1093. }
  1094. else if (this.Encryption == EncryptionAlgorithm.PkzipWeak)
  1095. {
  1096. // In general, using PKZIP encryption on a a zip entry whose input
  1097. // comes from a non-seekable stream, is tricky. Here's why:
  1098. //
  1099. // Byte 11 of the PKZIP encryption header is used for password
  1100. // validation and consistency checknig.
  1101. //
  1102. // Normally, the highest byte of the CRC is used as the 11th (last) byte
  1103. // in the PKZIP encryption header. This means the CRC must be known
  1104. // before encryption is performed. Normally that means we read the full
  1105. // data stream, compute the CRC, then seek back and read it again for
  1106. // the compression+encryption phase. Obviously this is bad for
  1107. // performance with a large input file.
  1108. //
  1109. // There's a twist in the ZIP spec (actually documented only in infozip
  1110. // code, not in the spec itself) that allows the high-order byte of the
  1111. // last modified time for the entry, when the lastmod time is in packed
  1112. // (DOS) format, to be used for Byte 11 in the encryption header. In
  1113. // this case, the bit 3 "data descriptor" must be used.
  1114. //
  1115. // An intelligent implementation would therefore force the use of the
  1116. // bit 3 data descriptor when PKZIP encryption is in use, regardless.
  1117. // This avoids the double-read of the stream to be encrypted. So far,
  1118. // DotNetZip doesn't do that; it just punts when the input stream is
  1119. // non-seekable, and the output does not use Bit 3.
  1120. //
  1121. // The other option is to use the CRC when it is already available, eg,
  1122. // when the source for the data is a ZipEntry (when the zip file is
  1123. // being updated). In this case we already know the CRC and can just use
  1124. // what we know.
  1125. if (this._Source != ZipEntrySource.ZipFile && ((this._BitField & 0x0008) != 0x0008))
  1126. throw new ZipException("It is not possible to use PKZIP encryption on a non-seekable input stream");
  1127. }
  1128. }
  1129. /// <summary>
  1130. /// Copy metadata that may have been changed by the app. We do this when
  1131. /// resetting the zipFile instance. If the app calls Save() on a ZipFile, then
  1132. /// tries to party on that file some more, we may need to Reset() it , which
  1133. /// means re-reading the entries and then copying the metadata. I think.
  1134. /// </summary>
  1135. internal void CopyMetaData(ZipEntry source)
  1136. {
  1137. this.__FileDataPosition = source.__FileDataPosition;
  1138. this.CompressionMethod = source.CompressionMethod;
  1139. this._CompressionMethod_FromZipFile = source._CompressionMethod_FromZipFile;
  1140. this._CompressedFileDataSize = source._CompressedFileDataSize;
  1141. this._UncompressedSize = source._UncompressedSize;
  1142. this._BitField = source._BitField;
  1143. this._Source = source._Source;
  1144. this._LastModified = source._LastModified;
  1145. this._Mtime = source._Mtime;
  1146. this._Atime = source._Atime;
  1147. this._Ctime = source._Ctime;
  1148. this._ntfsTimesAreSet = source._ntfsTimesAreSet;
  1149. this._emitUnixTimes = source._emitUnixTimes;
  1150. this._emitNtfsTimes = source._emitNtfsTimes;
  1151. }
  1152. private void OnWriteBlock(Int64 bytesXferred, Int64 totalBytesToXfer)
  1153. {
  1154. if (_container.ZipFile != null)
  1155. _ioOperationCanceled = _container.ZipFile.OnSaveBlock(this, bytesXferred, totalBytesToXfer);
  1156. }
  1157. private void _WriteEntryData(Stream s)
  1158. {
  1159. // Read in the data from the input stream (often a file in the filesystem),
  1160. // and write it to the output stream, calculating a CRC on it as we go.
  1161. // We will also compress and encrypt as necessary.
  1162. Stream input = null;
  1163. long fdp = -1L;
  1164. try
  1165. {
  1166. // Want to record the position in the zip file of the zip entry
  1167. // data (as opposed to the metadata). s.Position may fail on some
  1168. // write-only streams, eg stdout or System.Web.HttpResponseStream.
  1169. // We swallow that exception, because we don't care, in that case.
  1170. // But, don't set __FileDataPosition directly. It may be needed
  1171. // to READ the zip entry from the zip file, if this is a
  1172. // "re-stream" situation. In other words if the zip entry has
  1173. // changed compression level, or compression method, or (maybe?)
  1174. // encryption algorithm. In that case if the original entry is
  1175. // encrypted, we need __FileDataPosition to be the value for the
  1176. // input zip file. This s.Position is for the output zipfile. So
  1177. // we copy fdp to __FileDataPosition after this entry has been
  1178. // (maybe) restreamed.
  1179. fdp = s.Position;
  1180. }
  1181. catch (Exception) { }
  1182. try
  1183. {
  1184. // Use fileLength for progress updates, and to decide whether we can
  1185. // skip encryption and compression altogether (in case of length==zero)
  1186. long fileLength = SetInputAndFigureFileLength(ref input);
  1187. // Wrap a counting stream around the raw output stream:
  1188. // This is the last thing that happens before the bits go to the
  1189. // application-provided stream.
  1190. //
  1191. // Sometimes s is a CountingStream. Doesn't matter. Wrap it with a
  1192. // counter anyway. We need to count at both levels.
  1193. CountingStream entryCounter = new CountingStream(s);
  1194. Stream encryptor;
  1195. Stream compressor;
  1196. if (fileLength != 0L)
  1197. {
  1198. // Maybe wrap an encrypting stream around the counter: This will
  1199. // happen BEFORE output counting, and AFTER compression, if encryption
  1200. // is used.
  1201. encryptor = MaybeApplyEncryption(entryCounter);
  1202. // Maybe wrap a compressing Stream around that.
  1203. // This will happen BEFORE encryption (if any) as we write data out.
  1204. compressor = MaybeApplyCompression(encryptor, fileLength);
  1205. }
  1206. else
  1207. {
  1208. encryptor = compressor = entryCounter;
  1209. }
  1210. // Wrap a CrcCalculatorStream around that.
  1211. // This will happen BEFORE compression (if any) as we write data out.
  1212. var output = new Ionic.Crc.CrcCalculatorStream(compressor, true);
  1213. // output.Write() causes this flow:
  1214. // calc-crc -> compress -> encrypt -> count -> actually write
  1215. if (this._Source == ZipEntrySource.WriteDelegate)
  1216. {
  1217. // allow the application to write the data
  1218. this._WriteDelegate(this.FileName, output);
  1219. }
  1220. else
  1221. {
  1222. // synchronously copy the input stream to the output stream-chain
  1223. byte[] buffer = new byte[BufferSize];
  1224. int n;
  1225. while ((n = SharedUtilities.ReadWithRetry(input, buffer, 0, buffer.Length, FileName)) != 0)
  1226. {
  1227. output.Write(buffer, 0, n);
  1228. OnWriteBlock(output.TotalBytesSlurped, fileLength);
  1229. if (_ioOperationCanceled)
  1230. break;
  1231. }
  1232. }
  1233. FinishOutputStream(s, entryCounter, encryptor, compressor, output);
  1234. }
  1235. finally
  1236. {
  1237. if (this._Source == ZipEntrySource.JitStream)
  1238. {
  1239. // allow the application to close the stream
  1240. if (this._CloseDelegate != null)
  1241. this._CloseDelegate(this.FileName, input);
  1242. this._sourceStream = null;
  1243. }
  1244. else if ((input as FileStream) != null)
  1245. {
  1246. #if NETCF
  1247. input.Close();
  1248. #else
  1249. input.Dispose();
  1250. #endif
  1251. }
  1252. }
  1253. if (_ioOperationCanceled)
  1254. return;
  1255. // set FDP now, to allow for re-streaming
  1256. this.__FileDataPosition = fdp;
  1257. PostProcessOutput(s);
  1258. }
  1259. /// <summary>
  1260. /// Set the input stream and get its length, if possible. The length is
  1261. /// used for progress updates, AND, to allow an optimization in case of
  1262. /// a stream/file of zero length. In that case we skip the Encrypt and
  1263. /// compression Stream. (like DeflateStream or BZip2OutputStream)
  1264. /// </summary>
  1265. private long SetInputAndFigureFileLength(ref Stream input)
  1266. {
  1267. long fileLength = -1L;
  1268. // get the original stream:
  1269. if (this._Source == ZipEntrySource.Stream)
  1270. {
  1271. PrepSourceStream();
  1272. input = this._sourceStream;
  1273. // Try to get the length, no big deal if not available.
  1274. try { fileLength = this._sourceStream.Length; }
  1275. catch (NotSupportedException) { }
  1276. }
  1277. else if (this._Source == ZipEntrySource.ZipFile)
  1278. {
  1279. // we are "re-streaming" the zip entry.
  1280. string pwd = (_Encryption_FromZipFile == EncryptionAlgorithm.None) ? null : (this._Password ?? this._container.Password);
  1281. this._sourceStream = InternalOpenReader(pwd);
  1282. PrepSourceStream();
  1283. input = this._sourceStream;
  1284. fileLength = this._sourceStream.Length;
  1285. }
  1286. else if (this._Source == ZipEntrySource.JitStream)
  1287. {
  1288. // allow the application to open the stream
  1289. if (this._sourceStream == null) _sourceStream = this._OpenDelegate(this.FileName);
  1290. PrepSourceStream();
  1291. input = this._sourceStream;
  1292. try { fileLength = this._sourceStream.Length; }
  1293. catch (NotSupportedException) { }
  1294. }
  1295. else if (this._Source == ZipEntrySource.FileSystem)
  1296. {
  1297. // workitem 7145
  1298. FileShare fs = FileShare.ReadWrite;
  1299. #if !NETCF
  1300. // FileShare.Delete is not defined for the Compact Framework
  1301. fs |= FileShare.Delete;
  1302. #endif
  1303. // workitem 8423
  1304. input = File.Open(LocalFileName, FileMode.Open, FileAccess.Read, fs);
  1305. fileLength = input.Length;
  1306. }
  1307. return fileLength;
  1308. }
  1309. internal void FinishOutputStream(Stream s,
  1310. CountingStream entryCounter,
  1311. Stream encryptor,
  1312. Stream compressor,
  1313. Ionic.Crc.CrcCalculatorStream output)
  1314. {
  1315. if (output == null) return;
  1316. output.Close();
  1317. // by calling Close() on the deflate stream, we write the footer bytes, as necessary.
  1318. if ((compressor as Ionic.Zlib.DeflateStream) != null)
  1319. compressor.Close();
  1320. #if BZIP
  1321. else if ((compressor as Ionic.BZip2.BZip2OutputStream) != null)
  1322. compressor.Close();
  1323. #if !NETCF
  1324. else if ((compressor as Ionic.BZip2.ParallelBZip2OutputStream) != null)
  1325. compressor.Close();
  1326. #endif
  1327. #endif
  1328. #if !NETCF
  1329. else if ((compressor as Ionic.Zlib.ParallelDeflateOutputStream) != null)
  1330. compressor.Close();
  1331. #endif
  1332. encryptor.Flush();
  1333. encryptor.Close();
  1334. _LengthOfTrailer = 0;
  1335. _UncompressedSize = output.TotalBytesSlurped;
  1336. #if AESCRYPTO
  1337. WinZipAesCipherStream wzacs = encryptor as WinZipAesCipherStream;
  1338. if (wzacs != null && _UncompressedSize > 0)
  1339. {
  1340. s.Write(wzacs.FinalAuthentication, 0, 10);
  1341. _LengthOfTrailer += 10;
  1342. }
  1343. #endif
  1344. _CompressedFileDataSize = entryCounter.BytesWritten;
  1345. _CompressedSize = _CompressedFileDataSize; // may be adjusted
  1346. _Crc32 = output.Crc;
  1347. // Set _RelativeOffsetOfLocalHeader now, to allow for re-streaming
  1348. StoreRelativeOffset();
  1349. }
  1350. internal void PostProcessOutput(Stream s)
  1351. {
  1352. var s1 = s as CountingStream;
  1353. // workitem 8931 - for WriteDelegate.
  1354. // The WriteDelegate changes things because there can be a zero-byte stream
  1355. // written. In all other cases DotNetZip knows the length of the stream
  1356. // before compressing and encrypting. In this case we have to circle back,
  1357. // and omit all the crypto stuff - the GP bitfield, and the crypto header.
  1358. if (_UncompressedSize == 0 && _CompressedSize == 0)
  1359. {
  1360. if (this._Source == ZipEntrySource.ZipOutputStream) return; // nothing to do...
  1361. if (_Password != null)
  1362. {
  1363. int headerBytesToRetract = 0;
  1364. if (Encryption == EncryptionAlgorithm.PkzipWeak)
  1365. headerBytesToRetract = 12;
  1366. #if AESCRYPTO
  1367. else if (Encryption == EncryptionAlgorithm.WinZipAes128 ||
  1368. Encryption == EncryptionAlgorithm.WinZipAes256)
  1369. {
  1370. headerBytesToRetract = _aesCrypto_forWrite._Salt.Length + _aesCrypto_forWrite.GeneratedPV.Length;
  1371. }
  1372. #endif
  1373. if (this._Source == ZipEntrySource.ZipOutputStream && !s.CanSeek)
  1374. throw new ZipException("Zero bytes written, encryption in use, and non-seekable output.");
  1375. if (Encryption != EncryptionAlgorithm.None)
  1376. {
  1377. // seek back in the stream to un-output the security metadata
  1378. s.Seek(-1 * headerBytesToRetract, SeekOrigin.Current);
  1379. s.SetLength(s.Position);
  1380. // workitem 10178
  1381. Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
  1382. // workitem 11131
  1383. // adjust the count on the CountingStream as necessary
  1384. if (s1 != null) s1.Adjust(headerBytesToRetract);
  1385. // subtract the size of the security header from the _LengthOfHeader
  1386. _LengthOfHeader -= headerBytesToRetract;
  1387. __FileDataPosition -= headerBytesToRetract;
  1388. }
  1389. _Password = null;
  1390. // turn off the encryption bit
  1391. _BitField &= ~(0x0001);
  1392. // copy the updated bitfield value into the header
  1393. int j = 6;
  1394. _EntryHeader[j++] = (byte)(_BitField & 0x00FF);
  1395. _EntryHeader[j++] = (byte)((_BitField & 0xFF00) >> 8);
  1396. #if AESCRYPTO
  1397. if (Encryption == EncryptionAlgorithm.WinZipAes128 ||
  1398. Encryption == EncryptionAlgorithm.WinZipAes256)
  1399. {
  1400. // Fix the extra field - overwrite the 0x9901 headerId
  1401. // with dummy data. (arbitrarily, 0x9999)
  1402. Int16 fnLength = (short)(_EntryHeader[26] + _EntryHeader[27] * 256);
  1403. int offx = 30 + fnLength;
  1404. int aesIndex = FindExtraFieldSegment(_EntryHeader, offx, 0x9901);
  1405. if (aesIndex >= 0)
  1406. {
  1407. _EntryHeader[aesIndex++] = 0x99;
  1408. _EntryHeader[aesIndex++] = 0x99;
  1409. }
  1410. }
  1411. #endif
  1412. }
  1413. CompressionMethod = 0;
  1414. Encryption = EncryptionAlgorithm.None;
  1415. }
  1416. else if (_zipCrypto_forWrite != null
  1417. #if AESCRYPTO
  1418. || _aesCrypto_forWrite != null
  1419. #endif
  1420. )
  1421. {
  1422. if (Encryption == EncryptionAlgorithm.PkzipWeak)
  1423. {
  1424. _CompressedSize += 12; // 12 extra bytes for the encryption header
  1425. }
  1426. #if AESCRYPTO
  1427. else if (Encryption == EncryptionAlgorithm.WinZipAes128 ||
  1428. Encryption == EncryptionAlgorithm.WinZipAes256)
  1429. {
  1430. // adjust the compressed size to include the variable (salt+pv)
  1431. // security header and 10-byte trailer. According to the winzip AES
  1432. // spec, that metadata is included in the "Compressed Size" figure
  1433. // when encoding the zip archive.
  1434. _CompressedSize += _aesCrypto_forWrite.SizeOfEncryptionMetadata;
  1435. }
  1436. #endif
  1437. }
  1438. int i = 8;
  1439. _EntryHeader[i++] = (byte)(_CompressionMethod & 0x00FF);
  1440. _EntryHeader[i++] = (byte)((_CompressionMethod & 0xFF00) >> 8);
  1441. i = 14;
  1442. // CRC - the correct value now
  1443. _EntryHeader[i++] = (byte)(_Crc32 & 0x000000FF);
  1444. _EntryHeader[i++] = (byte)((_Crc32 & 0x0000FF00) >> 8);
  1445. _EntryHeader[i++] = (byte)((_Crc32 & 0x00FF0000) >> 16);
  1446. _EntryHeader[i++] = (byte)((_Crc32 & 0xFF000000) >> 24);
  1447. SetZip64Flags();
  1448. // (i==26) filename length (Int16)
  1449. Int16 filenameLength = (short)(_EntryHeader[26] + _EntryHeader[27] * 256);
  1450. Int16 extraFieldLength = (short)(_EntryHeader[28] + _EntryHeader[29] * 256);
  1451. if (_OutputUsesZip64.Value)
  1452. {
  1453. // VersionNeededToExtract - set to 45 to indicate zip64
  1454. _EntryHeader[4] = (byte)(45 & 0x00FF);
  1455. _EntryHeader[5] = 0x00;
  1456. // workitem 7924 - don't need bit 3
  1457. // // workitem 7917
  1458. // // set bit 3 for ZIP64 compatibility with WinZip12
  1459. // _BitField |= 0x0008;
  1460. // _EntryHeader[6] = (byte)(_BitField & 0x00FF);
  1461. // CompressedSize and UncompressedSize - 0xFF
  1462. for (int j = 0; j < 8; j++)
  1463. _EntryHeader[i++] = 0xff;
  1464. // At this point we need to find the "Extra field" that follows the
  1465. // filename. We had already emitted it, but the data (uncomp, comp,
  1466. // ROLH) was not available at the time we did so. Here, we emit it
  1467. // again, with final values.
  1468. i = 30 + filenameLength;
  1469. _EntryHeader[i++] = 0x01; // zip64
  1470. _EntryHeader[i++] = 0x00;
  1471. i += 2; // skip over data size, which is 16+4
  1472. Array.Copy(BitConverter.GetBytes(_UncompressedSize), 0, _EntryHeader, i, 8);
  1473. i += 8;
  1474. Array.Copy(BitConverter.GetBytes(_CompressedSize), 0, _EntryHeader, i, 8);
  1475. }
  1476. else
  1477. {
  1478. // VersionNeededToExtract - reset to 20 since no zip64
  1479. _EntryHeader[4] = (byte)(20 & 0x00FF);
  1480. _EntryHeader[5] = 0x00;
  1481. // CompressedSize - the correct value now
  1482. i = 18;
  1483. _EntryHeader[i++] = (byte)(_CompressedSize & 0x000000FF);
  1484. _EntryHeader[i++] = (byte)((_CompressedSize & 0x0000FF00) >> 8);
  1485. _EntryHeader[i++] = (byte)((_CompressedSize & 0x00FF0000) >> 16);
  1486. _EntryHeader[i++] = (byte)((_CompressedSize & 0xFF000000) >> 24);
  1487. // UncompressedSize - the correct value now
  1488. _EntryHeader[i++] = (byte)(_UncompressedSize & 0x000000FF);
  1489. _EntryHeader[i++] = (byte)((_UncompressedSize & 0x0000FF00) >> 8);
  1490. _EntryHeader[i++] = (byte)((_UncompressedSize & 0x00FF0000) >> 16);
  1491. _EntryHeader[i++] = (byte)((_UncompressedSize & 0xFF000000) >> 24);
  1492. // The HeaderId in the extra field header, is already dummied out.
  1493. if (extraFieldLength != 0)
  1494. {
  1495. i = 30 + filenameLength;
  1496. // For zip archives written by this library, if the zip64
  1497. // header exists, it is the first header. Because of the logic
  1498. // used when first writing the _EntryHeader bytes, the
  1499. // HeaderId is not guaranteed to be any particular value. So
  1500. // we determine if the first header is a putative zip64 header
  1501. // by examining the datasize. UInt16 HeaderId =
  1502. // (UInt16)(_EntryHeader[i] + _EntryHeader[i + 1] * 256);
  1503. Int16 DataSize = (short)(_EntryHeader[i + 2] + _EntryHeader[i + 3] * 256);
  1504. if (DataSize == 16)
  1505. {
  1506. // reset to Header Id to dummy value, effectively dummy-ing out the zip64 metadata
  1507. _EntryHeader[i++] = 0x99;
  1508. _EntryHeader[i++] = 0x99;
  1509. }
  1510. }
  1511. }
  1512. #if AESCRYPTO
  1513. if (Encryption == EncryptionAlgorithm.WinZipAes128 ||
  1514. Encryption == EncryptionAlgorithm.WinZipAes256)
  1515. {
  1516. // Must set compressionmethod to 0x0063 (decimal 99)
  1517. //
  1518. // and then set the compression method bytes inside the extra
  1519. // field to the actual compression method value.
  1520. i = 8;
  1521. _EntryHeader[i++] = 0x63;
  1522. _EntryHeader[i++] = 0;
  1523. i = 30 + filenameLength;
  1524. do
  1525. {
  1526. UInt16 HeaderId = (UInt16)(_EntryHeader[i] + _EntryHeader[i + 1] * 256);
  1527. Int16 DataSize = (short)(_EntryHeader[i + 2] + _EntryHeader[i + 3] * 256);
  1528. if (HeaderId != 0x9901)
  1529. {
  1530. // skip this header
  1531. i += DataSize + 4;
  1532. }
  1533. else
  1534. {
  1535. i += 9;
  1536. // actual compression method
  1537. _EntryHeader[i++] = (byte)(_CompressionMethod & 0x00FF);
  1538. _EntryHeader[i++] = (byte)(_CompressionMethod & 0xFF00);
  1539. }
  1540. } while (i < (extraFieldLength - 30 - filenameLength));
  1541. }
  1542. #endif
  1543. // finally, write the data.
  1544. // workitem 7216 - sometimes we don't seek even if we CAN. ASP.NET
  1545. // Response.OutputStream, or stdout are non-seekable. But we may also want
  1546. // to NOT seek in other cases, eg zip64. For all cases, we just check bit 3
  1547. // to see if we want to seek. There's one exception - if using a
  1548. // ZipOutputStream, and PKZip encryption is in use, then we set bit 3 even
  1549. // if the out is seekable. This is so the check on the last byte of the
  1550. // PKZip Encryption Header can be done on the current time, as opposed to
  1551. // the CRC, to prevent streaming the file twice. So, test for
  1552. // ZipOutputStream and seekable, and if so, seek back, even if bit 3 is set.
  1553. if ((_BitField & 0x0008) != 0x0008 ||
  1554. (this._Source == ZipEntrySource.ZipOutputStream && s.CanSeek))
  1555. {
  1556. // seek back and rewrite the entry header
  1557. var zss = s as ZipSegmentedStream;
  1558. if (zss != null && _diskNumber != zss.CurrentSegment)
  1559. {
  1560. // In this case the entry header is in a different file,
  1561. // which has already been closed. Need to re-open it.
  1562. using (Stream hseg = ZipSegmentedStream.ForUpdate(this._container.ZipFile.Name, _diskNumber))
  1563. {
  1564. hseg.Seek(this._RelativeOffsetOfLocalHeader, SeekOrigin.Begin);
  1565. hseg.Write(_EntryHeader, 0, _EntryHeader.Length);
  1566. }
  1567. }
  1568. else
  1569. {
  1570. // seek in the raw output stream, to the beginning of the header for
  1571. // this entry.
  1572. // workitem 8098: ok (output)
  1573. s.Seek(this._RelativeOffsetOfLocalHeader, SeekOrigin.Begin);
  1574. // write the updated header to the output stream
  1575. s.Write(_EntryHeader, 0, _EntryHeader.Length);
  1576. // adjust the count on the CountingStream as necessary
  1577. if (s1 != null) s1.Adjust(_EntryHeader.Length);
  1578. // seek in the raw output stream, to the end of the file data
  1579. // for this entry
  1580. s.Seek(_CompressedSize, SeekOrigin.Current);
  1581. }
  1582. }
  1583. // emit the descriptor - only if not a directory.
  1584. if (((_BitField & 0x0008) == 0x0008) && !IsDirectory)
  1585. {
  1586. byte[] Descriptor = new byte[16 + (_OutputUsesZip64.Value ? 8 : 0)];
  1587. i = 0;
  1588. // signature
  1589. Array.Copy(BitConverter.GetBytes(ZipConstants.ZipEntryDataDescriptorSignature), 0, Descriptor, i, 4);
  1590. i += 4;
  1591. // CRC - the correct value now
  1592. Array.Copy(BitConverter.GetBytes(_Crc32), 0, Descriptor, i, 4);
  1593. i += 4;
  1594. // workitem 7917
  1595. if (_OutputUsesZip64.Value)
  1596. {
  1597. // CompressedSize - the correct value now
  1598. Array.Copy(BitConverter.GetBytes(_CompressedSize), 0, Descriptor, i, 8);
  1599. i += 8;
  1600. // UncompressedSize - the correct value now
  1601. Array.Copy(BitConverter.GetBytes(_UncompressedSize), 0, Descriptor, i, 8);
  1602. i += 8;
  1603. }
  1604. else
  1605. {
  1606. // CompressedSize - (lower 32 bits) the correct value now
  1607. Descriptor[i++] = (byte)(_CompressedSize & 0x000000FF);
  1608. Descriptor[i++] = (byte)((_CompressedSize & 0x0000FF00) >> 8);
  1609. Descriptor[i++] = (byte)((_CompressedSize & 0x00FF0000) >> 16);
  1610. Descriptor[i++] = (byte)((_CompressedSize & 0xFF000000) >> 24);
  1611. // UncompressedSize - (lower 32 bits) the correct value now
  1612. Descriptor[i++] = (byte)(_UncompressedSize & 0x000000FF);
  1613. Descriptor[i++] = (byte)((_UncompressedSize & 0x0000FF00) >> 8);
  1614. Descriptor[i++] = (byte)((_UncompressedSize & 0x00FF0000) >> 16);
  1615. Descriptor[i++] = (byte)((_UncompressedSize & 0xFF000000) >> 24);
  1616. }
  1617. // finally, write the trailing descriptor to the output stream
  1618. s.Write(Descriptor, 0, Descriptor.Length);
  1619. _LengthOfTrailer += Descriptor.Length;
  1620. }
  1621. }
  1622. private void SetZip64Flags()
  1623. {
  1624. // zip64 housekeeping
  1625. _entryRequiresZip64 = new Nullable<bool>
  1626. (_CompressedSize >= 0xFFFFFFFF || _UncompressedSize >= 0xFFFFFFFF || _RelativeOffsetOfLocalHeader >= 0xFFFFFFFF);
  1627. // validate the ZIP64 usage
  1628. if (_container.Zip64 == Zip64Option.Never && _entryRequiresZip64.Value)
  1629. throw new ZipException("Compressed or Uncompressed size, or offset exceeds the maximum value. Consider setting the UseZip64WhenSaving property on the ZipFile instance.");
  1630. _OutputUsesZip64 = new Nullable<bool>(_container.Zip64 == Zip64Option.Always || _entryRequiresZip64.Value);
  1631. }
  1632. /// <summary>
  1633. /// Prepare the given stream for output - wrap it in a CountingStream, and
  1634. /// then in a CRC stream, and an encryptor and deflator as appropriate.
  1635. /// </summary>
  1636. /// <remarks>
  1637. /// <para>
  1638. /// Previously this was used in ZipEntry.Write(), but in an effort to
  1639. /// introduce some efficiencies in that method I've refactored to put the
  1640. /// code inline. This method still gets called by ZipOutputStream.
  1641. /// </para>
  1642. /// </remarks>
  1643. internal void PrepOutputStream(Stream s,
  1644. long streamLength,
  1645. out CountingStream outputCounter,
  1646. out Stream encryptor,
  1647. out Stream compressor,
  1648. out Ionic.Crc.CrcCalculatorStream output)
  1649. {
  1650. TraceWriteLine("PrepOutputStream: e({0}) comp({1}) crypto({2}) zf({3})",
  1651. FileName,
  1652. CompressionLevel,
  1653. Encryption,
  1654. _container.Name);
  1655. // Wrap a counting stream around the raw output stream:
  1656. // This is the last thing that happens before the bits go to the
  1657. // application-provided stream.
  1658. outputCounter = new CountingStream(s);
  1659. // Sometimes the incoming "raw" output stream is already a CountingStream.
  1660. // Doesn't matter. Wrap it with a counter anyway. We need to count at both
  1661. // levels.
  1662. if (streamLength != 0L)
  1663. {
  1664. // Maybe wrap an encrypting stream around that:
  1665. // This will happen BEFORE output counting, and AFTER deflation, if encryption
  1666. // is used.
  1667. encryptor = MaybeApplyEncryption(outputCounter);
  1668. // Maybe wrap a compressing Stream around that.
  1669. // This will happen BEFORE encryption (if any) as we write data out.
  1670. compressor = MaybeApplyCompression(encryptor, streamLength);
  1671. }
  1672. else
  1673. {
  1674. encryptor = compressor = outputCounter;
  1675. }
  1676. // Wrap a CrcCalculatorStream around that.
  1677. // This will happen BEFORE compression (if any) as we write data out.
  1678. output = new Ionic.Crc.CrcCalculatorStream(compressor, true);
  1679. }
  1680. private Stream MaybeApplyCompression(Stream s, long streamLength)
  1681. {
  1682. if (_CompressionMethod == 0x08 && CompressionLevel != Ionic.Zlib.CompressionLevel.None)
  1683. {
  1684. #if !NETCF
  1685. // ParallelDeflateThreshold == 0 means ALWAYS use parallel deflate
  1686. // ParallelDeflateThreshold == -1L means NEVER use parallel deflate
  1687. // Other values specify the actual threshold.
  1688. if (_container.ParallelDeflateThreshold == 0L ||
  1689. (streamLength > _container.ParallelDeflateThreshold &&
  1690. _container.ParallelDeflateThreshold > 0L))
  1691. {
  1692. // This is sort of hacky.
  1693. //
  1694. // It's expensive to create a ParallelDeflateOutputStream, because
  1695. // of the large memory buffers. But the class is unlike most Stream
  1696. // classes in that it can be re-used, so the caller can compress
  1697. // multiple files with it, one file at a time. The key is to call
  1698. // Reset() on it, in between uses.
  1699. //
  1700. // The ParallelDeflateOutputStream is attached to the container
  1701. // itself - there is just one for the entire ZipFile or
  1702. // ZipOutputStream. So it gets created once, per save, and then
  1703. // re-used many times.
  1704. //
  1705. // This approach will break when we go to a "parallel save"
  1706. // approach, where multiple entries within the zip file are being
  1707. // compressed and saved at the same time. But for now it's ok.
  1708. //
  1709. // instantiate the ParallelDeflateOutputStream
  1710. if (_container.ParallelDeflater == null)
  1711. {
  1712. _container.ParallelDeflater =
  1713. new Ionic.Zlib.ParallelDeflateOutputStream(s,
  1714. CompressionLevel,
  1715. _container.Strategy,
  1716. true);
  1717. // can set the codec buffer size only before the first call to Write().
  1718. if (_container.CodecBufferSize > 0)
  1719. _container.ParallelDeflater.BufferSize = _container.CodecBufferSize;
  1720. if (_container.ParallelDeflateMaxBufferPairs > 0)
  1721. _container.ParallelDeflater.MaxBufferPairs =
  1722. _container.ParallelDeflateMaxBufferPairs;
  1723. }
  1724. // reset it with the new stream
  1725. Ionic.Zlib.ParallelDeflateOutputStream o1 = _container.ParallelDeflater;
  1726. o1.Reset(s);
  1727. return o1;
  1728. }
  1729. #endif
  1730. var o = new Ionic.Zlib.DeflateStream(s, Ionic.Zlib.CompressionMode.Compress,
  1731. CompressionLevel,
  1732. true);
  1733. if (_container.CodecBufferSize > 0)
  1734. o.BufferSize = _container.CodecBufferSize;
  1735. o.Strategy = _container.Strategy;
  1736. return o;
  1737. }
  1738. #if BZIP
  1739. if (_CompressionMethod == 0x0c)
  1740. {
  1741. #if !NETCF
  1742. if (_container.ParallelDeflateThreshold == 0L ||
  1743. (streamLength > _container.ParallelDeflateThreshold &&
  1744. _container.ParallelDeflateThreshold > 0L))
  1745. {
  1746. var o1 = new Ionic.BZip2.ParallelBZip2OutputStream(s, true);
  1747. return o1;
  1748. }
  1749. #endif
  1750. var o = new Ionic.BZip2.BZip2OutputStream(s, true);
  1751. return o;
  1752. }
  1753. #endif
  1754. return s;
  1755. }
  1756. private Stream MaybeApplyEncryption(Stream s)
  1757. {
  1758. if (Encryption == EncryptionAlgorithm.PkzipWeak)
  1759. {
  1760. TraceWriteLine("MaybeApplyEncryption: e({0}) PKZIP", FileName);
  1761. return new ZipCipherStream(s, _zipCrypto_forWrite, CryptoMode.Encrypt);
  1762. }
  1763. #if AESCRYPTO
  1764. if (Encryption == EncryptionAlgorithm.WinZipAes128 ||
  1765. Encryption == EncryptionAlgorithm.WinZipAes256)
  1766. {
  1767. TraceWriteLine("MaybeApplyEncryption: e({0}) AES", FileName);
  1768. return new WinZipAesCipherStream(s, _aesCrypto_forWrite, CryptoMode.Encrypt);
  1769. }
  1770. #endif
  1771. TraceWriteLine("MaybeApplyEncryption: e({0}) None", FileName);
  1772. return s;
  1773. }
  1774. private void OnZipErrorWhileSaving(Exception e)
  1775. {
  1776. if (_container.ZipFile != null)
  1777. _ioOperationCanceled = _container.ZipFile.OnZipErrorSaving(this, e);
  1778. }
  1779. internal void Write(Stream s)
  1780. {
  1781. var cs1 = s as CountingStream;
  1782. var zss1 = s as ZipSegmentedStream;
  1783. bool done = false;
  1784. do
  1785. {
  1786. try
  1787. {
  1788. // When the app is updating a zip file, it may be possible to
  1789. // just copy data for a ZipEntry from the source zipfile to the
  1790. // destination, as a block, without decompressing and
  1791. // recompressing, etc. But, in some cases the app modifies the
  1792. // properties on a ZipEntry prior to calling Save(). A change to
  1793. // any of the metadata - the FileName, CompressioLeve and so on,
  1794. // means DotNetZip cannot simply copy through the existing
  1795. // ZipEntry data unchanged.
  1796. //
  1797. // There are two cases:
  1798. //
  1799. // 1. Changes to only metadata, which means the header and
  1800. // central directory must be changed.
  1801. //
  1802. // 2. Changes to the properties that affect the compressed
  1803. // stream, such as CompressionMethod, CompressionLevel, or
  1804. // EncryptionAlgorithm. In this case, DotNetZip must
  1805. // "re-stream" the data: the old entry data must be maybe
  1806. // decrypted, maybe decompressed, then maybe re-compressed
  1807. // and maybe re-encrypted.
  1808. //
  1809. // This test checks if the source for the entry data is a zip file, and
  1810. // if a restream is necessary. If NOT, then it just copies through
  1811. // one entry, potentially changing the metadata.
  1812. if (_Source == ZipEntrySource.ZipFile && !_restreamRequiredOnSave)
  1813. {
  1814. CopyThroughOneEntry(s);
  1815. return;
  1816. }
  1817. // Is the entry a directory? If so, the write is relatively simple.
  1818. if (IsDirectory)
  1819. {
  1820. WriteHeader(s, 1);
  1821. StoreRelativeOffset();
  1822. _entryRequiresZip64 = new Nullable<bool>(_RelativeOffsetOfLocalHeader >= 0xFFFFFFFF);
  1823. _OutputUsesZip64 = new Nullable<bool>(_container.Zip64 == Zip64Option.Always || _entryRequiresZip64.Value);
  1824. // handle case for split archives
  1825. if (zss1 != null)
  1826. _diskNumber = zss1.CurrentSegment;
  1827. return;
  1828. }
  1829. // At this point, the source for this entry is not a directory, and
  1830. // not a previously created zip file, or the source for the entry IS
  1831. // a previously created zip but the settings whave changed in
  1832. // important ways and therefore we will need to process the
  1833. // bytestream (compute crc, maybe compress, maybe encrypt) in order
  1834. // to write the content into the new zip.
  1835. //
  1836. // We do this in potentially 2 passes: The first time we do it as
  1837. // requested, maybe with compression and maybe encryption. If that
  1838. // causes the bytestream to inflate in size, and if compression was
  1839. // on, then we turn off compression and do it again.
  1840. bool readAgain = true;
  1841. int nCycles = 0;
  1842. do
  1843. {
  1844. nCycles++;
  1845. WriteHeader(s, nCycles);
  1846. // write the encrypted header
  1847. WriteSecurityMetadata(s);
  1848. // write the (potentially compressed, potentially encrypted) file data
  1849. _WriteEntryData(s);
  1850. // track total entry size (including the trailing descriptor and MAC)
  1851. _TotalEntrySize = _LengthOfHeader + _CompressedFileDataSize + _LengthOfTrailer;
  1852. // The file data has now been written to the stream, and
  1853. // the file pointer is positioned directly after file data.
  1854. if (nCycles > 1) readAgain = false;
  1855. else if (!s.CanSeek) readAgain = false;
  1856. else readAgain = WantReadAgain();
  1857. if (readAgain)
  1858. {
  1859. // Seek back in the raw output stream, to the beginning of the file
  1860. // data for this entry.
  1861. // handle case for split archives
  1862. if (zss1 != null)
  1863. {
  1864. // Console.WriteLine("***_diskNumber/first: {0}", _diskNumber);
  1865. // Console.WriteLine("***_diskNumber/current: {0}", zss.CurrentSegment);
  1866. zss1.TruncateBackward(_diskNumber, _RelativeOffsetOfLocalHeader);
  1867. }
  1868. else
  1869. // workitem 8098: ok (output).
  1870. s.Seek(_RelativeOffsetOfLocalHeader, SeekOrigin.Begin);
  1871. // If the last entry expands, we read again; but here, we must
  1872. // truncate the stream to prevent garbage data after the
  1873. // end-of-central-directory.
  1874. // workitem 8098: ok (output).
  1875. s.SetLength(s.Position);
  1876. // Adjust the count on the CountingStream as necessary.
  1877. if (cs1 != null) cs1.Adjust(_TotalEntrySize);
  1878. }
  1879. }
  1880. while (readAgain);
  1881. _skippedDuringSave = false;
  1882. done = true;
  1883. }
  1884. catch (System.Exception exc1)
  1885. {
  1886. ZipErrorAction orig = this.ZipErrorAction;
  1887. int loop = 0;
  1888. do
  1889. {
  1890. if (ZipErrorAction == ZipErrorAction.Throw)
  1891. throw;
  1892. if (ZipErrorAction == ZipErrorAction.Skip ||
  1893. ZipErrorAction == ZipErrorAction.Retry)
  1894. {
  1895. // must reset file pointer here.
  1896. // workitem 13903 - seek back only when necessary
  1897. long p1 = (cs1 != null)
  1898. ? cs1.ComputedPosition
  1899. : s.Position;
  1900. long delta = p1 - _future_ROLH;
  1901. if (delta > 0)
  1902. {
  1903. s.Seek(delta, SeekOrigin.Current); // may throw
  1904. long p2 = s.Position;
  1905. s.SetLength(s.Position); // to prevent garbage if this is the last entry
  1906. if (cs1 != null) cs1.Adjust(p1 - p2);
  1907. }
  1908. if (ZipErrorAction == ZipErrorAction.Skip)
  1909. {
  1910. WriteStatus("Skipping file {0} (exception: {1})", LocalFileName, exc1.ToString());
  1911. _skippedDuringSave = true;
  1912. done = true;
  1913. }
  1914. else
  1915. this.ZipErrorAction = orig;
  1916. break;
  1917. }
  1918. if (loop > 0) throw;
  1919. if (ZipErrorAction == ZipErrorAction.InvokeErrorEvent)
  1920. {
  1921. OnZipErrorWhileSaving(exc1);
  1922. if (_ioOperationCanceled)
  1923. {
  1924. done = true;
  1925. break;
  1926. }
  1927. }
  1928. loop++;
  1929. }
  1930. while (true);
  1931. }
  1932. }
  1933. while (!done);
  1934. }
  1935. internal void StoreRelativeOffset()
  1936. {
  1937. _RelativeOffsetOfLocalHeader = _future_ROLH;
  1938. }
  1939. internal void NotifySaveComplete()
  1940. {
  1941. // When updating a zip file, there are two contexts for properties
  1942. // like Encryption or CompressionMethod - the values read from the
  1943. // original zip file, and the values used in the updated zip file.
  1944. // The _FromZipFile versions are the originals. At the end of a save,
  1945. // these values are the same. So we need to update them. This takes
  1946. // care of the boundary case where a single zipfile instance can be
  1947. // saved multiple times, with distinct changes to the properties on
  1948. // the entries, in between each Save().
  1949. _Encryption_FromZipFile = _Encryption;
  1950. _CompressionMethod_FromZipFile = _CompressionMethod;
  1951. _restreamRequiredOnSave = false;
  1952. _metadataChanged = false;
  1953. //_Source = ZipEntrySource.None;
  1954. _Source = ZipEntrySource.ZipFile; // workitem 10694
  1955. }
  1956. internal void WriteSecurityMetadata(Stream outstream)
  1957. {
  1958. if (Encryption == EncryptionAlgorithm.None)
  1959. return;
  1960. string pwd = this._Password;
  1961. // special handling for source == ZipFile.
  1962. // Want to support the case where we re-stream an encrypted entry. This will involve,
  1963. // at runtime, reading, decrypting, and decompressing from the original zip file, then
  1964. // compressing, encrypting, and writing to the output zip file.
  1965. // If that's what we're doing, and the password hasn't been set on the entry,
  1966. // we use the container (ZipFile/ZipOutputStream) password to decrypt.
  1967. // This test here says to use the container password to re-encrypt, as well,
  1968. // with that password, if the entry password is null.
  1969. if (this._Source == ZipEntrySource.ZipFile && pwd == null)
  1970. pwd = this._container.Password;
  1971. if (pwd == null)
  1972. {
  1973. _zipCrypto_forWrite = null;
  1974. #if AESCRYPTO
  1975. _aesCrypto_forWrite = null;
  1976. #endif
  1977. return;
  1978. }
  1979. TraceWriteLine("WriteSecurityMetadata: e({0}) crypto({1}) pw({2})",
  1980. FileName, Encryption.ToString(), pwd);
  1981. if (Encryption == EncryptionAlgorithm.PkzipWeak)
  1982. {
  1983. // If PKZip (weak) encryption is in use, then the encrypted entry data
  1984. // is preceded by 12-byte "encryption header" for the entry.
  1985. _zipCrypto_forWrite = ZipCrypto.ForWrite(pwd);
  1986. // generate the random 12-byte header:
  1987. var rnd = new System.Random();
  1988. byte[] encryptionHeader = new byte[12];
  1989. rnd.NextBytes(encryptionHeader);
  1990. // workitem 8271
  1991. if ((this._BitField & 0x0008) == 0x0008)
  1992. {
  1993. // In the case that bit 3 of the general purpose bit flag is set to
  1994. // indicate the presence of a 'data descriptor' (signature
  1995. // 0x08074b50), the last byte of the decrypted header is sometimes
  1996. // compared with the high-order byte of the lastmodified time,
  1997. // rather than the high-order byte of the CRC, to verify the
  1998. // password.
  1999. //
  2000. // This is not documented in the PKWare Appnote.txt.
  2001. // This was discovered this by analysis of the Crypt.c source file in the
  2002. // InfoZip library
  2003. // http://www.info-zip.org/pub/infozip/
  2004. // Also, winzip insists on this!
  2005. if (_dontEmitLastModified)
  2006. {
  2007. _TimeBlob = 0;
  2008. }
  2009. else
  2010. {
  2011. _TimeBlob = Ionic.Zip.SharedUtilities.DateTimeToPacked(LastModified);
  2012. }
  2013. encryptionHeader[11] = (byte)((this._TimeBlob >> 8) & 0xff);
  2014. }
  2015. else
  2016. {
  2017. // When bit 3 is not set, the CRC value is required before
  2018. // encryption of the file data begins. In this case there is no way
  2019. // around it: must read the stream in its entirety to compute the
  2020. // actual CRC before proceeding.
  2021. FigureCrc32();
  2022. encryptionHeader[11] = (byte)((this._Crc32 >> 24) & 0xff);
  2023. }
  2024. // Encrypt the random header, INCLUDING the final byte which is either
  2025. // the high-order byte of the CRC32, or the high-order byte of the
  2026. // _TimeBlob. Must do this BEFORE encrypting the file data. This
  2027. // step changes the state of the cipher, or in the words of the PKZIP
  2028. // spec, it "further initializes" the cipher keys.
  2029. byte[] cipherText = _zipCrypto_forWrite.EncryptMessage(encryptionHeader, encryptionHeader.Length);
  2030. // Write the ciphered bonafide encryption header.
  2031. outstream.Write(cipherText, 0, cipherText.Length);
  2032. _LengthOfHeader += cipherText.Length; // 12 bytes
  2033. }
  2034. #if AESCRYPTO
  2035. else if (Encryption == EncryptionAlgorithm.WinZipAes128 ||
  2036. Encryption == EncryptionAlgorithm.WinZipAes256)
  2037. {
  2038. // If WinZip AES encryption is in use, then the encrypted entry data is
  2039. // preceded by a variable-sized Salt and a 2-byte "password
  2040. // verification" value for the entry.
  2041. int keystrength = GetKeyStrengthInBits(Encryption);
  2042. _aesCrypto_forWrite = WinZipAesCrypto.Generate(pwd, keystrength);
  2043. outstream.Write(_aesCrypto_forWrite.Salt, 0, _aesCrypto_forWrite._Salt.Length);
  2044. outstream.Write(_aesCrypto_forWrite.GeneratedPV, 0, _aesCrypto_forWrite.GeneratedPV.Length);
  2045. _LengthOfHeader += _aesCrypto_forWrite._Salt.Length + _aesCrypto_forWrite.GeneratedPV.Length;
  2046. TraceWriteLine("WriteSecurityMetadata: AES e({0}) keybits({1}) _LOH({2})",
  2047. FileName, keystrength, _LengthOfHeader);
  2048. }
  2049. #endif
  2050. }
  2051. private void CopyThroughOneEntry(Stream outStream)
  2052. {
  2053. // Just read the entry from the existing input zipfile and write to the output.
  2054. // But, if metadata has changed (like file times or attributes), or if the ZIP64
  2055. // option has changed, we can re-stream the entry data but must recompute the
  2056. // metadata.
  2057. if (this.LengthOfHeader == 0)
  2058. throw new BadStateException("Bad header length.");
  2059. // is it necessary to re-constitute new metadata for this entry?
  2060. bool needRecompute = _metadataChanged ||
  2061. (this.ArchiveStream is ZipSegmentedStream) ||
  2062. (outStream is ZipSegmentedStream) ||
  2063. (_InputUsesZip64 && _container.UseZip64WhenSaving == Zip64Option.Never) ||
  2064. (!_InputUsesZip64 && _container.UseZip64WhenSaving == Zip64Option.Always);
  2065. if (needRecompute)
  2066. CopyThroughWithRecompute(outStream);
  2067. else
  2068. CopyThroughWithNoChange(outStream);
  2069. // zip64 housekeeping
  2070. _entryRequiresZip64 = new Nullable<bool>
  2071. (_CompressedSize >= 0xFFFFFFFF || _UncompressedSize >= 0xFFFFFFFF ||
  2072. _RelativeOffsetOfLocalHeader >= 0xFFFFFFFF
  2073. );
  2074. _OutputUsesZip64 = new Nullable<bool>(_container.Zip64 == Zip64Option.Always || _entryRequiresZip64.Value);
  2075. }
  2076. private void CopyThroughWithRecompute(Stream outstream)
  2077. {
  2078. int n;
  2079. byte[] bytes = new byte[BufferSize];
  2080. var input = new CountingStream(this.ArchiveStream);
  2081. long origRelativeOffsetOfHeader = _RelativeOffsetOfLocalHeader;
  2082. // The header length may change due to rename of file, add a comment, etc.
  2083. // We need to retain the original.
  2084. int origLengthOfHeader = LengthOfHeader; // including crypto bytes!
  2085. // WriteHeader() has the side effect of changing _RelativeOffsetOfLocalHeader
  2086. // and setting _LengthOfHeader. While ReadHeader() reads the crypto header if
  2087. // present, WriteHeader() does not write the crypto header.
  2088. WriteHeader(outstream, 0);
  2089. StoreRelativeOffset();
  2090. if (!this.FileName.EndsWith("/"))
  2091. {
  2092. // Not a directory; there is file data.
  2093. // Seek to the beginning of the entry data in the input stream.
  2094. long pos = origRelativeOffsetOfHeader + origLengthOfHeader;
  2095. int len = GetLengthOfCryptoHeaderBytes(_Encryption_FromZipFile);
  2096. pos -= len; // want to keep the crypto header
  2097. _LengthOfHeader += len;
  2098. input.Seek(pos, SeekOrigin.Begin);
  2099. // copy through everything after the header to the output stream
  2100. long remaining = this._CompressedSize;
  2101. while (remaining > 0)
  2102. {
  2103. len = (remaining > bytes.Length) ? bytes.Length : (int)remaining;
  2104. // read
  2105. n = input.Read(bytes, 0, len);
  2106. _CheckRead(n);
  2107. // write
  2108. outstream.Write(bytes, 0, n);
  2109. remaining -= n;
  2110. OnWriteBlock(input.BytesRead, this._CompressedSize);
  2111. if (_ioOperationCanceled)
  2112. break;
  2113. }
  2114. // bit 3 descriptor
  2115. if ((this._BitField & 0x0008) == 0x0008)
  2116. {
  2117. int size = 16;
  2118. if (_InputUsesZip64) size += 8;
  2119. byte[] Descriptor = new byte[size];
  2120. input.Read(Descriptor, 0, size);
  2121. if (_InputUsesZip64 && _container.UseZip64WhenSaving == Zip64Option.Never)
  2122. {
  2123. // original descriptor was 24 bytes, now we need 16.
  2124. // Must check for underflow here.
  2125. // signature + CRC.
  2126. outstream.Write(Descriptor, 0, 8);
  2127. // Compressed
  2128. if (_CompressedSize > 0xFFFFFFFF)
  2129. throw new InvalidOperationException("ZIP64 is required");
  2130. outstream.Write(Descriptor, 8, 4);
  2131. // UnCompressed
  2132. if (_UncompressedSize > 0xFFFFFFFF)
  2133. throw new InvalidOperationException("ZIP64 is required");
  2134. outstream.Write(Descriptor, 16, 4);
  2135. _LengthOfTrailer -= 8;
  2136. }
  2137. else if (!_InputUsesZip64 && _container.UseZip64WhenSaving == Zip64Option.Always)
  2138. {
  2139. // original descriptor was 16 bytes, now we need 24
  2140. // signature + CRC
  2141. byte[] pad = new byte[4];
  2142. outstream.Write(Descriptor, 0, 8);
  2143. // Compressed
  2144. outstream.Write(Descriptor, 8, 4);
  2145. outstream.Write(pad, 0, 4);
  2146. // UnCompressed
  2147. outstream.Write(Descriptor, 12, 4);
  2148. outstream.Write(pad, 0, 4);
  2149. _LengthOfTrailer += 8;
  2150. }
  2151. else
  2152. {
  2153. // same descriptor on input and output. Copy it through.
  2154. outstream.Write(Descriptor, 0, size);
  2155. //_LengthOfTrailer += size;
  2156. }
  2157. }
  2158. }
  2159. _TotalEntrySize = _LengthOfHeader + _CompressedFileDataSize + _LengthOfTrailer;
  2160. }
  2161. private void CopyThroughWithNoChange(Stream outstream)
  2162. {
  2163. int n;
  2164. byte[] bytes = new byte[BufferSize];
  2165. var input = new CountingStream(this.ArchiveStream);
  2166. // seek to the beginning of the entry data in the input stream
  2167. input.Seek(this._RelativeOffsetOfLocalHeader, SeekOrigin.Begin);
  2168. if (this._TotalEntrySize == 0)
  2169. {
  2170. // We've never set the length of the entry.
  2171. // Set it here.
  2172. this._TotalEntrySize = this._LengthOfHeader + this._CompressedFileDataSize + _LengthOfTrailer;
  2173. // The CompressedSize includes all the leading metadata associated
  2174. // to encryption, if any, as well as the compressed data, or
  2175. // compressed-then-encrypted data, and the trailer in case of AES.
  2176. // The CompressedFileData size is the same, less the encryption
  2177. // framing data (12 bytes header for PKZip; 10/18 bytes header and
  2178. // 10 byte trailer for AES).
  2179. // The _LengthOfHeader includes all the zip entry header plus the
  2180. // crypto header, if any. The _LengthOfTrailer includes the
  2181. // 10-byte MAC for AES, where appropriate, and the bit-3
  2182. // Descriptor, where applicable.
  2183. }
  2184. // workitem 5616
  2185. // remember the offset, within the output stream, of this particular entry header.
  2186. // This may have changed if any of the other entries changed (eg, if a different
  2187. // entry was removed or added.)
  2188. var counter = outstream as CountingStream;
  2189. _RelativeOffsetOfLocalHeader = (counter != null)
  2190. ? counter.ComputedPosition
  2191. : outstream.Position; // BytesWritten
  2192. // copy through the header, filedata, trailer, everything...
  2193. long remaining = this._TotalEntrySize;
  2194. while (remaining > 0)
  2195. {
  2196. int len = (remaining > bytes.Length) ? bytes.Length : (int)remaining;
  2197. // read
  2198. n = input.Read(bytes, 0, len);
  2199. _CheckRead(n);
  2200. // write
  2201. outstream.Write(bytes, 0, n);
  2202. remaining -= n;
  2203. OnWriteBlock(input.BytesRead, this._TotalEntrySize);
  2204. if (_ioOperationCanceled)
  2205. break;
  2206. }
  2207. }
  2208. [System.Diagnostics.ConditionalAttribute("Trace")]
  2209. private void TraceWriteLine(string format, params object[] varParams)
  2210. {
  2211. lock (_outputLock)
  2212. {
  2213. int tid = System.Threading.Thread.CurrentThread.GetHashCode();
  2214. #if ! (NETCF || SILVERLIGHT || IOS || ANDROID)
  2215. Console.ForegroundColor = (ConsoleColor)(tid % 8 + 8);
  2216. #endif
  2217. Console.Write("{0:000} ZipEntry.Write ", tid);
  2218. Console.WriteLine(format, varParams);
  2219. #if ! (NETCF || SILVERLIGHT || IOS || ANDROID)
  2220. Console.ResetColor();
  2221. #endif
  2222. }
  2223. }
  2224. private object _outputLock = new Object();
  2225. }
  2226. }