1 // ComHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../C/Alloc.h"
6 #include "../../../C/CpuArch.h"
7 
8 #include "../../Common/IntToString.h"
9 #include "../../Common/ComTry.h"
10 #include "../../Common/MyCom.h"
11 #include "../../Common/MyBuffer.h"
12 #include "../../Common/MyString.h"
13 
14 #include "../../Windows/PropVariant.h"
15 
16 #include "../Common/LimitedStreams.h"
17 #include "../Common/ProgressUtils.h"
18 #include "../Common/RegisterArc.h"
19 #include "../Common/StreamUtils.h"
20 
21 #include "../Compress/CopyCoder.h"
22 
23 #define Get16(p) GetUi16(p)
24 #define Get32(p) GetUi32(p)
25 
26 namespace NArchive {
27 namespace NCom {
28 
29 #define SIGNATURE { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 }
30 static const Byte kSignature[] = SIGNATURE;
31 
32 enum EType
33 {
34   k_Type_Common,
35   k_Type_Msi,
36   k_Type_Msp,
37   k_Type_Doc,
38   k_Type_Ppt,
39   k_Type_Xls
40 };
41 
42 static const char * const kExtensions[] =
43 {
44     "compound"
45   , "msi"
46   , "msp"
47   , "doc"
48   , "ppt"
49   , "xls"
50 };
51 
52 namespace NFatID
53 {
54   // static const UInt32 kFree       = 0xFFFFFFFF;
55   static const UInt32 kEndOfChain = 0xFFFFFFFE;
56   // static const UInt32 kFatSector  = 0xFFFFFFFD;
57   // static const UInt32 kMatSector  = 0xFFFFFFFC;
58   static const UInt32 kMaxValue   = 0xFFFFFFFA;
59 }
60 
61 namespace NItemType
62 {
63   static const Byte kEmpty = 0;
64   static const Byte kStorage = 1;
65   // static const Byte kStream = 2;
66   // static const Byte kLockBytes = 3;
67   // static const Byte kProperty = 4;
68   static const Byte kRootStorage = 5;
69 }
70 
71 static const UInt32 kNameSizeMax = 64;
72 
73 struct CItem
74 {
75   Byte Name[kNameSizeMax];
76   // UInt16 NameSize;
77   // UInt32 Flags;
78   FILETIME CTime;
79   FILETIME MTime;
80   UInt64 Size;
81   UInt32 LeftDid;
82   UInt32 RightDid;
83   UInt32 SonDid;
84   UInt32 Sid;
85   Byte Type;
86 
IsEmptyNArchive::NCom::CItem87   bool IsEmpty() const { return Type == NItemType::kEmpty; }
IsDirNArchive::NCom::CItem88   bool IsDir() const { return Type == NItemType::kStorage || Type == NItemType::kRootStorage; }
89 
90   void Parse(const Byte *p, bool mode64bit);
91 };
92 
93 struct CRef
94 {
95   int Parent;
96   UInt32 Did;
97 };
98 
99 class CDatabase
100 {
101   UInt32 NumSectorsInMiniStream;
102   CObjArray<UInt32> MiniSids;
103 
104   HRESULT AddNode(int parent, UInt32 did);
105 public:
106 
107   CObjArray<UInt32> Fat;
108   UInt32 FatSize;
109 
110   CObjArray<UInt32> Mat;
111   UInt32 MatSize;
112 
113   CObjectVector<CItem> Items;
114   CRecordVector<CRef> Refs;
115 
116   UInt32 LongStreamMinSize;
117   unsigned SectorSizeBits;
118   unsigned MiniSectorSizeBits;
119 
120   Int32 MainSubfile;
121 
122   UInt64 PhySize;
123   EType Type;
124 
IsNotArcType() const125   bool IsNotArcType() const
126   {
127     return
128       Type != k_Type_Msi &&
129       Type != k_Type_Msp;
130   }
131 
UpdatePhySize(UInt64 val)132   void UpdatePhySize(UInt64 val)
133   {
134     if (PhySize < val)
135       PhySize = val;
136   }
137   HRESULT ReadSector(IInStream *inStream, Byte *buf, unsigned sectorSizeBits, UInt32 sid);
138   HRESULT ReadIDs(IInStream *inStream, Byte *buf, unsigned sectorSizeBits, UInt32 sid, UInt32 *dest);
139 
140   HRESULT Update_PhySize_WithItem(unsigned index);
141 
142   void Clear();
IsLargeStream(UInt64 size) const143   bool IsLargeStream(UInt64 size) const { return size >= LongStreamMinSize; }
144   UString GetItemPath(UInt32 index) const;
145 
GetItemPackSize(UInt64 size) const146   UInt64 GetItemPackSize(UInt64 size) const
147   {
148     UInt64 mask = ((UInt64)1 << (IsLargeStream(size) ? SectorSizeBits : MiniSectorSizeBits)) - 1;
149     return (size + mask) & ~mask;
150   }
151 
GetMiniCluster(UInt32 sid,UInt64 & res) const152   bool GetMiniCluster(UInt32 sid, UInt64 &res) const
153   {
154     unsigned subBits = SectorSizeBits - MiniSectorSizeBits;
155     UInt32 fid = sid >> subBits;
156     if (fid >= NumSectorsInMiniStream)
157       return false;
158     res = (((UInt64)MiniSids[fid] + 1) << subBits) + (sid & ((1 << subBits) - 1));
159     return true;
160   }
161 
162   HRESULT Open(IInStream *inStream);
163 };
164 
165 
ReadSector(IInStream * inStream,Byte * buf,unsigned sectorSizeBits,UInt32 sid)166 HRESULT CDatabase::ReadSector(IInStream *inStream, Byte *buf, unsigned sectorSizeBits, UInt32 sid)
167 {
168   UpdatePhySize(((UInt64)sid + 2) << sectorSizeBits);
169   RINOK(inStream->Seek((((UInt64)sid + 1) << sectorSizeBits), STREAM_SEEK_SET, NULL));
170   return ReadStream_FALSE(inStream, buf, (size_t)1 << sectorSizeBits);
171 }
172 
ReadIDs(IInStream * inStream,Byte * buf,unsigned sectorSizeBits,UInt32 sid,UInt32 * dest)173 HRESULT CDatabase::ReadIDs(IInStream *inStream, Byte *buf, unsigned sectorSizeBits, UInt32 sid, UInt32 *dest)
174 {
175   RINOK(ReadSector(inStream, buf, sectorSizeBits, sid));
176   UInt32 sectorSize = (UInt32)1 << sectorSizeBits;
177   for (UInt32 t = 0; t < sectorSize; t += 4)
178     *dest++ = Get32(buf + t);
179   return S_OK;
180 }
181 
GetFileTimeFromMem(const Byte * p,FILETIME * ft)182 static void GetFileTimeFromMem(const Byte *p, FILETIME *ft)
183 {
184   ft->dwLowDateTime = Get32(p);
185   ft->dwHighDateTime = Get32(p + 4);
186 }
187 
Parse(const Byte * p,bool mode64bit)188 void CItem::Parse(const Byte *p, bool mode64bit)
189 {
190   memcpy(Name, p, kNameSizeMax);
191   // NameSize = Get16(p + 64);
192   Type = p[66];
193   LeftDid = Get32(p + 68);
194   RightDid = Get32(p + 72);
195   SonDid = Get32(p + 76);
196   // Flags = Get32(p + 96);
197   GetFileTimeFromMem(p + 100, &CTime);
198   GetFileTimeFromMem(p + 108, &MTime);
199   Sid = Get32(p + 116);
200   Size = Get32(p + 120);
201   if (mode64bit)
202     Size |= ((UInt64)Get32(p + 124) << 32);
203 }
204 
Clear()205 void CDatabase::Clear()
206 {
207   PhySize = 0;
208 
209   Fat.Free();
210   MiniSids.Free();
211   Mat.Free();
212   Items.Clear();
213   Refs.Clear();
214 }
215 
216 static const UInt32 kNoDid = 0xFFFFFFFF;
217 
AddNode(int parent,UInt32 did)218 HRESULT CDatabase::AddNode(int parent, UInt32 did)
219 {
220   if (did == kNoDid)
221     return S_OK;
222   if (did >= (UInt32)Items.Size())
223     return S_FALSE;
224   const CItem &item = Items[did];
225   if (item.IsEmpty())
226     return S_FALSE;
227   CRef ref;
228   ref.Parent = parent;
229   ref.Did = did;
230   int index = Refs.Add(ref);
231   if (Refs.Size() > Items.Size())
232     return S_FALSE;
233   RINOK(AddNode(parent, item.LeftDid));
234   RINOK(AddNode(parent, item.RightDid));
235   if (item.IsDir())
236   {
237     RINOK(AddNode(index, item.SonDid));
238   }
239   return S_OK;
240 }
241 
CompoundNameToFileName(const UString & s)242 static UString CompoundNameToFileName(const UString &s)
243 {
244   UString res;
245   for (unsigned i = 0; i < s.Len(); i++)
246   {
247     wchar_t c = s[i];
248     if (c < 0x20)
249     {
250       res += '[';
251       res.Add_UInt32(c);
252       res += ']';
253     }
254     else
255       res += c;
256   }
257   return res;
258 }
259 
260 static const char k_Msi_Chars[] =
261   "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._";
262 
263 // static const char * const k_Msi_ID = ""; // "{msi}";
264 static const char k_Msi_SpecChar = '!';
265 
266 static const unsigned k_Msi_NumBits = 6;
267 static const unsigned k_Msi_NumChars = 1 << k_Msi_NumBits;
268 static const unsigned k_Msi_CharMask = k_Msi_NumChars - 1;
269 static const unsigned k_Msi_StartUnicodeChar = 0x3800;
270 static const unsigned k_Msi_UnicodeRange = k_Msi_NumChars * (k_Msi_NumChars + 1);
271 
272 
IsMsiName(const Byte * p)273 static bool IsMsiName(const Byte *p)
274 {
275   UInt32 c = Get16(p);
276   return
277       c >= k_Msi_StartUnicodeChar &&
278       c <= k_Msi_StartUnicodeChar + k_Msi_UnicodeRange;
279 }
280 
AreEqualNames(const Byte * rawName,const char * asciiName)281 static bool AreEqualNames(const Byte *rawName, const char *asciiName)
282 {
283   for (unsigned i = 0; i < kNameSizeMax / 2; i++)
284   {
285     wchar_t c = Get16(rawName + i * 2);
286     wchar_t c2 = (Byte)asciiName[i];
287     if (c != c2)
288       return false;
289     if (c == 0)
290       return true;
291   }
292   return false;
293 }
294 
CompoundMsiNameToFileName(const UString & name,UString & res)295 static bool CompoundMsiNameToFileName(const UString &name, UString &res)
296 {
297   res.Empty();
298   for (unsigned i = 0; i < name.Len(); i++)
299   {
300     wchar_t c = name[i];
301     if (c < (wchar_t)k_Msi_StartUnicodeChar || c > (wchar_t)(k_Msi_StartUnicodeChar + k_Msi_UnicodeRange))
302       return false;
303     /*
304     if (i == 0)
305       res += k_Msi_ID;
306     */
307     c -= k_Msi_StartUnicodeChar;
308 
309     unsigned c0 = (unsigned)c & k_Msi_CharMask;
310     unsigned c1 = (unsigned)c >> k_Msi_NumBits;
311 
312     if (c1 <= k_Msi_NumChars)
313     {
314       res += k_Msi_Chars[c0];
315       if (c1 == k_Msi_NumChars)
316         break;
317       res += k_Msi_Chars[c1];
318     }
319     else
320       res += k_Msi_SpecChar;
321   }
322   return true;
323 }
324 
ConvertName(const Byte * p,bool & isMsi)325 static UString ConvertName(const Byte *p, bool &isMsi)
326 {
327   isMsi = false;
328   UString s;
329 
330   for (unsigned i = 0; i < kNameSizeMax; i += 2)
331   {
332     wchar_t c = Get16(p + i);
333     if (c == 0)
334       break;
335     s += c;
336   }
337 
338   UString msiName;
339   if (CompoundMsiNameToFileName(s, msiName))
340   {
341     isMsi = true;
342     return msiName;
343   }
344   return CompoundNameToFileName(s);
345 }
346 
ConvertName(const Byte * p)347 static UString ConvertName(const Byte *p)
348 {
349   bool isMsi;
350   return ConvertName(p, isMsi);
351 }
352 
GetItemPath(UInt32 index) const353 UString CDatabase::GetItemPath(UInt32 index) const
354 {
355   UString s;
356   while (index != kNoDid)
357   {
358     const CRef &ref = Refs[index];
359     const CItem &item = Items[ref.Did];
360     if (!s.IsEmpty())
361       s.InsertAtFront(WCHAR_PATH_SEPARATOR);
362     s.Insert(0, ConvertName(item.Name));
363     index = ref.Parent;
364   }
365   return s;
366 }
367 
Update_PhySize_WithItem(unsigned index)368 HRESULT CDatabase::Update_PhySize_WithItem(unsigned index)
369 {
370   const CItem &item = Items[index];
371   bool isLargeStream = (index == 0 || IsLargeStream(item.Size));
372   if (!isLargeStream)
373     return S_OK;
374   unsigned bsLog = isLargeStream ? SectorSizeBits : MiniSectorSizeBits;
375   // streamSpec->Size = item.Size;
376 
377   UInt32 clusterSize = (UInt32)1 << bsLog;
378   UInt64 numClusters64 = (item.Size + clusterSize - 1) >> bsLog;
379   if (numClusters64 >= ((UInt32)1 << 31))
380     return S_FALSE;
381   UInt32 sid = item.Sid;
382   UInt64 size = item.Size;
383 
384   if (size != 0)
385   {
386     for (;; size -= clusterSize)
387     {
388       // if (isLargeStream)
389       {
390         if (sid >= FatSize)
391           return S_FALSE;
392         UpdatePhySize(((UInt64)sid + 2) << bsLog);
393         sid = Fat[sid];
394       }
395       if (size <= clusterSize)
396         break;
397     }
398   }
399   if (sid != NFatID::kEndOfChain)
400     return S_FALSE;
401   return S_OK;
402 }
403 
404 // There is name "[!]MsiPatchSequence" in msp files
405 static const unsigned kMspSequence_Size = 18;
406 static const Byte kMspSequence[kMspSequence_Size] =
407   { 0x40, 0x48, 0x96, 0x45, 0x6C, 0x3E, 0xE4, 0x45,
408     0xE6, 0x42, 0x16, 0x42, 0x37, 0x41, 0x27, 0x41,
409     0x37, 0x41 };
410 
Open(IInStream * inStream)411 HRESULT CDatabase::Open(IInStream *inStream)
412 {
413   MainSubfile = -1;
414   Type = k_Type_Common;
415   const UInt32 kHeaderSize = 512;
416   Byte p[kHeaderSize];
417   PhySize = kHeaderSize;
418   RINOK(ReadStream_FALSE(inStream, p, kHeaderSize));
419   if (memcmp(p, kSignature, ARRAY_SIZE(kSignature)) != 0)
420     return S_FALSE;
421   if (Get16(p + 0x1A) > 4) // majorVer
422     return S_FALSE;
423   if (Get16(p + 0x1C) != 0xFFFE) // Little-endian
424     return S_FALSE;
425   unsigned sectorSizeBits = Get16(p + 0x1E);
426   bool mode64bit = (sectorSizeBits >= 12);
427   unsigned miniSectorSizeBits = Get16(p + 0x20);
428   SectorSizeBits = sectorSizeBits;
429   MiniSectorSizeBits = miniSectorSizeBits;
430 
431   if (sectorSizeBits > 24 ||
432       sectorSizeBits < 7 ||
433       miniSectorSizeBits > 24 ||
434       miniSectorSizeBits < 2 ||
435       miniSectorSizeBits > sectorSizeBits)
436     return S_FALSE;
437   UInt32 numSectorsForFAT = Get32(p + 0x2C); // SAT
438   LongStreamMinSize = Get32(p + 0x38);
439 
440   UInt32 sectSize = (UInt32)1 << sectorSizeBits;
441 
442   CByteBuffer sect(sectSize);
443 
444   unsigned ssb2 = sectorSizeBits - 2;
445   UInt32 numSidsInSec = (UInt32)1 << ssb2;
446   UInt32 numFatItems = numSectorsForFAT << ssb2;
447   if ((numFatItems >> ssb2) != numSectorsForFAT)
448     return S_FALSE;
449   FatSize = numFatItems;
450 
451   {
452     UInt32 numSectorsForBat = Get32(p + 0x48); // master sector allocation table
453     const UInt32 kNumHeaderBatItems = 109;
454     UInt32 numBatItems = kNumHeaderBatItems + (numSectorsForBat << ssb2);
455     if (numBatItems < kNumHeaderBatItems || ((numBatItems - kNumHeaderBatItems) >> ssb2) != numSectorsForBat)
456       return S_FALSE;
457     CObjArray<UInt32> bat(numBatItems);
458     UInt32 i;
459     for (i = 0; i < kNumHeaderBatItems; i++)
460       bat[i] = Get32(p + 0x4c + i * 4);
461     UInt32 sid = Get32(p + 0x44);
462     for (UInt32 s = 0; s < numSectorsForBat; s++)
463     {
464       RINOK(ReadIDs(inStream, sect, sectorSizeBits, sid, bat + i));
465       i += numSidsInSec - 1;
466       sid = bat[i];
467     }
468     numBatItems = i;
469 
470     Fat.Alloc(numFatItems);
471     UInt32 j = 0;
472 
473     for (i = 0; i < numFatItems; j++, i += numSidsInSec)
474     {
475       if (j >= numBatItems)
476         return S_FALSE;
477       RINOK(ReadIDs(inStream, sect, sectorSizeBits, bat[j], Fat + i));
478     }
479     FatSize = numFatItems = i;
480   }
481 
482   UInt32 numMatItems;
483   {
484     UInt32 numSectorsForMat = Get32(p + 0x40);
485     numMatItems = (UInt32)numSectorsForMat << ssb2;
486     if ((numMatItems >> ssb2) != numSectorsForMat)
487       return S_FALSE;
488     Mat.Alloc(numMatItems);
489     UInt32 i;
490     UInt32 sid = Get32(p + 0x3C); // short-sector table SID
491     for (i = 0; i < numMatItems; i += numSidsInSec)
492     {
493       RINOK(ReadIDs(inStream, sect, sectorSizeBits, sid, Mat + i));
494       if (sid >= numFatItems)
495         return S_FALSE;
496       sid = Fat[sid];
497     }
498     if (sid != NFatID::kEndOfChain)
499       return S_FALSE;
500   }
501 
502   {
503     CByteBuffer used(numFatItems);
504     for (UInt32 i = 0; i < numFatItems; i++)
505       used[i] = 0;
506     UInt32 sid = Get32(p + 0x30); // directory stream SID
507     for (;;)
508     {
509       if (sid >= numFatItems)
510         return S_FALSE;
511       if (used[sid])
512         return S_FALSE;
513       used[sid] = 1;
514       RINOK(ReadSector(inStream, sect, sectorSizeBits, sid));
515       for (UInt32 i = 0; i < sectSize; i += 128)
516       {
517         CItem item;
518         item.Parse(sect + i, mode64bit);
519         Items.Add(item);
520       }
521       sid = Fat[sid];
522       if (sid == NFatID::kEndOfChain)
523         break;
524     }
525   }
526 
527   const CItem &root = Items[0];
528 
529   {
530     UInt32 numSectorsInMiniStream;
531     {
532       UInt64 numSatSects64 = (root.Size + sectSize - 1) >> sectorSizeBits;
533       if (numSatSects64 > NFatID::kMaxValue)
534         return S_FALSE;
535       numSectorsInMiniStream = (UInt32)numSatSects64;
536     }
537     NumSectorsInMiniStream = numSectorsInMiniStream;
538     MiniSids.Alloc(numSectorsInMiniStream);
539     {
540       UInt64 matSize64 = (root.Size + ((UInt64)1 << miniSectorSizeBits) - 1) >> miniSectorSizeBits;
541       if (matSize64 > NFatID::kMaxValue)
542         return S_FALSE;
543       MatSize = (UInt32)matSize64;
544       if (numMatItems < MatSize)
545         return S_FALSE;
546     }
547 
548     UInt32 sid = root.Sid;
549     for (UInt32 i = 0; ; i++)
550     {
551       if (sid == NFatID::kEndOfChain)
552       {
553         if (i != numSectorsInMiniStream)
554           return S_FALSE;
555         break;
556       }
557       if (i >= numSectorsInMiniStream)
558         return S_FALSE;
559       MiniSids[i] = sid;
560       if (sid >= numFatItems)
561         return S_FALSE;
562       sid = Fat[sid];
563     }
564   }
565 
566   RINOK(AddNode(-1, root.SonDid));
567 
568   unsigned numCabs = 0;
569 
570   FOR_VECTOR (i, Refs)
571   {
572     const CItem &item = Items[Refs[i].Did];
573     if (item.IsDir() || numCabs > 1)
574       continue;
575     bool isMsiName;
576     const UString msiName = ConvertName(item.Name, isMsiName);
577     if (isMsiName && !msiName.IsEmpty())
578     {
579       // bool isThereExt = (msiName.Find(L'.') >= 0);
580       bool isMsiSpec = (msiName[0] == k_Msi_SpecChar);
581       if ((msiName.Len() >= 4 && StringsAreEqualNoCase_Ascii(msiName.RightPtr(4), ".cab"))
582           || (!isMsiSpec && msiName.Len() >= 3 && StringsAreEqualNoCase_Ascii(msiName.RightPtr(3), "exe"))
583           // || (!isMsiSpec && !isThereExt)
584           )
585       {
586         numCabs++;
587         MainSubfile = i;
588       }
589     }
590   }
591 
592   if (numCabs > 1)
593     MainSubfile = -1;
594 
595   {
596     FOR_VECTOR (t, Items)
597     {
598       Update_PhySize_WithItem(t);
599     }
600   }
601   {
602     FOR_VECTOR (t, Items)
603     {
604       const CItem &item = Items[t];
605 
606       if (IsMsiName(item.Name))
607       {
608         Type = k_Type_Msi;
609         if (memcmp(item.Name, kMspSequence, kMspSequence_Size) == 0)
610         {
611           Type = k_Type_Msp;
612           break;
613         }
614         continue;
615       }
616       if (AreEqualNames(item.Name, "WordDocument"))
617       {
618         Type = k_Type_Doc;
619         break;
620       }
621       if (AreEqualNames(item.Name, "PowerPoint Document"))
622       {
623         Type = k_Type_Ppt;
624         break;
625       }
626       if (AreEqualNames(item.Name, "Workbook"))
627       {
628         Type = k_Type_Xls;
629         break;
630       }
631     }
632   }
633 
634   return S_OK;
635 }
636 
637 class CHandler:
638   public IInArchive,
639   public IInArchiveGetStream,
640   public CMyUnknownImp
641 {
642   CMyComPtr<IInStream> _stream;
643   CDatabase _db;
644 public:
645   MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream)
646   INTERFACE_IInArchive(;)
647   STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
648 };
649 
650 static const Byte kProps[] =
651 {
652   kpidPath,
653   kpidSize,
654   kpidPackSize,
655   kpidCTime,
656   kpidMTime
657 };
658 
659 static const Byte kArcProps[] =
660 {
661   kpidExtension,
662   kpidClusterSize,
663   kpidSectorSize
664 };
665 
666 IMP_IInArchive_Props
667 IMP_IInArchive_ArcProps
668 
GetArchiveProperty(PROPID propID,PROPVARIANT * value)669 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
670 {
671   COM_TRY_BEGIN
672   NWindows::NCOM::CPropVariant prop;
673   switch (propID)
674   {
675     case kpidExtension: prop = kExtensions[(unsigned)_db.Type]; break;
676     case kpidPhySize: prop = _db.PhySize; break;
677     case kpidClusterSize: prop = (UInt32)1 << _db.SectorSizeBits; break;
678     case kpidSectorSize: prop = (UInt32)1 << _db.MiniSectorSizeBits; break;
679     case kpidMainSubfile: if (_db.MainSubfile >= 0) prop = (UInt32)_db.MainSubfile; break;
680     case kpidIsNotArcType: if (_db.IsNotArcType()) prop = true; break;
681   }
682   prop.Detach(value);
683   return S_OK;
684   COM_TRY_END
685 }
686 
GetProperty(UInt32 index,PROPID propID,PROPVARIANT * value)687 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
688 {
689   COM_TRY_BEGIN
690   NWindows::NCOM::CPropVariant prop;
691   const CRef &ref = _db.Refs[index];
692   const CItem &item = _db.Items[ref.Did];
693 
694   switch (propID)
695   {
696     case kpidPath:  prop = _db.GetItemPath(index); break;
697     case kpidIsDir:  prop = item.IsDir(); break;
698     case kpidCTime:  prop = item.CTime; break;
699     case kpidMTime:  prop = item.MTime; break;
700     case kpidPackSize:  if (!item.IsDir()) prop = _db.GetItemPackSize(item.Size); break;
701     case kpidSize:  if (!item.IsDir()) prop = item.Size; break;
702   }
703   prop.Detach(value);
704   return S_OK;
705   COM_TRY_END
706 }
707 
Open(IInStream * inStream,const UInt64 *,IArchiveOpenCallback *)708 STDMETHODIMP CHandler::Open(IInStream *inStream,
709     const UInt64 * /* maxCheckStartPosition */,
710     IArchiveOpenCallback * /* openArchiveCallback */)
711 {
712   COM_TRY_BEGIN
713   Close();
714   try
715   {
716     if (_db.Open(inStream) != S_OK)
717       return S_FALSE;
718     _stream = inStream;
719   }
720   catch(...) { return S_FALSE; }
721   return S_OK;
722   COM_TRY_END
723 }
724 
Close()725 STDMETHODIMP CHandler::Close()
726 {
727   _db.Clear();
728   _stream.Release();
729   return S_OK;
730 }
731 
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)732 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
733     Int32 testMode, IArchiveExtractCallback *extractCallback)
734 {
735   COM_TRY_BEGIN
736   bool allFilesMode = (numItems == (UInt32)(Int32)-1);
737   if (allFilesMode)
738     numItems = _db.Refs.Size();
739   if (numItems == 0)
740     return S_OK;
741   UInt32 i;
742   UInt64 totalSize = 0;
743   for (i = 0; i < numItems; i++)
744   {
745     const CItem &item = _db.Items[_db.Refs[allFilesMode ? i : indices[i]].Did];
746     if (!item.IsDir())
747       totalSize += item.Size;
748   }
749   RINOK(extractCallback->SetTotal(totalSize));
750 
751   UInt64 totalPackSize;
752   totalSize = totalPackSize = 0;
753 
754   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
755   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
756 
757   CLocalProgress *lps = new CLocalProgress;
758   CMyComPtr<ICompressProgressInfo> progress = lps;
759   lps->Init(extractCallback, false);
760 
761   for (i = 0; i < numItems; i++)
762   {
763     lps->InSize = totalPackSize;
764     lps->OutSize = totalSize;
765     RINOK(lps->SetCur());
766     Int32 index = allFilesMode ? i : indices[i];
767     const CItem &item = _db.Items[_db.Refs[index].Did];
768 
769     CMyComPtr<ISequentialOutStream> outStream;
770     Int32 askMode = testMode ?
771         NExtract::NAskMode::kTest :
772         NExtract::NAskMode::kExtract;
773     RINOK(extractCallback->GetStream(index, &outStream, askMode));
774 
775     if (item.IsDir())
776     {
777       RINOK(extractCallback->PrepareOperation(askMode));
778       RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
779       continue;
780     }
781 
782     totalPackSize += _db.GetItemPackSize(item.Size);
783     totalSize += item.Size;
784 
785     if (!testMode && !outStream)
786       continue;
787     RINOK(extractCallback->PrepareOperation(askMode));
788     Int32 res = NExtract::NOperationResult::kDataError;
789     CMyComPtr<ISequentialInStream> inStream;
790     HRESULT hres = GetStream(index, &inStream);
791     if (hres == S_FALSE)
792       res = NExtract::NOperationResult::kDataError;
793     else if (hres == E_NOTIMPL)
794       res = NExtract::NOperationResult::kUnsupportedMethod;
795     else
796     {
797       RINOK(hres);
798       if (inStream)
799       {
800         RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress));
801         if (copyCoderSpec->TotalSize == item.Size)
802           res = NExtract::NOperationResult::kOK;
803       }
804     }
805     outStream.Release();
806     RINOK(extractCallback->SetOperationResult(res));
807   }
808   return S_OK;
809   COM_TRY_END
810 }
811 
GetNumberOfItems(UInt32 * numItems)812 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
813 {
814   *numItems = _db.Refs.Size();
815   return S_OK;
816 }
817 
GetStream(UInt32 index,ISequentialInStream ** stream)818 STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
819 {
820   COM_TRY_BEGIN
821   *stream = 0;
822   UInt32 itemIndex = _db.Refs[index].Did;
823   const CItem &item = _db.Items[itemIndex];
824   CClusterInStream *streamSpec = new CClusterInStream;
825   CMyComPtr<ISequentialInStream> streamTemp = streamSpec;
826   streamSpec->Stream = _stream;
827   streamSpec->StartOffset = 0;
828 
829   bool isLargeStream = (itemIndex == 0 || _db.IsLargeStream(item.Size));
830   int bsLog = isLargeStream ? _db.SectorSizeBits : _db.MiniSectorSizeBits;
831   streamSpec->BlockSizeLog = bsLog;
832   streamSpec->Size = item.Size;
833 
834   UInt32 clusterSize = (UInt32)1 << bsLog;
835   UInt64 numClusters64 = (item.Size + clusterSize - 1) >> bsLog;
836   if (numClusters64 >= ((UInt32)1 << 31))
837     return E_NOTIMPL;
838   streamSpec->Vector.ClearAndReserve((unsigned)numClusters64);
839   UInt32 sid = item.Sid;
840   UInt64 size = item.Size;
841 
842   if (size != 0)
843   {
844     for (;; size -= clusterSize)
845     {
846       if (isLargeStream)
847       {
848         if (sid >= _db.FatSize)
849           return S_FALSE;
850         streamSpec->Vector.AddInReserved(sid + 1);
851         sid = _db.Fat[sid];
852       }
853       else
854       {
855         UInt64 val = 0;
856         if (sid >= _db.MatSize || !_db.GetMiniCluster(sid, val) || val >= (UInt64)1 << 32)
857           return S_FALSE;
858         streamSpec->Vector.AddInReserved((UInt32)val);
859         sid = _db.Mat[sid];
860       }
861       if (size <= clusterSize)
862         break;
863     }
864   }
865   if (sid != NFatID::kEndOfChain)
866     return S_FALSE;
867   RINOK(streamSpec->InitAndSeek());
868   *stream = streamTemp.Detach();
869   return S_OK;
870   COM_TRY_END
871 }
872 
873 REGISTER_ARC_I(
874   "Compound", "msi msp doc xls ppt", 0, 0xE5,
875   kSignature,
876   0,
877   0,
878   NULL)
879 
880 }}
881