1 // XarHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../C/CpuArch.h"
6 
7 #include "../../Common/ComTry.h"
8 #include "../../Common/MyLinux.h"
9 #include "../../Common/MyXml.h"
10 #include "../../Common/StringToInt.h"
11 #include "../../Common/UTFConvert.h"
12 
13 #include "../../Windows/PropVariant.h"
14 #include "../../Windows/TimeUtils.h"
15 
16 #include "../Common/LimitedStreams.h"
17 #include "../Common/ProgressUtils.h"
18 #include "../Common/RegisterArc.h"
19 #include "../Common/StreamObjects.h"
20 #include "../Common/StreamUtils.h"
21 
22 #include "../Compress/BZip2Decoder.h"
23 #include "../Compress/CopyCoder.h"
24 #include "../Compress/ZlibDecoder.h"
25 
26 #include "Common/OutStreamWithSha1.h"
27 
28 using namespace NWindows;
29 
30 #define XAR_SHOW_RAW
31 
32 #define Get16(p) GetBe16(p)
33 #define Get32(p) GetBe32(p)
34 #define Get64(p) GetBe64(p)
35 
36 namespace NArchive {
37 namespace NXar {
38 
39 static const size_t kXmlSizeMax = ((size_t )1 << 30) - (1 << 14);
40 static const size_t kXmlPackSizeMax = kXmlSizeMax;
41 
42 /*
43 #define XAR_CKSUM_NONE  0
44 #define XAR_CKSUM_SHA1  1
45 #define XAR_CKSUM_MD5   2
46 
47 static const char * const k_ChecksumAlgos[] =
48 {
49     "None"
50   , "SHA-1"
51   , "MD5"
52 };
53 */
54 
55 #define METHOD_NAME_ZLIB "zlib"
56 
57 
58 struct CFile
59 {
60   AString Name;
61   AString Method;
62   UInt64 Size;
63   UInt64 PackSize;
64   UInt64 Offset;
65 
66   UInt64 CTime;
67   UInt64 MTime;
68   UInt64 ATime;
69   UInt32 Mode;
70 
71   AString User;
72   AString Group;
73 
74   bool IsDir;
75   bool HasData;
76   bool ModeDefined;
77   bool Sha1IsDefined;
78   // bool packSha1IsDefined;
79 
80   Byte Sha1[SHA1_DIGEST_SIZE];
81   // Byte packSha1[SHA1_DIGEST_SIZE];
82 
83   int Parent;
84 
CFileNArchive::NXar::CFile85   CFile():
86       Size(0), PackSize(0), Offset(0),
87       CTime(0), MTime(0), ATime(0), Mode(0),
88       IsDir(false), HasData(false), ModeDefined(false), Sha1IsDefined(false),
89       /* packSha1IsDefined(false), */
90       Parent(-1)
91       {}
92 
IsCopyMethodNArchive::NXar::CFile93   bool IsCopyMethod() const
94   {
95     return Method.IsEmpty() || Method == "octet-stream";
96   }
97 
UpdateTotalPackSizeNArchive::NXar::CFile98   void UpdateTotalPackSize(UInt64 &totalSize) const
99   {
100     UInt64 t = Offset + PackSize;
101     if (totalSize < t)
102       totalSize = t;
103   }
104 };
105 
106 class CHandler:
107   public IInArchive,
108   public IInArchiveGetStream,
109   public CMyUnknownImp
110 {
111   UInt64 _dataStartPos;
112   CMyComPtr<IInStream> _inStream;
113   CByteArr _xml;
114   size_t _xmlLen;
115   CObjectVector<CFile> _files;
116   // UInt32 _checkSumAlgo;
117   UInt64 _phySize;
118   Int32 _mainSubfile;
119   bool _is_pkg;
120 
121   HRESULT Open2(IInStream *stream);
122   HRESULT Extract(IInStream *stream);
123 public:
124   MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream)
125   INTERFACE_IInArchive(;)
126   STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
127 };
128 
129 static const Byte kArcProps[] =
130 {
131   kpidSubType,
132   kpidHeadersSize
133 };
134 
135 static const Byte kProps[] =
136 {
137   kpidPath,
138   kpidSize,
139   kpidPackSize,
140   kpidMTime,
141   kpidCTime,
142   kpidATime,
143   kpidPosixAttrib,
144   kpidUser,
145   kpidGroup,
146   kpidMethod
147 };
148 
149 IMP_IInArchive_Props
150 IMP_IInArchive_ArcProps
151 
152 #define PARSE_NUM(_num_, _dest_) \
153     { const char *end; _dest_ = ConvertStringToUInt32(p, &end); \
154     if ((unsigned)(end - p) != _num_) return 0; \
155     p += _num_ + 1; }
156 
ParseUInt64(const CXmlItem & item,const char * name,UInt64 & res)157 static bool ParseUInt64(const CXmlItem &item, const char *name, UInt64 &res)
158 {
159   const AString s (item.GetSubStringForTag(name));
160   if (s.IsEmpty())
161     return false;
162   const char *end;
163   res = ConvertStringToUInt64(s, &end);
164   return *end == 0;
165 }
166 
ParseTime(const CXmlItem & item,const char * name)167 static UInt64 ParseTime(const CXmlItem &item, const char *name)
168 {
169   const AString s (item.GetSubStringForTag(name));
170   if (s.Len() < 20)
171     return 0;
172   const char *p = s;
173   if (p[ 4] != '-' || p[ 7] != '-' || p[10] != 'T' ||
174       p[13] != ':' || p[16] != ':' || p[19] != 'Z')
175     return 0;
176   UInt32 year, month, day, hour, min, sec;
177   PARSE_NUM(4, year)
178   PARSE_NUM(2, month)
179   PARSE_NUM(2, day)
180   PARSE_NUM(2, hour)
181   PARSE_NUM(2, min)
182   PARSE_NUM(2, sec)
183 
184   UInt64 numSecs;
185   if (!NTime::GetSecondsSince1601(year, month, day, hour, min, sec, numSecs))
186     return 0;
187   return numSecs * 10000000;
188 }
189 
HexToByte(unsigned char c)190 static int HexToByte(unsigned char c)
191 {
192   if (c >= '0' && c <= '9') return c - '0';
193   if (c >= 'A' && c <= 'F') return c - 'A' + 10;
194   if (c >= 'a' && c <= 'f') return c - 'a' + 10;
195   return -1;
196 }
197 
ParseSha1(const CXmlItem & item,const char * name,Byte * digest)198 static bool ParseSha1(const CXmlItem &item, const char *name, Byte *digest)
199 {
200   int index = item.FindSubTag(name);
201   if (index < 0)
202     return false;
203   const CXmlItem &checkItem = item.SubItems[index];
204   const AString style (checkItem.GetPropVal("style"));
205   if (style == "SHA1")
206   {
207     const AString s (checkItem.GetSubString());
208     if (s.Len() != SHA1_DIGEST_SIZE * 2)
209       return false;
210     for (unsigned i = 0; i < s.Len(); i += 2)
211     {
212       int b0 = HexToByte(s[i]);
213       int b1 = HexToByte(s[i + 1]);
214       if (b0 < 0 || b1 < 0)
215         return false;
216       digest[i / 2] = (Byte)((b0 << 4) | b1);
217     }
218     return true;
219   }
220   return false;
221 }
222 
AddItem(const CXmlItem & item,CObjectVector<CFile> & files,int parent)223 static bool AddItem(const CXmlItem &item, CObjectVector<CFile> &files, int parent)
224 {
225   if (!item.IsTag)
226     return true;
227   if (item.Name == "file")
228   {
229     CFile file;
230     file.Parent = parent;
231     parent = files.Size();
232     file.Name = item.GetSubStringForTag("name");
233     const AString type (item.GetSubStringForTag("type"));
234     if (type == "directory")
235       file.IsDir = true;
236     else if (type == "file")
237       file.IsDir = false;
238     else
239       return false;
240 
241     int dataIndex = item.FindSubTag("data");
242     if (dataIndex >= 0 && !file.IsDir)
243     {
244       file.HasData = true;
245       const CXmlItem &dataItem = item.SubItems[dataIndex];
246       if (!ParseUInt64(dataItem, "size", file.Size))
247         return false;
248       if (!ParseUInt64(dataItem, "length", file.PackSize))
249         return false;
250       if (!ParseUInt64(dataItem, "offset", file.Offset))
251         return false;
252       file.Sha1IsDefined = ParseSha1(dataItem, "extracted-checksum", file.Sha1);
253       // file.packSha1IsDefined = ParseSha1(dataItem, "archived-checksum",  file.packSha1);
254       int encodingIndex = dataItem.FindSubTag("encoding");
255       if (encodingIndex >= 0)
256       {
257         const CXmlItem &encodingItem = dataItem.SubItems[encodingIndex];
258         if (encodingItem.IsTag)
259         {
260           AString s (encodingItem.GetPropVal("style"));
261           if (!s.IsEmpty())
262           {
263             const AString appl ("application/");
264             if (s.IsPrefixedBy(appl))
265             {
266               s.DeleteFrontal(appl.Len());
267               const AString xx ("x-");
268               if (s.IsPrefixedBy(xx))
269               {
270                 s.DeleteFrontal(xx.Len());
271                 if (s == "gzip")
272                   s = METHOD_NAME_ZLIB;
273               }
274             }
275             file.Method = s;
276           }
277         }
278       }
279     }
280 
281     file.CTime = ParseTime(item, "ctime");
282     file.MTime = ParseTime(item, "mtime");
283     file.ATime = ParseTime(item, "atime");
284 
285     {
286       const AString s (item.GetSubStringForTag("mode"));
287       if (s[0] == '0')
288       {
289         const char *end;
290         file.Mode = ConvertOctStringToUInt32(s, &end);
291         file.ModeDefined = (*end == 0);
292       }
293     }
294 
295     file.User = item.GetSubStringForTag("user");
296     file.Group = item.GetSubStringForTag("group");
297 
298     files.Add(file);
299   }
300   FOR_VECTOR (i, item.SubItems)
301     if (!AddItem(item.SubItems[i], files, parent))
302       return false;
303   return true;
304 }
305 
Open2(IInStream * stream)306 HRESULT CHandler::Open2(IInStream *stream)
307 {
308   const UInt32 kHeaderSize = 0x1C;
309   Byte buf[kHeaderSize];
310   RINOK(ReadStream_FALSE(stream, buf, kHeaderSize));
311 
312   UInt32 size = Get16(buf + 4);
313   // UInt32 ver = Get16(buf + 6); // == 1
314   if (Get32(buf) != 0x78617221 || size != kHeaderSize)
315     return S_FALSE;
316 
317   UInt64 packSize = Get64(buf + 8);
318   UInt64 unpackSize = Get64(buf + 0x10);
319 
320   // _checkSumAlgo = Get32(buf + 0x18);
321 
322   if (packSize >= kXmlPackSizeMax ||
323       unpackSize >= kXmlSizeMax)
324     return S_FALSE;
325 
326   _dataStartPos = kHeaderSize + packSize;
327   _phySize = _dataStartPos;
328 
329   _xml.Alloc((size_t)unpackSize + 1);
330   _xmlLen = (size_t)unpackSize;
331 
332   NCompress::NZlib::CDecoder *zlibCoderSpec = new NCompress::NZlib::CDecoder();
333   CMyComPtr<ICompressCoder> zlibCoder = zlibCoderSpec;
334 
335   CLimitedSequentialInStream *inStreamLimSpec = new CLimitedSequentialInStream;
336   CMyComPtr<ISequentialInStream> inStreamLim(inStreamLimSpec);
337   inStreamLimSpec->SetStream(stream);
338   inStreamLimSpec->Init(packSize);
339 
340   CBufPtrSeqOutStream *outStreamLimSpec = new CBufPtrSeqOutStream;
341   CMyComPtr<ISequentialOutStream> outStreamLim(outStreamLimSpec);
342   outStreamLimSpec->Init(_xml, (size_t)unpackSize);
343 
344   RINOK(zlibCoder->Code(inStreamLim, outStreamLim, NULL, NULL, NULL));
345 
346   if (outStreamLimSpec->GetPos() != (size_t)unpackSize)
347     return S_FALSE;
348 
349   _xml[(size_t)unpackSize] = 0;
350   if (strlen((const char *)(const Byte *)_xml) != unpackSize) return S_FALSE;
351 
352   CXml xml;
353   if (!xml.Parse((const char *)(const Byte *)_xml))
354     return S_FALSE;
355 
356   if (!xml.Root.IsTagged("xar") || xml.Root.SubItems.Size() != 1)
357     return S_FALSE;
358   const CXmlItem &toc = xml.Root.SubItems[0];
359   if (!toc.IsTagged("toc"))
360     return S_FALSE;
361   if (!AddItem(toc, _files, -1))
362     return S_FALSE;
363 
364   UInt64 totalPackSize = 0;
365   unsigned numMainFiles = 0;
366 
367   FOR_VECTOR (i, _files)
368   {
369     const CFile &file = _files[i];
370     file.UpdateTotalPackSize(totalPackSize);
371     if (file.Name == "Payload" || file.Name == "Content")
372     {
373       _mainSubfile = i;
374       numMainFiles++;
375     }
376     else if (file.Name == "PackageInfo")
377       _is_pkg = true;
378   }
379 
380   if (numMainFiles > 1)
381     _mainSubfile = -1;
382 
383   _phySize = _dataStartPos + totalPackSize;
384 
385   return S_OK;
386 }
387 
Open(IInStream * stream,const UInt64 *,IArchiveOpenCallback *)388 STDMETHODIMP CHandler::Open(IInStream *stream,
389     const UInt64 * /* maxCheckStartPosition */,
390     IArchiveOpenCallback * /* openArchiveCallback */)
391 {
392   COM_TRY_BEGIN
393   {
394     Close();
395     if (Open2(stream) != S_OK)
396       return S_FALSE;
397     _inStream = stream;
398   }
399   return S_OK;
400   COM_TRY_END
401 }
402 
Close()403 STDMETHODIMP CHandler::Close()
404 {
405   _phySize = 0;
406   _inStream.Release();
407   _files.Clear();
408   _xmlLen = 0;
409   _xml.Free();
410   _mainSubfile = -1;
411   _is_pkg = false;
412   return S_OK;
413 }
414 
GetNumberOfItems(UInt32 * numItems)415 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
416 {
417   *numItems = _files.Size()
418     #ifdef XAR_SHOW_RAW
419     + 1
420     #endif
421   ;
422   return S_OK;
423 }
424 
TimeToProp(UInt64 t,NCOM::CPropVariant & prop)425 static void TimeToProp(UInt64 t, NCOM::CPropVariant &prop)
426 {
427   if (t != 0)
428   {
429     FILETIME ft;
430     ft.dwLowDateTime = (UInt32)(t);
431     ft.dwHighDateTime = (UInt32)(t >> 32);
432     prop = ft;
433   }
434 }
435 
Utf8StringToProp(const AString & s,NCOM::CPropVariant & prop)436 static void Utf8StringToProp(const AString &s, NCOM::CPropVariant &prop)
437 {
438   if (!s.IsEmpty())
439   {
440     UString us;
441     ConvertUTF8ToUnicode(s, us);
442     prop = us;
443   }
444 }
445 
GetArchiveProperty(PROPID propID,PROPVARIANT * value)446 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
447 {
448   COM_TRY_BEGIN
449   NCOM::CPropVariant prop;
450   switch (propID)
451   {
452     case kpidHeadersSize: prop = _dataStartPos; break;
453     case kpidPhySize: prop = _phySize; break;
454     case kpidMainSubfile: if (_mainSubfile >= 0) prop = (UInt32)_mainSubfile; break;
455     case kpidSubType: if (_is_pkg) prop = "pkg"; break;
456     case kpidExtension: prop = _is_pkg ? "pkg" : "xar"; break;
457   }
458   prop.Detach(value);
459   return S_OK;
460   COM_TRY_END
461 }
462 
GetProperty(UInt32 index,PROPID propID,PROPVARIANT * value)463 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
464 {
465   COM_TRY_BEGIN
466   NCOM::CPropVariant prop;
467 
468   #ifdef XAR_SHOW_RAW
469   if (index == _files.Size())
470   {
471     switch (propID)
472     {
473       case kpidPath: prop = "[TOC].xml"; break;
474       case kpidSize:
475       case kpidPackSize: prop = (UInt64)_xmlLen; break;
476     }
477   }
478   else
479   #endif
480   {
481     const CFile &item = _files[index];
482     switch (propID)
483     {
484       case kpidMethod: Utf8StringToProp(item.Method, prop); break;
485 
486       case kpidPath:
487       {
488         AString path;
489         int cur = index;
490         do
491         {
492           const CFile &item2 = _files[cur];
493           if (!path.IsEmpty())
494             path.InsertAtFront(CHAR_PATH_SEPARATOR);
495           if (item2.Name.IsEmpty())
496             path.Insert(0, "unknown");
497           else
498             path.Insert(0, item2.Name);
499           cur = item2.Parent;
500         }
501         while (cur >= 0);
502 
503         Utf8StringToProp(path, prop);
504         break;
505       }
506 
507       case kpidIsDir: prop = item.IsDir; break;
508       case kpidSize: if (!item.IsDir) prop = item.Size; break;
509       case kpidPackSize: if (!item.IsDir) prop = item.PackSize; break;
510 
511       case kpidMTime: TimeToProp(item.MTime, prop); break;
512       case kpidCTime: TimeToProp(item.CTime, prop); break;
513       case kpidATime: TimeToProp(item.ATime, prop); break;
514       case kpidPosixAttrib:
515         if (item.ModeDefined)
516         {
517           UInt32 mode = item.Mode;
518           if ((mode & MY_LIN_S_IFMT) == 0)
519             mode |= (item.IsDir ? MY_LIN_S_IFDIR : MY_LIN_S_IFREG);
520           prop = mode;
521         }
522         break;
523       case kpidUser: Utf8StringToProp(item.User, prop); break;
524       case kpidGroup: Utf8StringToProp(item.Group, prop); break;
525     }
526   }
527   prop.Detach(value);
528   return S_OK;
529   COM_TRY_END
530 }
531 
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)532 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
533     Int32 testMode, IArchiveExtractCallback *extractCallback)
534 {
535   COM_TRY_BEGIN
536   bool allFilesMode = (numItems == (UInt32)(Int32)-1);
537   if (allFilesMode)
538     numItems = _files.Size();
539   if (numItems == 0)
540     return S_OK;
541   UInt64 totalSize = 0;
542   UInt32 i;
543   for (i = 0; i < numItems; i++)
544   {
545     UInt32 index = (allFilesMode ? i : indices[i]);
546     #ifdef XAR_SHOW_RAW
547     if (index == _files.Size())
548       totalSize += _xmlLen;
549     else
550     #endif
551       totalSize += _files[index].Size;
552   }
553   extractCallback->SetTotal(totalSize);
554 
555   UInt64 currentPackTotal = 0;
556   UInt64 currentUnpTotal = 0;
557   UInt64 currentPackSize = 0;
558   UInt64 currentUnpSize = 0;
559 
560   const UInt32 kZeroBufSize = (1 << 14);
561   CByteBuffer zeroBuf(kZeroBufSize);
562   memset(zeroBuf, 0, kZeroBufSize);
563 
564   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
565   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
566 
567   NCompress::NZlib::CDecoder *zlibCoderSpec = new NCompress::NZlib::CDecoder();
568   CMyComPtr<ICompressCoder> zlibCoder = zlibCoderSpec;
569 
570   NCompress::NBZip2::CDecoder *bzip2CoderSpec = new NCompress::NBZip2::CDecoder();
571   CMyComPtr<ICompressCoder> bzip2Coder = bzip2CoderSpec;
572 
573   NCompress::NDeflate::NDecoder::CCOMCoder *deflateCoderSpec = new NCompress::NDeflate::NDecoder::CCOMCoder();
574   CMyComPtr<ICompressCoder> deflateCoder = deflateCoderSpec;
575 
576   CLocalProgress *lps = new CLocalProgress;
577   CMyComPtr<ICompressProgressInfo> progress = lps;
578   lps->Init(extractCallback, false);
579 
580   CLimitedSequentialInStream *inStreamSpec = new CLimitedSequentialInStream;
581   CMyComPtr<ISequentialInStream> inStream(inStreamSpec);
582   inStreamSpec->SetStream(_inStream);
583 
584 
585   CLimitedSequentialOutStream *outStreamLimSpec = new CLimitedSequentialOutStream;
586   CMyComPtr<ISequentialOutStream> outStream(outStreamLimSpec);
587 
588   COutStreamWithSha1 *outStreamSha1Spec = new COutStreamWithSha1;
589   {
590     CMyComPtr<ISequentialOutStream> outStreamSha1(outStreamSha1Spec);
591     outStreamLimSpec->SetStream(outStreamSha1);
592   }
593 
594   for (i = 0; i < numItems; i++, currentPackTotal += currentPackSize, currentUnpTotal += currentUnpSize)
595   {
596     lps->InSize = currentPackTotal;
597     lps->OutSize = currentUnpTotal;
598     currentPackSize = 0;
599     currentUnpSize = 0;
600     RINOK(lps->SetCur());
601     CMyComPtr<ISequentialOutStream> realOutStream;
602     Int32 askMode = testMode ?
603         NExtract::NAskMode::kTest :
604         NExtract::NAskMode::kExtract;
605     UInt32 index = allFilesMode ? i : indices[i];
606     RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
607 
608     if (index < _files.Size())
609     {
610       const CFile &item = _files[index];
611       if (item.IsDir)
612       {
613         RINOK(extractCallback->PrepareOperation(askMode));
614         RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
615         continue;
616       }
617     }
618 
619     if (!testMode && !realOutStream)
620       continue;
621     RINOK(extractCallback->PrepareOperation(askMode));
622 
623     outStreamSha1Spec->SetStream(realOutStream);
624     realOutStream.Release();
625 
626     Int32 opRes = NExtract::NOperationResult::kOK;
627     #ifdef XAR_SHOW_RAW
628     if (index == _files.Size())
629     {
630       outStreamSha1Spec->Init(false);
631       outStreamLimSpec->Init(_xmlLen);
632       RINOK(WriteStream(outStream, _xml, _xmlLen));
633       currentPackSize = currentUnpSize = _xmlLen;
634     }
635     else
636     #endif
637     {
638       const CFile &item = _files[index];
639       if (item.HasData)
640       {
641         currentPackSize = item.PackSize;
642         currentUnpSize = item.Size;
643 
644         RINOK(_inStream->Seek(_dataStartPos + item.Offset, STREAM_SEEK_SET, NULL));
645         inStreamSpec->Init(item.PackSize);
646         outStreamSha1Spec->Init(item.Sha1IsDefined);
647         outStreamLimSpec->Init(item.Size);
648         HRESULT res = S_OK;
649 
650         ICompressCoder *coder = NULL;
651         if (item.IsCopyMethod())
652           if (item.PackSize == item.Size)
653             coder = copyCoder;
654           else
655             opRes = NExtract::NOperationResult::kUnsupportedMethod;
656         else if (item.Method == METHOD_NAME_ZLIB)
657           coder = zlibCoder;
658         else if (item.Method == "bzip2")
659           coder = bzip2Coder;
660         else
661           opRes = NExtract::NOperationResult::kUnsupportedMethod;
662 
663         if (coder)
664           res = coder->Code(inStream, outStream, NULL, NULL, progress);
665 
666         if (res != S_OK)
667         {
668           if (!outStreamLimSpec->IsFinishedOK())
669             opRes = NExtract::NOperationResult::kDataError;
670           else if (res != S_FALSE)
671             return res;
672           if (opRes == NExtract::NOperationResult::kOK)
673             opRes = NExtract::NOperationResult::kDataError;
674         }
675 
676         if (opRes == NExtract::NOperationResult::kOK)
677         {
678           if (outStreamLimSpec->IsFinishedOK() &&
679               outStreamSha1Spec->GetSize() == item.Size)
680           {
681             if (!outStreamLimSpec->IsFinishedOK())
682             {
683               opRes = NExtract::NOperationResult::kDataError;
684             }
685             else if (item.Sha1IsDefined)
686             {
687               Byte digest[SHA1_DIGEST_SIZE];
688               outStreamSha1Spec->Final(digest);
689               if (memcmp(digest, item.Sha1, SHA1_DIGEST_SIZE) != 0)
690                 opRes = NExtract::NOperationResult::kCRCError;
691             }
692           }
693           else
694             opRes = NExtract::NOperationResult::kDataError;
695         }
696       }
697     }
698     outStreamSha1Spec->ReleaseStream();
699     RINOK(extractCallback->SetOperationResult(opRes));
700   }
701   return S_OK;
702   COM_TRY_END
703 }
704 
GetStream(UInt32 index,ISequentialInStream ** stream)705 STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
706 {
707   *stream = NULL;
708   COM_TRY_BEGIN
709   #ifdef XAR_SHOW_RAW
710   if (index == _files.Size())
711   {
712     Create_BufInStream_WithNewBuffer(_xml, _xmlLen, stream);
713     return S_OK;
714   }
715   else
716   #endif
717   {
718     const CFile &item = _files[index];
719     if (item.HasData && item.IsCopyMethod() && item.PackSize == item.Size)
720       return CreateLimitedInStream(_inStream, _dataStartPos + item.Offset, item.Size, stream);
721   }
722   return S_FALSE;
723   COM_TRY_END
724 }
725 
726 static const Byte k_Signature[] = { 'x', 'a', 'r', '!', 0, 0x1C };
727 
728 REGISTER_ARC_I(
729   "Xar", "xar pkg xip", 0, 0xE1,
730   k_Signature,
731   0,
732   0,
733   NULL)
734 
735 }}
736