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