• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Client7z.cpp
2 
3 #include "StdAfx.h"
4 
5 #include <stdio.h>
6 
7 #include "../../../Common/MyWindows.h"
8 #include "../../../Common/MyInitGuid.h"
9 
10 #include "../../../Common/Defs.h"
11 #include "../../../Common/IntToString.h"
12 #include "../../../Common/StringConvert.h"
13 
14 #include "../../../Windows/DLL.h"
15 #include "../../../Windows/FileDir.h"
16 #include "../../../Windows/FileFind.h"
17 #include "../../../Windows/FileName.h"
18 #include "../../../Windows/NtCheck.h"
19 #include "../../../Windows/PropVariant.h"
20 #include "../../../Windows/PropVariantConv.h"
21 
22 #include "../../Common/FileStreams.h"
23 
24 #include "../../Archive/IArchive.h"
25 
26 #include "../../IPassword.h"
27 #include "../../../../C/7zVersion.h"
28 
29 #ifdef _WIN32
30 extern
31 HINSTANCE g_hInstance;
32 HINSTANCE g_hInstance = NULL;
33 #endif
34 
35 // You can find full list of all GUIDs supported by 7-Zip in Guid.txt file.
36 // 7z format GUID: {23170F69-40C1-278A-1000-000110070000}
37 
38 #define DEFINE_GUID_ARC(name, id) Z7_DEFINE_GUID(name, \
39   0x23170F69, 0x40C1, 0x278A, 0x10, 0x00, 0x00, 0x01, 0x10, id, 0x00, 0x00);
40 
41 enum
42 {
43   kId_Zip = 1,
44   kId_BZip2 = 2,
45   kId_7z = 7,
46   kId_Xz = 0xC,
47   kId_Tar = 0xEE,
48   kId_GZip = 0xEF
49 };
50 
51 // use another id, if you want to support other formats (zip, Xz, ...).
52 // DEFINE_GUID_ARC (CLSID_Format, kId_Zip)
53 // DEFINE_GUID_ARC (CLSID_Format, kId_BZip2)
54 // DEFINE_GUID_ARC (CLSID_Format, kId_Xz)
55 // DEFINE_GUID_ARC (CLSID_Format, kId_Tar)
56 // DEFINE_GUID_ARC (CLSID_Format, kId_GZip)
57 DEFINE_GUID_ARC (CLSID_Format, kId_7z)
58 
59 using namespace NWindows;
60 using namespace NFile;
61 using namespace NDir;
62 
63 #ifdef _WIN32
64 #define kDllName "7z.dll"
65 #else
66 #define kDllName "7z.so"
67 #endif
68 
69 static const char * const kCopyrightString =
70   "\n"
71   "7-Zip"
72   " (" kDllName " client)"
73   " " MY_VERSION
74   " : " MY_COPYRIGHT_DATE
75   "\n";
76 
77 static const char * const kHelpString =
78 "Usage: 7zcl.exe [a | l | x] archive.7z [fileName ...]\n"
79 "Examples:\n"
80 "  7zcl.exe a archive.7z f1.txt f2.txt  : compress two files to archive.7z\n"
81 "  7zcl.exe l archive.7z   : List contents of archive.7z\n"
82 "  7zcl.exe x archive.7z   : eXtract files from archive.7z\n";
83 
84 
Convert_UString_to_AString(const UString & s,AString & temp)85 static void Convert_UString_to_AString(const UString &s, AString &temp)
86 {
87   int codePage = CP_OEMCP;
88   /*
89   int g_CodePage = -1;
90   int codePage = g_CodePage;
91   if (codePage == -1)
92     codePage = CP_OEMCP;
93   if (codePage == CP_UTF8)
94     ConvertUnicodeToUTF8(s, temp);
95   else
96   */
97     UnicodeStringToMultiByte2(temp, s, (UINT)codePage);
98 }
99 
CmdStringToFString(const char * s)100 static FString CmdStringToFString(const char *s)
101 {
102   return us2fs(GetUnicodeString(s));
103 }
104 
Print(const char * s)105 static void Print(const char *s)
106 {
107   fputs(s, stdout);
108 }
109 
Print(const AString & s)110 static void Print(const AString &s)
111 {
112   Print(s.Ptr());
113 }
114 
Print(const UString & s)115 static void Print(const UString &s)
116 {
117   AString as;
118   Convert_UString_to_AString(s, as);
119   Print(as);
120 }
121 
Print(const wchar_t * s)122 static void Print(const wchar_t *s)
123 {
124   Print(UString(s));
125 }
126 
PrintNewLine()127 static void PrintNewLine()
128 {
129   Print("\n");
130 }
131 
PrintStringLn(const char * s)132 static void PrintStringLn(const char *s)
133 {
134   Print(s);
135   PrintNewLine();
136 }
137 
PrintError(const char * message)138 static void PrintError(const char *message)
139 {
140   Print("Error: ");
141   PrintNewLine();
142   Print(message);
143   PrintNewLine();
144 }
145 
PrintError(const char * message,const FString & name)146 static void PrintError(const char *message, const FString &name)
147 {
148   PrintError(message);
149   Print(name);
150 }
151 
152 
IsArchiveItemProp(IInArchive * archive,UInt32 index,PROPID propID,bool & result)153 static HRESULT IsArchiveItemProp(IInArchive *archive, UInt32 index, PROPID propID, bool &result)
154 {
155   NCOM::CPropVariant prop;
156   RINOK(archive->GetProperty(index, propID, &prop))
157   if (prop.vt == VT_BOOL)
158     result = VARIANT_BOOLToBool(prop.boolVal);
159   else if (prop.vt == VT_EMPTY)
160     result = false;
161   else
162     return E_FAIL;
163   return S_OK;
164 }
165 
IsArchiveItemFolder(IInArchive * archive,UInt32 index,bool & result)166 static HRESULT IsArchiveItemFolder(IInArchive *archive, UInt32 index, bool &result)
167 {
168   return IsArchiveItemProp(archive, index, kpidIsDir, result);
169 }
170 
171 
172 static const wchar_t * const kEmptyFileAlias = L"[Content]";
173 
174 
175 //////////////////////////////////////////////////////////////
176 // Archive Open callback class
177 
178 
179 class CArchiveOpenCallback Z7_final:
180   public IArchiveOpenCallback,
181   public ICryptoGetTextPassword,
182   public CMyUnknownImp
183 {
184   Z7_IFACES_IMP_UNK_2(IArchiveOpenCallback, ICryptoGetTextPassword)
185 public:
186 
187   bool PasswordIsDefined;
188   UString Password;
189 
CArchiveOpenCallback()190   CArchiveOpenCallback() : PasswordIsDefined(false) {}
191 };
192 
Z7_COM7F_IMF(CArchiveOpenCallback::SetTotal (const UInt64 *,const UInt64 *))193 Z7_COM7F_IMF(CArchiveOpenCallback::SetTotal(const UInt64 * /* files */, const UInt64 * /* bytes */))
194 {
195   return S_OK;
196 }
197 
Z7_COM7F_IMF(CArchiveOpenCallback::SetCompleted (const UInt64 *,const UInt64 *))198 Z7_COM7F_IMF(CArchiveOpenCallback::SetCompleted(const UInt64 * /* files */, const UInt64 * /* bytes */))
199 {
200   return S_OK;
201 }
202 
Z7_COM7F_IMF(CArchiveOpenCallback::CryptoGetTextPassword (BSTR * password))203 Z7_COM7F_IMF(CArchiveOpenCallback::CryptoGetTextPassword(BSTR *password))
204 {
205   if (!PasswordIsDefined)
206   {
207     // You can ask real password here from user
208     // Password = GetPassword(OutStream);
209     // PasswordIsDefined = true;
210     PrintError("Password is not defined");
211     return E_ABORT;
212   }
213   return StringToBstr(Password, password);
214 }
215 
216 
217 
218 static const char * const kIncorrectCommand = "incorrect command";
219 
220 //////////////////////////////////////////////////////////////
221 // Archive Extracting callback class
222 
223 static const char * const kTestingString    =  "Testing     ";
224 static const char * const kExtractingString =  "Extracting  ";
225 static const char * const kSkippingString   =  "Skipping    ";
226 static const char * const kReadingString    =  "Reading     ";
227 
228 static const char * const kUnsupportedMethod = "Unsupported Method";
229 static const char * const kCRCFailed = "CRC Failed";
230 static const char * const kDataError = "Data Error";
231 static const char * const kUnavailableData = "Unavailable data";
232 static const char * const kUnexpectedEnd = "Unexpected end of data";
233 static const char * const kDataAfterEnd = "There are some data after the end of the payload data";
234 static const char * const kIsNotArc = "Is not archive";
235 static const char * const kHeadersError = "Headers Error";
236 
237 
238 struct CArcTime
239 {
240   FILETIME FT;
241   UInt16 Prec;
242   Byte Ns100;
243   bool Def;
244 
CArcTimeCArcTime245   CArcTime()
246   {
247     Clear();
248   }
249 
ClearCArcTime250   void Clear()
251   {
252     FT.dwHighDateTime = FT.dwLowDateTime = 0;
253     Prec = 0;
254     Ns100 = 0;
255     Def = false;
256   }
257 
IsZeroCArcTime258   bool IsZero() const
259   {
260     return FT.dwLowDateTime == 0 && FT.dwHighDateTime == 0 && Ns100 == 0;
261   }
262 
GetNumDigitsCArcTime263   int GetNumDigits() const
264   {
265     if (Prec == k_PropVar_TimePrec_Unix ||
266         Prec == k_PropVar_TimePrec_DOS)
267       return 0;
268     if (Prec == k_PropVar_TimePrec_HighPrec)
269       return 9;
270     if (Prec == k_PropVar_TimePrec_0)
271       return 7;
272     int digits = (int)Prec - (int)k_PropVar_TimePrec_Base;
273     if (digits < 0)
274       digits = 0;
275     return digits;
276   }
277 
Write_To_FiTimeCArcTime278   void Write_To_FiTime(CFiTime &dest) const
279   {
280    #ifdef _WIN32
281     dest = FT;
282    #else
283     if (FILETIME_To_timespec(FT, dest))
284     if ((Prec == k_PropVar_TimePrec_Base + 8 ||
285          Prec == k_PropVar_TimePrec_Base + 9)
286         && Ns100 != 0)
287     {
288       dest.tv_nsec += Ns100;
289     }
290    #endif
291   }
292 
Set_From_PropCArcTime293   void Set_From_Prop(const PROPVARIANT &prop)
294   {
295     FT = prop.filetime;
296     unsigned prec = 0;
297     unsigned ns100 = 0;
298     const unsigned prec_Temp = prop.wReserved1;
299     if (prec_Temp != 0
300         && prec_Temp <= k_PropVar_TimePrec_1ns
301         && prop.wReserved3 == 0)
302     {
303       const unsigned ns100_Temp = prop.wReserved2;
304       if (ns100_Temp < 100)
305       {
306         ns100 = ns100_Temp;
307         prec = prec_Temp;
308       }
309     }
310     Prec = (UInt16)prec;
311     Ns100 = (Byte)ns100;
312     Def = true;
313   }
314 };
315 
316 
317 
318 class CArchiveExtractCallback Z7_final:
319   public IArchiveExtractCallback,
320   public ICryptoGetTextPassword,
321   public CMyUnknownImp
322 {
323   Z7_IFACES_IMP_UNK_2(IArchiveExtractCallback, ICryptoGetTextPassword)
324   Z7_IFACE_COM7_IMP(IProgress)
325 
326   CMyComPtr<IInArchive> _archiveHandler;
327   FString _directoryPath;  // Output directory
328   UString _filePath;       // name inside arcvhive
329   FString _diskFilePath;   // full path to file on disk
330   bool _extractMode;
331   struct CProcessedFileInfo
332   {
333     CArcTime MTime;
334     UInt32 Attrib;
335     bool isDir;
336     bool Attrib_Defined;
337   } _processedFileInfo;
338 
339   COutFileStream *_outFileStreamSpec;
340   CMyComPtr<ISequentialOutStream> _outFileStream;
341 
342 public:
343   void Init(IInArchive *archiveHandler, const FString &directoryPath);
344 
345   UInt64 NumErrors;
346   bool PasswordIsDefined;
347   UString Password;
348 
CArchiveExtractCallback()349   CArchiveExtractCallback() : PasswordIsDefined(false) {}
350 };
351 
Init(IInArchive * archiveHandler,const FString & directoryPath)352 void CArchiveExtractCallback::Init(IInArchive *archiveHandler, const FString &directoryPath)
353 {
354   NumErrors = 0;
355   _archiveHandler = archiveHandler;
356   _directoryPath = directoryPath;
357   NName::NormalizeDirPathPrefix(_directoryPath);
358 }
359 
Z7_COM7F_IMF(CArchiveExtractCallback::SetTotal (UInt64))360 Z7_COM7F_IMF(CArchiveExtractCallback::SetTotal(UInt64 /* size */))
361 {
362   return S_OK;
363 }
364 
Z7_COM7F_IMF(CArchiveExtractCallback::SetCompleted (const UInt64 *))365 Z7_COM7F_IMF(CArchiveExtractCallback::SetCompleted(const UInt64 * /* completeValue */))
366 {
367   return S_OK;
368 }
369 
Z7_COM7F_IMF(CArchiveExtractCallback::GetStream (UInt32 index,ISequentialOutStream ** outStream,Int32 askExtractMode))370 Z7_COM7F_IMF(CArchiveExtractCallback::GetStream(UInt32 index,
371     ISequentialOutStream **outStream, Int32 askExtractMode))
372 {
373   *outStream = NULL;
374   _outFileStream.Release();
375 
376   {
377     // Get Name
378     NCOM::CPropVariant prop;
379     RINOK(_archiveHandler->GetProperty(index, kpidPath, &prop))
380 
381     UString fullPath;
382     if (prop.vt == VT_EMPTY)
383       fullPath = kEmptyFileAlias;
384     else
385     {
386       if (prop.vt != VT_BSTR)
387         return E_FAIL;
388       fullPath = prop.bstrVal;
389     }
390     _filePath = fullPath;
391   }
392 
393   if (askExtractMode != NArchive::NExtract::NAskMode::kExtract)
394     return S_OK;
395 
396   {
397     // Get Attrib
398     NCOM::CPropVariant prop;
399     RINOK(_archiveHandler->GetProperty(index, kpidAttrib, &prop))
400     if (prop.vt == VT_EMPTY)
401     {
402       _processedFileInfo.Attrib = 0;
403       _processedFileInfo.Attrib_Defined = false;
404     }
405     else
406     {
407       if (prop.vt != VT_UI4)
408         return E_FAIL;
409       _processedFileInfo.Attrib = prop.ulVal;
410       _processedFileInfo.Attrib_Defined = true;
411     }
412   }
413 
414   RINOK(IsArchiveItemFolder(_archiveHandler, index, _processedFileInfo.isDir))
415 
416   {
417     _processedFileInfo.MTime.Clear();
418     // Get Modified Time
419     NCOM::CPropVariant prop;
420     RINOK(_archiveHandler->GetProperty(index, kpidMTime, &prop))
421     switch (prop.vt)
422     {
423       case VT_EMPTY:
424         // _processedFileInfo.MTime = _utcMTimeDefault;
425         break;
426       case VT_FILETIME:
427         _processedFileInfo.MTime.Set_From_Prop(prop);
428         break;
429       default:
430         return E_FAIL;
431     }
432 
433   }
434   {
435     // Get Size
436     NCOM::CPropVariant prop;
437     RINOK(_archiveHandler->GetProperty(index, kpidSize, &prop))
438     UInt64 newFileSize;
439     /* bool newFileSizeDefined = */ ConvertPropVariantToUInt64(prop, newFileSize);
440   }
441 
442 
443   {
444     // Create folders for file
445     int slashPos = _filePath.ReverseFind_PathSepar();
446     if (slashPos >= 0)
447       CreateComplexDir(_directoryPath + us2fs(_filePath.Left(slashPos)));
448   }
449 
450   FString fullProcessedPath = _directoryPath + us2fs(_filePath);
451   _diskFilePath = fullProcessedPath;
452 
453   if (_processedFileInfo.isDir)
454   {
455     CreateComplexDir(fullProcessedPath);
456   }
457   else
458   {
459     NFind::CFileInfo fi;
460     if (fi.Find(fullProcessedPath))
461     {
462       if (!DeleteFileAlways(fullProcessedPath))
463       {
464         PrintError("Cannot delete output file", fullProcessedPath);
465         return E_ABORT;
466       }
467     }
468 
469     _outFileStreamSpec = new COutFileStream;
470     CMyComPtr<ISequentialOutStream> outStreamLoc(_outFileStreamSpec);
471     if (!_outFileStreamSpec->Open(fullProcessedPath, CREATE_ALWAYS))
472     {
473       PrintError("Cannot open output file", fullProcessedPath);
474       return E_ABORT;
475     }
476     _outFileStream = outStreamLoc;
477     *outStream = outStreamLoc.Detach();
478   }
479   return S_OK;
480 }
481 
Z7_COM7F_IMF(CArchiveExtractCallback::PrepareOperation (Int32 askExtractMode))482 Z7_COM7F_IMF(CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode))
483 {
484   _extractMode = false;
485   switch (askExtractMode)
486   {
487     case NArchive::NExtract::NAskMode::kExtract:  _extractMode = true; break;
488   }
489   switch (askExtractMode)
490   {
491     case NArchive::NExtract::NAskMode::kExtract:  Print(kExtractingString); break;
492     case NArchive::NExtract::NAskMode::kTest:  Print(kTestingString); break;
493     case NArchive::NExtract::NAskMode::kSkip:  Print(kSkippingString); break;
494     case NArchive::NExtract::NAskMode::kReadExternal: Print(kReadingString); break;
495     default:
496       Print("??? "); break;
497   }
498   Print(_filePath);
499   return S_OK;
500 }
501 
Z7_COM7F_IMF(CArchiveExtractCallback::SetOperationResult (Int32 operationResult))502 Z7_COM7F_IMF(CArchiveExtractCallback::SetOperationResult(Int32 operationResult))
503 {
504   switch (operationResult)
505   {
506     case NArchive::NExtract::NOperationResult::kOK:
507       break;
508     default:
509     {
510       NumErrors++;
511       Print("  :  ");
512       const char *s = NULL;
513       switch (operationResult)
514       {
515         case NArchive::NExtract::NOperationResult::kUnsupportedMethod:
516           s = kUnsupportedMethod;
517           break;
518         case NArchive::NExtract::NOperationResult::kCRCError:
519           s = kCRCFailed;
520           break;
521         case NArchive::NExtract::NOperationResult::kDataError:
522           s = kDataError;
523           break;
524         case NArchive::NExtract::NOperationResult::kUnavailable:
525           s = kUnavailableData;
526           break;
527         case NArchive::NExtract::NOperationResult::kUnexpectedEnd:
528           s = kUnexpectedEnd;
529           break;
530         case NArchive::NExtract::NOperationResult::kDataAfterEnd:
531           s = kDataAfterEnd;
532           break;
533         case NArchive::NExtract::NOperationResult::kIsNotArc:
534           s = kIsNotArc;
535           break;
536         case NArchive::NExtract::NOperationResult::kHeadersError:
537           s = kHeadersError;
538           break;
539       }
540       if (s)
541       {
542         Print("Error : ");
543         Print(s);
544       }
545       else
546       {
547         char temp[16];
548         ConvertUInt32ToString((UInt32)operationResult, temp);
549         Print("Error #");
550         Print(temp);
551       }
552     }
553   }
554 
555   if (_outFileStream)
556   {
557     if (_processedFileInfo.MTime.Def)
558     {
559       CFiTime ft;
560       _processedFileInfo.MTime.Write_To_FiTime(ft);
561       _outFileStreamSpec->SetMTime(&ft);
562     }
563     RINOK(_outFileStreamSpec->Close())
564   }
565   _outFileStream.Release();
566   if (_extractMode && _processedFileInfo.Attrib_Defined)
567     SetFileAttrib_PosixHighDetect(_diskFilePath, _processedFileInfo.Attrib);
568   PrintNewLine();
569   return S_OK;
570 }
571 
572 
Z7_COM7F_IMF(CArchiveExtractCallback::CryptoGetTextPassword (BSTR * password))573 Z7_COM7F_IMF(CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password))
574 {
575   if (!PasswordIsDefined)
576   {
577     // You can ask real password here from user
578     // Password = GetPassword(OutStream);
579     // PasswordIsDefined = true;
580     PrintError("Password is not defined");
581     return E_ABORT;
582   }
583   return StringToBstr(Password, password);
584 }
585 
586 
587 
588 //////////////////////////////////////////////////////////////
589 // Archive Creating callback class
590 
591 struct CDirItem: public NWindows::NFile::NFind::CFileInfoBase
592 {
593   UString Path_For_Handler;
594   FString FullPath; // for filesystem
595 
CDirItemCDirItem596   CDirItem(const NWindows::NFile::NFind::CFileInfo &fi):
597       CFileInfoBase(fi)
598     {}
599 };
600 
601 class CArchiveUpdateCallback Z7_final:
602   public IArchiveUpdateCallback2,
603   public ICryptoGetTextPassword2,
604   public CMyUnknownImp
605 {
606   Z7_IFACES_IMP_UNK_2(IArchiveUpdateCallback2, ICryptoGetTextPassword2)
607   Z7_IFACE_COM7_IMP(IProgress)
608   Z7_IFACE_COM7_IMP(IArchiveUpdateCallback)
609 
610 public:
611   CRecordVector<UInt64> VolumesSizes;
612   UString VolName;
613   UString VolExt;
614 
615   FString DirPrefix;
616   const CObjectVector<CDirItem> *DirItems;
617 
618   bool PasswordIsDefined;
619   UString Password;
620   bool AskPassword;
621 
622   bool m_NeedBeClosed;
623 
624   FStringVector FailedFiles;
625   CRecordVector<HRESULT> FailedCodes;
626 
CArchiveUpdateCallback()627   CArchiveUpdateCallback():
628       DirItems(NULL),
629       PasswordIsDefined(false),
630       AskPassword(false)
631       {}
632 
~CArchiveUpdateCallback()633   ~CArchiveUpdateCallback() { Finilize(); }
634   HRESULT Finilize();
635 
Init(const CObjectVector<CDirItem> * dirItems)636   void Init(const CObjectVector<CDirItem> *dirItems)
637   {
638     DirItems = dirItems;
639     m_NeedBeClosed = false;
640     FailedFiles.Clear();
641     FailedCodes.Clear();
642   }
643 };
644 
Z7_COM7F_IMF(CArchiveUpdateCallback::SetTotal (UInt64))645 Z7_COM7F_IMF(CArchiveUpdateCallback::SetTotal(UInt64 /* size */))
646 {
647   return S_OK;
648 }
649 
Z7_COM7F_IMF(CArchiveUpdateCallback::SetCompleted (const UInt64 *))650 Z7_COM7F_IMF(CArchiveUpdateCallback::SetCompleted(const UInt64 * /* completeValue */))
651 {
652   return S_OK;
653 }
654 
Z7_COM7F_IMF(CArchiveUpdateCallback::GetUpdateItemInfo (UInt32,Int32 * newData,Int32 * newProperties,UInt32 * indexInArchive))655 Z7_COM7F_IMF(CArchiveUpdateCallback::GetUpdateItemInfo(UInt32 /* index */,
656       Int32 *newData, Int32 *newProperties, UInt32 *indexInArchive))
657 {
658   if (newData)
659     *newData = BoolToInt(true);
660   if (newProperties)
661     *newProperties = BoolToInt(true);
662   if (indexInArchive)
663     *indexInArchive = (UInt32)(Int32)-1;
664   return S_OK;
665 }
666 
Z7_COM7F_IMF(CArchiveUpdateCallback::GetProperty (UInt32 index,PROPID propID,PROPVARIANT * value))667 Z7_COM7F_IMF(CArchiveUpdateCallback::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
668 {
669   NCOM::CPropVariant prop;
670 
671   if (propID == kpidIsAnti)
672   {
673     prop = false;
674     prop.Detach(value);
675     return S_OK;
676   }
677 
678   {
679     const CDirItem &di = (*DirItems)[index];
680     switch (propID)
681     {
682       case kpidPath:  prop = di.Path_For_Handler; break;
683       case kpidIsDir:  prop = di.IsDir(); break;
684       case kpidSize:  prop = di.Size; break;
685       case kpidCTime:  PropVariant_SetFrom_FiTime(prop, di.CTime); break;
686       case kpidATime:  PropVariant_SetFrom_FiTime(prop, di.ATime); break;
687       case kpidMTime:  PropVariant_SetFrom_FiTime(prop, di.MTime); break;
688       case kpidAttrib:  prop = (UInt32)di.GetWinAttrib(); break;
689       case kpidPosixAttrib: prop = (UInt32)di.GetPosixAttrib(); break;
690     }
691   }
692   prop.Detach(value);
693   return S_OK;
694 }
695 
Finilize()696 HRESULT CArchiveUpdateCallback::Finilize()
697 {
698   if (m_NeedBeClosed)
699   {
700     PrintNewLine();
701     m_NeedBeClosed = false;
702   }
703   return S_OK;
704 }
705 
GetStream2(const wchar_t * name)706 static void GetStream2(const wchar_t *name)
707 {
708   Print("Compressing  ");
709   if (name[0] == 0)
710     name = kEmptyFileAlias;
711   Print(name);
712 }
713 
Z7_COM7F_IMF(CArchiveUpdateCallback::GetStream (UInt32 index,ISequentialInStream ** inStream))714 Z7_COM7F_IMF(CArchiveUpdateCallback::GetStream(UInt32 index, ISequentialInStream **inStream))
715 {
716   RINOK(Finilize())
717 
718   const CDirItem &dirItem = (*DirItems)[index];
719   GetStream2(dirItem.Path_For_Handler);
720 
721   if (dirItem.IsDir())
722     return S_OK;
723 
724   {
725     CInFileStream *inStreamSpec = new CInFileStream;
726     CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec);
727     FString path = DirPrefix + dirItem.FullPath;
728     if (!inStreamSpec->Open(path))
729     {
730       const DWORD sysError = ::GetLastError();
731       FailedCodes.Add(HRESULT_FROM_WIN32(sysError));
732       FailedFiles.Add(path);
733       // if (systemError == ERROR_SHARING_VIOLATION)
734       {
735         PrintNewLine();
736         PrintError("WARNING: can't open file");
737         // Print(NError::MyFormatMessageW(systemError));
738         return S_FALSE;
739       }
740       // return sysError;
741     }
742     *inStream = inStreamLoc.Detach();
743   }
744   return S_OK;
745 }
746 
Z7_COM7F_IMF(CArchiveUpdateCallback::SetOperationResult (Int32))747 Z7_COM7F_IMF(CArchiveUpdateCallback::SetOperationResult(Int32 /* operationResult */))
748 {
749   m_NeedBeClosed = true;
750   return S_OK;
751 }
752 
Z7_COM7F_IMF(CArchiveUpdateCallback::GetVolumeSize (UInt32 index,UInt64 * size))753 Z7_COM7F_IMF(CArchiveUpdateCallback::GetVolumeSize(UInt32 index, UInt64 *size))
754 {
755   if (VolumesSizes.Size() == 0)
756     return S_FALSE;
757   if (index >= (UInt32)VolumesSizes.Size())
758     index = VolumesSizes.Size() - 1;
759   *size = VolumesSizes[index];
760   return S_OK;
761 }
762 
Z7_COM7F_IMF(CArchiveUpdateCallback::GetVolumeStream (UInt32 index,ISequentialOutStream ** volumeStream))763 Z7_COM7F_IMF(CArchiveUpdateCallback::GetVolumeStream(UInt32 index, ISequentialOutStream **volumeStream))
764 {
765   wchar_t temp[16];
766   ConvertUInt32ToString(index + 1, temp);
767   UString res = temp;
768   while (res.Len() < 2)
769     res.InsertAtFront(L'0');
770   UString fileName = VolName;
771   fileName.Add_Dot();
772   fileName += res;
773   fileName += VolExt;
774   COutFileStream *streamSpec = new COutFileStream;
775   CMyComPtr<ISequentialOutStream> streamLoc(streamSpec);
776   if (!streamSpec->Create(us2fs(fileName), false))
777     return GetLastError_noZero_HRESULT();
778   *volumeStream = streamLoc.Detach();
779   return S_OK;
780 }
781 
Z7_COM7F_IMF(CArchiveUpdateCallback::CryptoGetTextPassword2 (Int32 * passwordIsDefined,BSTR * password))782 Z7_COM7F_IMF(CArchiveUpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password))
783 {
784   if (!PasswordIsDefined)
785   {
786     if (AskPassword)
787     {
788       // You can ask real password here from user
789       // Password = GetPassword(OutStream);
790       // PasswordIsDefined = true;
791       PrintError("Password is not defined");
792       return E_ABORT;
793     }
794   }
795   *passwordIsDefined = BoolToInt(PasswordIsDefined);
796   return StringToBstr(Password, password);
797 }
798 
799 
800 // Main function
801 
802 #if defined(_UNICODE) && !defined(_WIN64) && !defined(UNDER_CE)
803 #define NT_CHECK_FAIL_ACTION PrintError("Unsupported Windows version"); return 1;
804 #endif
805 
main(int numArgs,const char * args[])806 int Z7_CDECL main(int numArgs, const char *args[])
807 {
808   NT_CHECK
809 
810   #ifdef ENV_HAVE_LOCALE
811   MY_SetLocale();
812   #endif
813 
814   PrintStringLn(kCopyrightString);
815 
816   if (numArgs < 2)
817   {
818     PrintStringLn(kHelpString);
819     return 0;
820   }
821 
822   FString dllPrefix;
823 
824   #ifdef _WIN32
825   dllPrefix = NDLL::GetModuleDirPrefix();
826   #else
827   {
828     AString s (args[0]);
829     int sep = s.ReverseFind_PathSepar();
830     s.DeleteFrom(sep + 1);
831     dllPrefix = s;
832   }
833   #endif
834 
835   NDLL::CLibrary lib;
836   if (!lib.Load(dllPrefix + FTEXT(kDllName)))
837   {
838     PrintError("Cannot load 7-zip library");
839     return 1;
840   }
841 
842   Func_CreateObject
843      f_CreateObject = Z7_GET_PROC_ADDRESS(
844   Func_CreateObject, lib.Get_HMODULE(),
845       "CreateObject");
846   if (!f_CreateObject)
847   {
848     PrintError("Cannot get CreateObject");
849     return 1;
850   }
851 
852   char c = 0;
853   UString password;
854   bool passwordIsDefined = false;
855   CObjectVector<FString> params;
856 
857   for (int curCmd = 1; curCmd < numArgs; curCmd++)
858   {
859     AString a(args[curCmd]);
860 
861     if (!a.IsEmpty())
862     {
863       if (a[0] == '-')
864       {
865         if (!passwordIsDefined && a[1] == 'p')
866         {
867           password = GetUnicodeString(a.Ptr(2));
868           passwordIsDefined = true;
869           continue;
870         }
871       }
872       else
873       {
874         if (c)
875         {
876           params.Add(CmdStringToFString(a));
877           continue;
878         }
879         if (a.Len() == 1)
880         {
881           c = (char)MyCharLower_Ascii(a[0]);
882           continue;
883         }
884       }
885     }
886     {
887       PrintError(kIncorrectCommand);
888       return 1;
889     }
890   }
891 
892   if (!c || params.Size() < 1)
893   {
894     PrintError(kIncorrectCommand);
895     return 1;
896   }
897 
898   const FString &archiveName = params[0];
899 
900   if (c == 'a')
901   {
902     // create archive command
903     if (params.Size() < 2)
904     {
905       PrintError(kIncorrectCommand);
906       return 1;
907     }
908     CObjectVector<CDirItem> dirItems;
909     {
910       unsigned i;
911       for (i = 1; i < params.Size(); i++)
912       {
913         const FString &name = params[i];
914 
915         NFind::CFileInfo fi;
916         if (!fi.Find(name))
917         {
918           PrintError("Can't find file", name);
919           return 1;
920         }
921 
922         CDirItem di(fi);
923 
924         di.Path_For_Handler = fs2us(name);
925         di.FullPath = name;
926         dirItems.Add(di);
927       }
928     }
929 
930     COutFileStream *outFileStreamSpec = new COutFileStream;
931     CMyComPtr<IOutStream> outFileStream = outFileStreamSpec;
932     if (!outFileStreamSpec->Create(archiveName, false))
933     {
934       PrintError("can't create archive file");
935       return 1;
936     }
937 
938     CMyComPtr<IOutArchive> outArchive;
939     if (f_CreateObject(&CLSID_Format, &IID_IOutArchive, (void **)&outArchive) != S_OK)
940     {
941       PrintError("Cannot get class object");
942       return 1;
943     }
944 
945     CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback;
946     CMyComPtr<IArchiveUpdateCallback2> updateCallback(updateCallbackSpec);
947     updateCallbackSpec->Init(&dirItems);
948     updateCallbackSpec->PasswordIsDefined = passwordIsDefined;
949     updateCallbackSpec->Password = password;
950 
951     /*
952     {
953       const wchar_t *names[] =
954       {
955         L"m",
956         L"s",
957         L"x"
958       };
959       const unsigned kNumProps = Z7_ARRAY_SIZE(names);
960       NCOM::CPropVariant values[kNumProps] =
961       {
962         L"lzma",
963         false,    // solid mode OFF
964         (UInt32)9 // compression level = 9 - ultra
965       };
966       CMyComPtr<ISetProperties> setProperties;
967       outArchive->QueryInterface(IID_ISetProperties, (void **)&setProperties);
968       if (!setProperties)
969       {
970         PrintError("ISetProperties unsupported");
971         return 1;
972       }
973       if (setProperties->SetProperties(names, values, kNumProps) != S_OK)
974       {
975         PrintError("SetProperties() error");
976         return 1;
977       }
978     }
979     */
980 
981     HRESULT result = outArchive->UpdateItems(outFileStream, dirItems.Size(), updateCallback);
982 
983     updateCallbackSpec->Finilize();
984 
985     if (result != S_OK)
986     {
987       PrintError("Update Error");
988       return 1;
989     }
990 
991     FOR_VECTOR (i, updateCallbackSpec->FailedFiles)
992     {
993       PrintNewLine();
994       PrintError("Error for file", updateCallbackSpec->FailedFiles[i]);
995     }
996 
997     if (updateCallbackSpec->FailedFiles.Size() != 0)
998       return 1;
999   }
1000   else
1001   {
1002     if (params.Size() != 1)
1003     {
1004       PrintError(kIncorrectCommand);
1005       return 1;
1006     }
1007 
1008     bool listCommand;
1009 
1010     if (c == 'l')
1011       listCommand = true;
1012     else if (c == 'x')
1013       listCommand = false;
1014     else
1015     {
1016       PrintError(kIncorrectCommand);
1017       return 1;
1018     }
1019 
1020     CMyComPtr<IInArchive> archive;
1021     if (f_CreateObject(&CLSID_Format, &IID_IInArchive, (void **)&archive) != S_OK)
1022     {
1023       PrintError("Cannot get class object");
1024       return 1;
1025     }
1026 
1027     CInFileStream *fileSpec = new CInFileStream;
1028     CMyComPtr<IInStream> file = fileSpec;
1029 
1030     if (!fileSpec->Open(archiveName))
1031     {
1032       PrintError("Cannot open archive file", archiveName);
1033       return 1;
1034     }
1035 
1036     {
1037       CArchiveOpenCallback *openCallbackSpec = new CArchiveOpenCallback;
1038       CMyComPtr<IArchiveOpenCallback> openCallback(openCallbackSpec);
1039       openCallbackSpec->PasswordIsDefined = passwordIsDefined;
1040       openCallbackSpec->Password = password;
1041 
1042       const UInt64 scanSize = 1 << 23;
1043       if (archive->Open(file, &scanSize, openCallback) != S_OK)
1044       {
1045         PrintError("Cannot open file as archive", archiveName);
1046         return 1;
1047       }
1048     }
1049 
1050     if (listCommand)
1051     {
1052       // List command
1053       UInt32 numItems = 0;
1054       archive->GetNumberOfItems(&numItems);
1055       for (UInt32 i = 0; i < numItems; i++)
1056       {
1057         {
1058           // Get uncompressed size of file
1059           NCOM::CPropVariant prop;
1060           archive->GetProperty(i, kpidSize, &prop);
1061           char s[32];
1062           ConvertPropVariantToShortString(prop, s);
1063           Print(s);
1064           Print("  ");
1065         }
1066         {
1067           // Get name of file
1068           NCOM::CPropVariant prop;
1069           archive->GetProperty(i, kpidPath, &prop);
1070           if (prop.vt == VT_BSTR)
1071             Print(prop.bstrVal);
1072           else if (prop.vt != VT_EMPTY)
1073             Print("ERROR!");
1074         }
1075         PrintNewLine();
1076       }
1077     }
1078     else
1079     {
1080       // Extract command
1081       CArchiveExtractCallback *extractCallbackSpec = new CArchiveExtractCallback;
1082       CMyComPtr<IArchiveExtractCallback> extractCallback(extractCallbackSpec);
1083       extractCallbackSpec->Init(archive, FString()); // second parameter is output folder path
1084       extractCallbackSpec->PasswordIsDefined = passwordIsDefined;
1085       extractCallbackSpec->Password = password;
1086 
1087       /*
1088       const wchar_t *names[] =
1089       {
1090         L"mt",
1091         L"mtf"
1092       };
1093       const unsigned kNumProps = sizeof(names) / sizeof(names[0]);
1094       NCOM::CPropVariant values[kNumProps] =
1095       {
1096         (UInt32)1,
1097         false
1098       };
1099       CMyComPtr<ISetProperties> setProperties;
1100       archive->QueryInterface(IID_ISetProperties, (void **)&setProperties);
1101       if (setProperties)
1102       {
1103         if (setProperties->SetProperties(names, values, kNumProps) != S_OK)
1104         {
1105           PrintError("SetProperties() error");
1106           return 1;
1107         }
1108       }
1109       */
1110 
1111       HRESULT result = archive->Extract(NULL, (UInt32)(Int32)(-1), false, extractCallback);
1112 
1113       if (result != S_OK)
1114       {
1115         PrintError("Extract Error");
1116         return 1;
1117       }
1118     }
1119   }
1120 
1121   return 0;
1122 }
1123