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