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