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