1 // Client7z.cpp
2 
3 #include "StdAfx.h"
4 
5 #include <stdio.h>
6 
7 #include "../../../Common/Defs.h"
8 #include "../../../Common/MyInitGuid.h"
9 
10 #include "../../../Common/IntToString.h"
11 #include "../../../Common/StringConvert.h"
12 
13 #include "../../../Windows/DLL.h"
14 #include "../../../Windows/FileDir.h"
15 #include "../../../Windows/FileFind.h"
16 #include "../../../Windows/FileName.h"
17 #include "../../../Windows/NtCheck.h"
18 #include "../../../Windows/PropVariant.h"
19 #include "../../../Windows/PropVariantConv.h"
20 
21 #include "../../Common/FileStreams.h"
22 
23 #include "../../Archive/IArchive.h"
24 
25 #include "../../IPassword.h"
26 #include "../../../../C/7zVersion.h"
27 
28 #ifdef _WIN32
29 HINSTANCE g_hInstance = 0;
30 #endif
31 
32 // Tou can find the list of all GUIDs in Guid.txt file.
33 // use another CLSIDs, if you want to support other formats (zip, rar, ...).
34 // {23170F69-40C1-278A-1000-000110070000}
35 DEFINE_GUID(CLSID_CFormat7z,
36   0x23170F69, 0x40C1, 0x278A, 0x10, 0x00, 0x00, 0x01, 0x10, 0x07, 0x00, 0x00);
37 
38 using namespace NWindows;
39 using namespace NFile;
40 using namespace NDir;
41 
42 #define kDllName "7z.dll"
43 
44 static const char *kCopyrightString = "\n7-Zip " MY_VERSION
45 " ("  kDllName " client) "
46 MY_COPYRIGHT " " MY_DATE "\n";
47 
48 static const char *kHelpString =
49 "Usage: Client7z.exe [a | l | x ] archive.7z [fileName ...]\n"
50 "Examples:\n"
51 "  Client7z.exe a archive.7z f1.txt f2.txt  : compress two files to archive.7z\n"
52 "  Client7z.exe l archive.7z   : List contents of archive.7z\n"
53 "  Client7z.exe x archive.7z   : eXtract files from archive.7z\n";
54 
55 
FStringToConsoleString(const FString & s)56 static AString FStringToConsoleString(const FString &s)
57 {
58   return GetOemString(fs2us(s));
59 }
60 
CmdStringToFString(const char * s)61 static FString CmdStringToFString(const char *s)
62 {
63   return us2fs(GetUnicodeString(s));
64 }
65 
PrintString(const UString & s)66 static void PrintString(const UString &s)
67 {
68   printf("%s", (LPCSTR)GetOemString(s));
69 }
70 
PrintString(const AString & s)71 static void PrintString(const AString &s)
72 {
73   printf("%s", (LPCSTR)s);
74 }
75 
PrintNewLine()76 static void PrintNewLine()
77 {
78   PrintString("\n");
79 }
80 
PrintStringLn(const AString & s)81 static void PrintStringLn(const AString &s)
82 {
83   PrintString(s);
84   PrintNewLine();
85 }
86 
PrintError(const char * message,const FString & name)87 static void PrintError(const char *message, const FString &name)
88 {
89   printf("Error: %s", (LPCSTR)message);
90   PrintNewLine();
91   PrintString(FStringToConsoleString(name));
92   PrintNewLine();
93 }
94 
PrintError(const AString & s)95 static void PrintError(const AString &s)
96 {
97   PrintNewLine();
98   PrintString(s);
99   PrintNewLine();
100 }
101 
IsArchiveItemProp(IInArchive * archive,UInt32 index,PROPID propID,bool & result)102 static HRESULT IsArchiveItemProp(IInArchive *archive, UInt32 index, PROPID propID, bool &result)
103 {
104   NCOM::CPropVariant prop;
105   RINOK(archive->GetProperty(index, propID, &prop));
106   if (prop.vt == VT_BOOL)
107     result = VARIANT_BOOLToBool(prop.boolVal);
108   else if (prop.vt == VT_EMPTY)
109     result = false;
110   else
111     return E_FAIL;
112   return S_OK;
113 }
114 
IsArchiveItemFolder(IInArchive * archive,UInt32 index,bool & result)115 static HRESULT IsArchiveItemFolder(IInArchive *archive, UInt32 index, bool &result)
116 {
117   return IsArchiveItemProp(archive, index, kpidIsDir, result);
118 }
119 
120 
121 static const wchar_t *kEmptyFileAlias = L"[Content]";
122 
123 
124 //////////////////////////////////////////////////////////////
125 // Archive Open callback class
126 
127 
128 class CArchiveOpenCallback:
129   public IArchiveOpenCallback,
130   public ICryptoGetTextPassword,
131   public CMyUnknownImp
132 {
133 public:
134   MY_UNKNOWN_IMP1(ICryptoGetTextPassword)
135 
136   STDMETHOD(SetTotal)(const UInt64 *files, const UInt64 *bytes);
137   STDMETHOD(SetCompleted)(const UInt64 *files, const UInt64 *bytes);
138 
139   STDMETHOD(CryptoGetTextPassword)(BSTR *password);
140 
141   bool PasswordIsDefined;
142   UString Password;
143 
CArchiveOpenCallback()144   CArchiveOpenCallback() : PasswordIsDefined(false) {}
145 };
146 
SetTotal(const UInt64 *,const UInt64 *)147 STDMETHODIMP CArchiveOpenCallback::SetTotal(const UInt64 * /* files */, const UInt64 * /* bytes */)
148 {
149   return S_OK;
150 }
151 
SetCompleted(const UInt64 *,const UInt64 *)152 STDMETHODIMP CArchiveOpenCallback::SetCompleted(const UInt64 * /* files */, const UInt64 * /* bytes */)
153 {
154   return S_OK;
155 }
156 
CryptoGetTextPassword(BSTR * password)157 STDMETHODIMP CArchiveOpenCallback::CryptoGetTextPassword(BSTR *password)
158 {
159   if (!PasswordIsDefined)
160   {
161     // You can ask real password here from user
162     // Password = GetPassword(OutStream);
163     // PasswordIsDefined = true;
164     PrintError("Password is not defined");
165     return E_ABORT;
166   }
167   return StringToBstr(Password, password);
168 }
169 
170 
171 //////////////////////////////////////////////////////////////
172 // Archive Extracting callback class
173 
174 static const char *kTestingString    =  "Testing     ";
175 static const char *kExtractingString =  "Extracting  ";
176 static const char *kSkippingString   =  "Skipping    ";
177 
178 static const char *kUnsupportedMethod = "Unsupported Method";
179 static const char *kCRCFailed = "CRC Failed";
180 static const char *kDataError = "Data Error";
181 static const char *kUnavailableData = "Unavailable data";
182 static const char *kUnexpectedEnd = "Unexpected end of data";
183 static const char *kDataAfterEnd = "There are some data after the end of the payload data";
184 static const char *kIsNotArc = "Is not archive";
185 static const char *kHeadersError = "Headers Error";
186 
187 class CArchiveExtractCallback:
188   public IArchiveExtractCallback,
189   public ICryptoGetTextPassword,
190   public CMyUnknownImp
191 {
192 public:
193   MY_UNKNOWN_IMP1(ICryptoGetTextPassword)
194 
195   // IProgress
196   STDMETHOD(SetTotal)(UInt64 size);
197   STDMETHOD(SetCompleted)(const UInt64 *completeValue);
198 
199   // IArchiveExtractCallback
200   STDMETHOD(GetStream)(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode);
201   STDMETHOD(PrepareOperation)(Int32 askExtractMode);
202   STDMETHOD(SetOperationResult)(Int32 resultEOperationResult);
203 
204   // ICryptoGetTextPassword
205   STDMETHOD(CryptoGetTextPassword)(BSTR *aPassword);
206 
207 private:
208   CMyComPtr<IInArchive> _archiveHandler;
209   FString _directoryPath;  // Output directory
210   UString _filePath;       // name inside arcvhive
211   FString _diskFilePath;   // full path to file on disk
212   bool _extractMode;
213   struct CProcessedFileInfo
214   {
215     FILETIME MTime;
216     UInt32 Attrib;
217     bool isDir;
218     bool AttribDefined;
219     bool MTimeDefined;
220   } _processedFileInfo;
221 
222   COutFileStream *_outFileStreamSpec;
223   CMyComPtr<ISequentialOutStream> _outFileStream;
224 
225 public:
226   void Init(IInArchive *archiveHandler, const FString &directoryPath);
227 
228   UInt64 NumErrors;
229   bool PasswordIsDefined;
230   UString Password;
231 
CArchiveExtractCallback()232   CArchiveExtractCallback() : PasswordIsDefined(false) {}
233 };
234 
Init(IInArchive * archiveHandler,const FString & directoryPath)235 void CArchiveExtractCallback::Init(IInArchive *archiveHandler, const FString &directoryPath)
236 {
237   NumErrors = 0;
238   _archiveHandler = archiveHandler;
239   _directoryPath = directoryPath;
240   NName::NormalizeDirPathPrefix(_directoryPath);
241 }
242 
SetTotal(UInt64)243 STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 /* size */)
244 {
245   return S_OK;
246 }
247 
SetCompleted(const UInt64 *)248 STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 * /* completeValue */)
249 {
250   return S_OK;
251 }
252 
GetStream(UInt32 index,ISequentialOutStream ** outStream,Int32 askExtractMode)253 STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index,
254     ISequentialOutStream **outStream, Int32 askExtractMode)
255 {
256   *outStream = 0;
257   _outFileStream.Release();
258 
259   {
260     // Get Name
261     NCOM::CPropVariant prop;
262     RINOK(_archiveHandler->GetProperty(index, kpidPath, &prop));
263 
264     UString fullPath;
265     if (prop.vt == VT_EMPTY)
266       fullPath = kEmptyFileAlias;
267     else
268     {
269       if (prop.vt != VT_BSTR)
270         return E_FAIL;
271       fullPath = prop.bstrVal;
272     }
273     _filePath = fullPath;
274   }
275 
276   if (askExtractMode != NArchive::NExtract::NAskMode::kExtract)
277     return S_OK;
278 
279   {
280     // Get Attrib
281     NCOM::CPropVariant prop;
282     RINOK(_archiveHandler->GetProperty(index, kpidAttrib, &prop));
283     if (prop.vt == VT_EMPTY)
284     {
285       _processedFileInfo.Attrib = 0;
286       _processedFileInfo.AttribDefined = false;
287     }
288     else
289     {
290       if (prop.vt != VT_UI4)
291         return E_FAIL;
292       _processedFileInfo.Attrib = prop.ulVal;
293       _processedFileInfo.AttribDefined = true;
294     }
295   }
296 
297   RINOK(IsArchiveItemFolder(_archiveHandler, index, _processedFileInfo.isDir));
298 
299   {
300     // Get Modified Time
301     NCOM::CPropVariant prop;
302     RINOK(_archiveHandler->GetProperty(index, kpidMTime, &prop));
303     _processedFileInfo.MTimeDefined = false;
304     switch(prop.vt)
305     {
306       case VT_EMPTY:
307         // _processedFileInfo.MTime = _utcMTimeDefault;
308         break;
309       case VT_FILETIME:
310         _processedFileInfo.MTime = prop.filetime;
311         _processedFileInfo.MTimeDefined = true;
312         break;
313       default:
314         return E_FAIL;
315     }
316 
317   }
318   {
319     // Get Size
320     NCOM::CPropVariant prop;
321     RINOK(_archiveHandler->GetProperty(index, kpidSize, &prop));
322     UInt64 newFileSize;
323     /* bool newFileSizeDefined = */ ConvertPropVariantToUInt64(prop, newFileSize);
324   }
325 
326 
327   {
328     // Create folders for file
329     int slashPos = _filePath.ReverseFind(WCHAR_PATH_SEPARATOR);
330     if (slashPos >= 0)
331       CreateComplexDir(_directoryPath + us2fs(_filePath.Left(slashPos)));
332   }
333 
334   FString fullProcessedPath = _directoryPath + us2fs(_filePath);
335   _diskFilePath = fullProcessedPath;
336 
337   if (_processedFileInfo.isDir)
338   {
339     CreateComplexDir(fullProcessedPath);
340   }
341   else
342   {
343     NFind::CFileInfo fi;
344     if (fi.Find(fullProcessedPath))
345     {
346       if (!DeleteFileAlways(fullProcessedPath))
347       {
348         PrintError("Can not delete output file", fullProcessedPath);
349         return E_ABORT;
350       }
351     }
352 
353     _outFileStreamSpec = new COutFileStream;
354     CMyComPtr<ISequentialOutStream> outStreamLoc(_outFileStreamSpec);
355     if (!_outFileStreamSpec->Open(fullProcessedPath, CREATE_ALWAYS))
356     {
357       PrintError("Can not open output file", fullProcessedPath);
358       return E_ABORT;
359     }
360     _outFileStream = outStreamLoc;
361     *outStream = outStreamLoc.Detach();
362   }
363   return S_OK;
364 }
365 
PrepareOperation(Int32 askExtractMode)366 STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode)
367 {
368   _extractMode = false;
369   switch (askExtractMode)
370   {
371     case NArchive::NExtract::NAskMode::kExtract:  _extractMode = true; break;
372   };
373   switch (askExtractMode)
374   {
375     case NArchive::NExtract::NAskMode::kExtract:  PrintString(kExtractingString); break;
376     case NArchive::NExtract::NAskMode::kTest:  PrintString(kTestingString); break;
377     case NArchive::NExtract::NAskMode::kSkip:  PrintString(kSkippingString); break;
378   };
379   PrintString(_filePath);
380   return S_OK;
381 }
382 
SetOperationResult(Int32 operationResult)383 STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult)
384 {
385   switch (operationResult)
386   {
387     case NArchive::NExtract::NOperationResult::kOK:
388       break;
389     default:
390     {
391       NumErrors++;
392       PrintString("  :  ");
393       const char *s = NULL;
394       switch (operationResult)
395       {
396         case NArchive::NExtract::NOperationResult::kUnsupportedMethod:
397           s = kUnsupportedMethod;
398           break;
399         case NArchive::NExtract::NOperationResult::kCRCError:
400           s = kCRCFailed;
401           break;
402         case NArchive::NExtract::NOperationResult::kDataError:
403           s = kDataError;
404           break;
405         case NArchive::NExtract::NOperationResult::kUnavailable:
406           s = kUnavailableData;
407           break;
408         case NArchive::NExtract::NOperationResult::kUnexpectedEnd:
409           s = kUnexpectedEnd;
410           break;
411         case NArchive::NExtract::NOperationResult::kDataAfterEnd:
412           s = kDataAfterEnd;
413           break;
414         case NArchive::NExtract::NOperationResult::kIsNotArc:
415           s = kIsNotArc;
416           break;
417         case NArchive::NExtract::NOperationResult::kHeadersError:
418           s = kHeadersError;
419           break;
420       }
421       if (s)
422       {
423         PrintString("Error : ");
424         PrintString(s);
425       }
426       else
427       {
428         char temp[16];
429         ConvertUInt32ToString(operationResult, temp);
430         PrintString("Error #");
431         PrintString(temp);
432       }
433     }
434   }
435 
436   if (_outFileStream != NULL)
437   {
438     if (_processedFileInfo.MTimeDefined)
439       _outFileStreamSpec->SetMTime(&_processedFileInfo.MTime);
440     RINOK(_outFileStreamSpec->Close());
441   }
442   _outFileStream.Release();
443   if (_extractMode && _processedFileInfo.AttribDefined)
444     SetFileAttrib(_diskFilePath, _processedFileInfo.Attrib);
445   PrintNewLine();
446   return S_OK;
447 }
448 
449 
CryptoGetTextPassword(BSTR * password)450 STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password)
451 {
452   if (!PasswordIsDefined)
453   {
454     // You can ask real password here from user
455     // Password = GetPassword(OutStream);
456     // PasswordIsDefined = true;
457     PrintError("Password is not defined");
458     return E_ABORT;
459   }
460   return StringToBstr(Password, password);
461 }
462 
463 
464 
465 //////////////////////////////////////////////////////////////
466 // Archive Creating callback class
467 
468 struct CDirItem
469 {
470   UInt64 Size;
471   FILETIME CTime;
472   FILETIME ATime;
473   FILETIME MTime;
474   UString Name;
475   FString FullPath;
476   UInt32 Attrib;
477 
isDirCDirItem478   bool isDir() const { return (Attrib & FILE_ATTRIBUTE_DIRECTORY) != 0 ; }
479 };
480 
481 class CArchiveUpdateCallback:
482   public IArchiveUpdateCallback2,
483   public ICryptoGetTextPassword2,
484   public CMyUnknownImp
485 {
486 public:
487   MY_UNKNOWN_IMP2(IArchiveUpdateCallback2, ICryptoGetTextPassword2)
488 
489   // IProgress
490   STDMETHOD(SetTotal)(UInt64 size);
491   STDMETHOD(SetCompleted)(const UInt64 *completeValue);
492 
493   // IUpdateCallback2
494   STDMETHOD(EnumProperties)(IEnumSTATPROPSTG **enumerator);
495   STDMETHOD(GetUpdateItemInfo)(UInt32 index,
496       Int32 *newData, Int32 *newProperties, UInt32 *indexInArchive);
497   STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value);
498   STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **inStream);
499   STDMETHOD(SetOperationResult)(Int32 operationResult);
500   STDMETHOD(GetVolumeSize)(UInt32 index, UInt64 *size);
501   STDMETHOD(GetVolumeStream)(UInt32 index, ISequentialOutStream **volumeStream);
502 
503   STDMETHOD(CryptoGetTextPassword2)(Int32 *passwordIsDefined, BSTR *password);
504 
505 public:
506   CRecordVector<UInt64> VolumesSizes;
507   UString VolName;
508   UString VolExt;
509 
510   FString DirPrefix;
511   const CObjectVector<CDirItem> *DirItems;
512 
513   bool PasswordIsDefined;
514   UString Password;
515   bool AskPassword;
516 
517   bool m_NeedBeClosed;
518 
519   FStringVector FailedFiles;
520   CRecordVector<HRESULT> FailedCodes;
521 
CArchiveUpdateCallback()522   CArchiveUpdateCallback(): PasswordIsDefined(false), AskPassword(false), DirItems(0) {};
523 
~CArchiveUpdateCallback()524   ~CArchiveUpdateCallback() { Finilize(); }
525   HRESULT Finilize();
526 
Init(const CObjectVector<CDirItem> * dirItems)527   void Init(const CObjectVector<CDirItem> *dirItems)
528   {
529     DirItems = dirItems;
530     m_NeedBeClosed = false;
531     FailedFiles.Clear();
532     FailedCodes.Clear();
533   }
534 };
535 
SetTotal(UInt64)536 STDMETHODIMP CArchiveUpdateCallback::SetTotal(UInt64 /* size */)
537 {
538   return S_OK;
539 }
540 
SetCompleted(const UInt64 *)541 STDMETHODIMP CArchiveUpdateCallback::SetCompleted(const UInt64 * /* completeValue */)
542 {
543   return S_OK;
544 }
545 
546 
EnumProperties(IEnumSTATPROPSTG **)547 STDMETHODIMP CArchiveUpdateCallback::EnumProperties(IEnumSTATPROPSTG ** /* enumerator */)
548 {
549   return E_NOTIMPL;
550 }
551 
GetUpdateItemInfo(UInt32,Int32 * newData,Int32 * newProperties,UInt32 * indexInArchive)552 STDMETHODIMP CArchiveUpdateCallback::GetUpdateItemInfo(UInt32 /* index */,
553       Int32 *newData, Int32 *newProperties, UInt32 *indexInArchive)
554 {
555   if (newData != NULL)
556     *newData = BoolToInt(true);
557   if (newProperties != NULL)
558     *newProperties = BoolToInt(true);
559   if (indexInArchive != NULL)
560     *indexInArchive = (UInt32)(Int32)-1;
561   return S_OK;
562 }
563 
GetProperty(UInt32 index,PROPID propID,PROPVARIANT * value)564 STDMETHODIMP CArchiveUpdateCallback::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
565 {
566   NCOM::CPropVariant prop;
567 
568   if (propID == kpidIsAnti)
569   {
570     prop = false;
571     prop.Detach(value);
572     return S_OK;
573   }
574 
575   {
576     const CDirItem &dirItem = (*DirItems)[index];
577     switch(propID)
578     {
579       case kpidPath:  prop = dirItem.Name; break;
580       case kpidIsDir:  prop = dirItem.isDir(); break;
581       case kpidSize:  prop = dirItem.Size; break;
582       case kpidAttrib:  prop = dirItem.Attrib; break;
583       case kpidCTime:  prop = dirItem.CTime; break;
584       case kpidATime:  prop = dirItem.ATime; break;
585       case kpidMTime:  prop = dirItem.MTime; break;
586     }
587   }
588   prop.Detach(value);
589   return S_OK;
590 }
591 
Finilize()592 HRESULT CArchiveUpdateCallback::Finilize()
593 {
594   if (m_NeedBeClosed)
595   {
596     PrintNewLine();
597     m_NeedBeClosed = false;
598   }
599   return S_OK;
600 }
601 
GetStream2(const wchar_t * name)602 static void GetStream2(const wchar_t *name)
603 {
604   PrintString("Compressing  ");
605   if (name[0] == 0)
606     name = kEmptyFileAlias;
607   PrintString(name);
608 }
609 
GetStream(UInt32 index,ISequentialInStream ** inStream)610 STDMETHODIMP CArchiveUpdateCallback::GetStream(UInt32 index, ISequentialInStream **inStream)
611 {
612   RINOK(Finilize());
613 
614   const CDirItem &dirItem = (*DirItems)[index];
615   GetStream2(dirItem.Name);
616 
617   if (dirItem.isDir())
618     return S_OK;
619 
620   {
621     CInFileStream *inStreamSpec = new CInFileStream;
622     CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec);
623     FString path = DirPrefix + dirItem.FullPath;
624     if (!inStreamSpec->Open(path))
625     {
626       DWORD sysError = ::GetLastError();
627       FailedCodes.Add(sysError);
628       FailedFiles.Add(path);
629       // if (systemError == ERROR_SHARING_VIOLATION)
630       {
631         PrintNewLine();
632         PrintError("WARNING: can't open file");
633         // PrintString(NError::MyFormatMessageW(systemError));
634         return S_FALSE;
635       }
636       // return sysError;
637     }
638     *inStream = inStreamLoc.Detach();
639   }
640   return S_OK;
641 }
642 
SetOperationResult(Int32)643 STDMETHODIMP CArchiveUpdateCallback::SetOperationResult(Int32 /* operationResult */)
644 {
645   m_NeedBeClosed = true;
646   return S_OK;
647 }
648 
GetVolumeSize(UInt32 index,UInt64 * size)649 STDMETHODIMP CArchiveUpdateCallback::GetVolumeSize(UInt32 index, UInt64 *size)
650 {
651   if (VolumesSizes.Size() == 0)
652     return S_FALSE;
653   if (index >= (UInt32)VolumesSizes.Size())
654     index = VolumesSizes.Size() - 1;
655   *size = VolumesSizes[index];
656   return S_OK;
657 }
658 
GetVolumeStream(UInt32 index,ISequentialOutStream ** volumeStream)659 STDMETHODIMP CArchiveUpdateCallback::GetVolumeStream(UInt32 index, ISequentialOutStream **volumeStream)
660 {
661   wchar_t temp[16];
662   ConvertUInt32ToString(index + 1, temp);
663   UString res = temp;
664   while (res.Len() < 2)
665     res = UString(L'0') + res;
666   UString fileName = VolName;
667   fileName += L'.';
668   fileName += res;
669   fileName += VolExt;
670   COutFileStream *streamSpec = new COutFileStream;
671   CMyComPtr<ISequentialOutStream> streamLoc(streamSpec);
672   if (!streamSpec->Create(us2fs(fileName), false))
673     return ::GetLastError();
674   *volumeStream = streamLoc.Detach();
675   return S_OK;
676 }
677 
CryptoGetTextPassword2(Int32 * passwordIsDefined,BSTR * password)678 STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password)
679 {
680   if (!PasswordIsDefined)
681   {
682     if (AskPassword)
683     {
684       // You can ask real password here from user
685       // Password = GetPassword(OutStream);
686       // PasswordIsDefined = true;
687       PrintError("Password is not defined");
688       return E_ABORT;
689     }
690   }
691   *passwordIsDefined = BoolToInt(PasswordIsDefined);
692   return StringToBstr(Password, password);
693 }
694 
695 //////////////////////////////////////////////////////////////////////////
696 // Main function
697 
698 #define NT_CHECK_FAIL_ACTION PrintError("Unsupported Windows version"); return 1;
699 
main(int numArgs,const char * args[])700 int MY_CDECL main(int numArgs, const char *args[])
701 {
702   NT_CHECK
703 
704   PrintStringLn(kCopyrightString);
705 
706   if (numArgs < 3)
707   {
708     PrintStringLn(kHelpString);
709     return 1;
710   }
711   NDLL::CLibrary lib;
712   if (!lib.Load(NDLL::GetModuleDirPrefix() + FTEXT(kDllName)))
713   {
714     PrintError("Can not load 7-zip library");
715     return 1;
716   }
717   Func_CreateObject createObjectFunc = (Func_CreateObject)lib.GetProc("CreateObject");
718   if (!createObjectFunc)
719   {
720     PrintError("Can not get CreateObject");
721     return 1;
722   }
723 
724   char c;
725   {
726     AString command = args[1];
727     if (command.Len() != 1)
728     {
729       PrintError("incorrect command");
730       return 1;
731     }
732     c = (char)MyCharLower_Ascii(command[0]);
733   }
734   FString archiveName = CmdStringToFString(args[2]);
735   if (c == 'a')
736   {
737     // create archive command
738     if (numArgs < 4)
739     {
740       PrintStringLn(kHelpString);
741       return 1;
742     }
743     CObjectVector<CDirItem> dirItems;
744     {
745       int i;
746       for (i = 3; i < numArgs; i++)
747       {
748         CDirItem di;
749         FString name = CmdStringToFString(args[i]);
750 
751         NFind::CFileInfo fi;
752         if (!fi.Find(name))
753         {
754           PrintError("Can't find file", name);
755           return 1;
756         }
757 
758         di.Attrib = fi.Attrib;
759         di.Size = fi.Size;
760         di.CTime = fi.CTime;
761         di.ATime = fi.ATime;
762         di.MTime = fi.MTime;
763         di.Name = fs2us(name);
764         di.FullPath = name;
765         dirItems.Add(di);
766       }
767     }
768     COutFileStream *outFileStreamSpec = new COutFileStream;
769     CMyComPtr<IOutStream> outFileStream = outFileStreamSpec;
770     if (!outFileStreamSpec->Create(archiveName, false))
771     {
772       PrintError("can't create archive file");
773       return 1;
774     }
775 
776     CMyComPtr<IOutArchive> outArchive;
777     if (createObjectFunc(&CLSID_CFormat7z, &IID_IOutArchive, (void **)&outArchive) != S_OK)
778     {
779       PrintError("Can not get class object");
780       return 1;
781     }
782 
783     CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback;
784     CMyComPtr<IArchiveUpdateCallback2> updateCallback(updateCallbackSpec);
785     updateCallbackSpec->Init(&dirItems);
786     // updateCallbackSpec->PasswordIsDefined = true;
787     // updateCallbackSpec->Password = L"1";
788 
789     /*
790     {
791       const wchar_t *names[] =
792       {
793         L"s",
794         L"x"
795       };
796       const unsigned kNumProps = ARRAY_SIZE(names);
797       NCOM::CPropVariant values[kNumProps] =
798       {
799         false,    // solid mode OFF
800         (UInt32)9 // compression level = 9 - ultra
801       };
802       CMyComPtr<ISetProperties> setProperties;
803       outArchive->QueryInterface(IID_ISetProperties, (void **)&setProperties);
804       if (!setProperties)
805       {
806         PrintError("ISetProperties unsupported");
807         return 1;
808       }
809       RINOK(setProperties->SetProperties(names, values, kNumProps));
810     }
811     */
812 
813     HRESULT result = outArchive->UpdateItems(outFileStream, dirItems.Size(), updateCallback);
814     updateCallbackSpec->Finilize();
815     if (result != S_OK)
816     {
817       PrintError("Update Error");
818       return 1;
819     }
820     FOR_VECTOR (i, updateCallbackSpec->FailedFiles)
821     {
822       PrintNewLine();
823       PrintError("Error for file", updateCallbackSpec->FailedFiles[i]);
824     }
825     if (updateCallbackSpec->FailedFiles.Size() != 0)
826       return 1;
827   }
828   else
829   {
830     if (numArgs != 3)
831     {
832       PrintStringLn(kHelpString);
833       return 1;
834     }
835 
836     bool listCommand;
837     if (c == 'l')
838       listCommand = true;
839     else if (c == 'x')
840       listCommand = false;
841     else
842     {
843       PrintError("incorrect command");
844       return 1;
845     }
846 
847     CMyComPtr<IInArchive> archive;
848     if (createObjectFunc(&CLSID_CFormat7z, &IID_IInArchive, (void **)&archive) != S_OK)
849     {
850       PrintError("Can not get class object");
851       return 1;
852     }
853 
854     CInFileStream *fileSpec = new CInFileStream;
855     CMyComPtr<IInStream> file = fileSpec;
856 
857     if (!fileSpec->Open(archiveName))
858     {
859       PrintError("Can not open archive file", archiveName);
860       return 1;
861     }
862 
863     {
864       CArchiveOpenCallback *openCallbackSpec = new CArchiveOpenCallback;
865       CMyComPtr<IArchiveOpenCallback> openCallback(openCallbackSpec);
866       openCallbackSpec->PasswordIsDefined = false;
867       // openCallbackSpec->PasswordIsDefined = true;
868       // openCallbackSpec->Password = L"1";
869 
870       if (archive->Open(file, 0, openCallback) != S_OK)
871       {
872         PrintError("Can not open file as archive", archiveName);
873         return 1;
874       }
875     }
876 
877     if (listCommand)
878     {
879       // List command
880       UInt32 numItems = 0;
881       archive->GetNumberOfItems(&numItems);
882       for (UInt32 i = 0; i < numItems; i++)
883       {
884         {
885           // Get uncompressed size of file
886           NCOM::CPropVariant prop;
887           archive->GetProperty(i, kpidSize, &prop);
888           char s[32];
889           ConvertPropVariantToShortString(prop, s);
890           PrintString(s);
891           PrintString("  ");
892         }
893         {
894           // Get name of file
895           NCOM::CPropVariant prop;
896           archive->GetProperty(i, kpidPath, &prop);
897           if (prop.vt == VT_BSTR)
898             PrintString(prop.bstrVal);
899           else if (prop.vt != VT_EMPTY)
900             PrintString("ERROR!");
901         }
902         PrintNewLine();
903       }
904     }
905     else
906     {
907       // Extract command
908       CArchiveExtractCallback *extractCallbackSpec = new CArchiveExtractCallback;
909       CMyComPtr<IArchiveExtractCallback> extractCallback(extractCallbackSpec);
910       extractCallbackSpec->Init(archive, FTEXT("")); // second parameter is output folder path
911       extractCallbackSpec->PasswordIsDefined = false;
912       // extractCallbackSpec->PasswordIsDefined = true;
913       // extractCallbackSpec->Password = L"1";
914       HRESULT result = archive->Extract(NULL, (UInt32)(Int32)(-1), false, extractCallback);
915       if (result != S_OK)
916       {
917         PrintError("Extract Error");
918         return 1;
919       }
920     }
921   }
922   return 0;
923 }
924