5 #include "Common/MyInitGuid.h"
6 #include "Common/StringConvert.h"
7 #include "Common/IntToString.h"
9 #include "Windows/PropVariant.h"
10 #include "Windows/PropVariantConversions.h"
11 #include "Windows/DLL.h"
12 #include "Windows/FileDir.h"
13 #include "Windows/FileName.h"
14 #include "Windows/FileFind.h"
16 #include "../../Common/FileStreams.h"
17 #include "../../Archive/IArchive.h"
18 #include "../../IPassword.h"
19 #include "../../MyVersion.h"
22 // {23170F69-40C1-278A-1000-000110070000}
23 DEFINE_GUID(CLSID_CFormat7z,
24 0x23170F69, 0x40C1, 0x278A, 0x10, 0x00, 0x00, 0x01, 0x10, 0x07, 0x00, 0x00);
26 using namespace NWindows;
28 #define kDllName "7z.dll"
30 static const char *kCopyrightString = MY_7ZIP_VERSION
31 " (" kDllName " client) "
32 MY_COPYRIGHT " " MY_DATE;
34 static const char *kHelpString =
35 "Usage: Client7z.exe [a | l | x ] archive.7z [fileName ...]\n"
37 " Client7z.exe a archive.7z f1.txt f2.txt : compress two files to archive.7z\n"
38 " Client7z.exe l archive.7z : List contents of archive.7z\n"
39 " Client7z.exe x archive.7z : eXtract files from archive.7z\n";
42 typedef UINT32 (WINAPI * CreateObjectFunc)(
44 const GUID *interfaceID,
50 static inline bool IsItWindowsNT()
52 OSVERSIONINFO versionInfo;
53 versionInfo.dwOSVersionInfoSize = sizeof(versionInfo);
54 if (!::GetVersionEx(&versionInfo))
56 return (versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT);
61 void PrintString(const UString &s)
63 printf("%s", (LPCSTR)GetOemString(s));
66 void PrintString(const AString &s)
68 printf("%s", (LPCSTR)s);
76 void PrintStringLn(const AString &s)
82 void PrintError(const AString &s)
89 static HRESULT IsArchiveItemProp(IInArchive *archive, UInt32 index, PROPID propID, bool &result)
91 NCOM::CPropVariant prop;
92 RINOK(archive->GetProperty(index, propID, &prop));
93 if(prop.vt == VT_BOOL)
94 result = VARIANT_BOOLToBool(prop.boolVal);
95 else if (prop.vt == VT_EMPTY)
102 static HRESULT IsArchiveItemFolder(IInArchive *archive, UInt32 index, bool &result)
104 return IsArchiveItemProp(archive, index, kpidIsFolder, result);
108 static const wchar_t *kEmptyFileAlias = L"[Content]";
111 //////////////////////////////////////////////////////////////
112 // Archive Open callback class
115 class CArchiveOpenCallback:
116 public IArchiveOpenCallback,
117 public ICryptoGetTextPassword,
121 MY_UNKNOWN_IMP1(ICryptoGetTextPassword)
123 STDMETHOD(SetTotal)(const UInt64 *files, const UInt64 *bytes);
124 STDMETHOD(SetCompleted)(const UInt64 *files, const UInt64 *bytes);
126 STDMETHOD(CryptoGetTextPassword)(BSTR *password);
128 bool PasswordIsDefined;
131 CArchiveOpenCallback() : PasswordIsDefined(false) {}
134 STDMETHODIMP CArchiveOpenCallback::SetTotal(const UInt64 * /* files */, const UInt64 * /* bytes */)
139 STDMETHODIMP CArchiveOpenCallback::SetCompleted(const UInt64 * /* files */, const UInt64 * /* bytes */)
144 STDMETHODIMP CArchiveOpenCallback::CryptoGetTextPassword(BSTR *password)
146 if (!PasswordIsDefined)
148 // You can ask real password here from user
149 // Password = GetPassword(OutStream);
150 // PasswordIsDefined = true;
151 PrintError("Password is not defined");
154 CMyComBSTR tempName(Password);
155 *password = tempName.Detach();
160 //////////////////////////////////////////////////////////////
161 // Archive Extracting callback class
163 static const wchar_t *kCantDeleteOutputFile = L"ERROR: Can not delete output file ";
165 static const char *kTestingString = "Testing ";
166 static const char *kExtractingString = "Extracting ";
167 static const char *kSkippingString = "Skipping ";
169 static const char *kUnsupportedMethod = "Unsupported Method";
170 static const char *kCRCFailed = "CRC Failed";
171 static const char *kDataError = "Data Error";
172 static const char *kUnknownError = "Unknown Error";
174 class CArchiveExtractCallback:
175 public IArchiveExtractCallback,
176 public ICryptoGetTextPassword,
180 MY_UNKNOWN_IMP1(ICryptoGetTextPassword)
183 STDMETHOD(SetTotal)(UInt64 size);
184 STDMETHOD(SetCompleted)(const UInt64 *completeValue);
186 // IArchiveExtractCallback
187 STDMETHOD(GetStream)(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode);
188 STDMETHOD(PrepareOperation)(Int32 askExtractMode);
189 STDMETHOD(SetOperationResult)(Int32 resultEOperationResult);
191 // ICryptoGetTextPassword
192 STDMETHOD(CryptoGetTextPassword)(BSTR *aPassword);
195 CMyComPtr<IInArchive> _archiveHandler;
196 UString _directoryPath; // Output directory
197 UString _filePath; // name inside arcvhive
198 UString _diskFilePath; // full path to file on disk
200 struct CProcessedFileInfo
202 FILETIME UTCLastWriteTime;
205 bool AttributesAreDefined;
206 bool UTCLastWriteTimeIsDefined;
207 } _processedFileInfo;
209 COutFileStream *_outFileStreamSpec;
210 CMyComPtr<ISequentialOutStream> _outFileStream;
213 void Init(IInArchive *archiveHandler, const UString &directoryPath);
216 bool PasswordIsDefined;
219 CArchiveExtractCallback() : PasswordIsDefined(false) {}
222 void CArchiveExtractCallback::Init(IInArchive *archiveHandler, const UString &directoryPath)
225 _archiveHandler = archiveHandler;
226 _directoryPath = directoryPath;
227 NFile::NName::NormalizeDirPathPrefix(_directoryPath);
230 STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 /* size */)
235 STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 * /* completeValue */)
240 STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index,
241 ISequentialOutStream **outStream, Int32 askExtractMode)
244 _outFileStream.Release();
248 NCOM::CPropVariant propVariant;
249 RINOK(_archiveHandler->GetProperty(index, kpidPath, &propVariant));
252 if(propVariant.vt == VT_EMPTY)
253 fullPath = kEmptyFileAlias;
256 if(propVariant.vt != VT_BSTR)
258 fullPath = propVariant.bstrVal;
260 _filePath = fullPath;
263 if (askExtractMode != NArchive::NExtract::NAskMode::kExtract)
268 NCOM::CPropVariant propVariant;
269 RINOK(_archiveHandler->GetProperty(index, kpidAttributes, &propVariant));
270 if (propVariant.vt == VT_EMPTY)
272 _processedFileInfo.Attributes = 0;
273 _processedFileInfo.AttributesAreDefined = false;
277 if (propVariant.vt != VT_UI4)
278 throw "incorrect item";
279 _processedFileInfo.Attributes = propVariant.ulVal;
280 _processedFileInfo.AttributesAreDefined = true;
284 RINOK(IsArchiveItemFolder(_archiveHandler, index, _processedFileInfo.IsDirectory));
288 NCOM::CPropVariant propVariant;
289 RINOK(_archiveHandler->GetProperty(index, kpidLastWriteTime, &propVariant));
290 _processedFileInfo.UTCLastWriteTimeIsDefined = false;
291 switch(propVariant.vt)
294 // _processedFileInfo.UTCLastWriteTime = _utcLastWriteTimeDefault;
297 _processedFileInfo.UTCLastWriteTime = propVariant.filetime;
298 _processedFileInfo.UTCLastWriteTimeIsDefined = true;
307 NCOM::CPropVariant propVariant;
308 RINOK(_archiveHandler->GetProperty(index, kpidSize, &propVariant));
309 bool newFileSizeDefined = (propVariant.vt != VT_EMPTY);
311 if (newFileSizeDefined)
312 newFileSize = ConvertPropVariantToUInt64(propVariant);
317 // Create folders for file
318 int slashPos = _filePath.ReverseFind(WCHAR_PATH_SEPARATOR);
320 NFile::NDirectory::CreateComplexDirectory(_directoryPath + _filePath.Left(slashPos));
323 UString fullProcessedPath = _directoryPath + _filePath;
324 _diskFilePath = fullProcessedPath;
326 if (_processedFileInfo.IsDirectory)
328 NFile::NDirectory::CreateComplexDirectory(fullProcessedPath);
332 NFile::NFind::CFileInfoW fileInfo;
333 if(NFile::NFind::FindFile(fullProcessedPath, fileInfo))
335 if (!NFile::NDirectory::DeleteFileAlways(fullProcessedPath))
337 PrintString(UString(kCantDeleteOutputFile) + fullProcessedPath);
342 _outFileStreamSpec = new COutFileStream;
343 CMyComPtr<ISequentialOutStream> outStreamLoc(_outFileStreamSpec);
344 if (!_outFileStreamSpec->Open(fullProcessedPath, CREATE_ALWAYS))
346 PrintString((UString)L"can not open output file " + fullProcessedPath);
349 _outFileStream = outStreamLoc;
350 *outStream = outStreamLoc.Detach();
355 STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode)
357 _extractMode = false;
358 switch (askExtractMode)
360 case NArchive::NExtract::NAskMode::kExtract:
363 switch (askExtractMode)
365 case NArchive::NExtract::NAskMode::kExtract:
366 PrintString(kExtractingString);
368 case NArchive::NExtract::NAskMode::kTest:
369 PrintString(kTestingString);
371 case NArchive::NExtract::NAskMode::kSkip:
372 PrintString(kSkippingString);
375 PrintString(_filePath);
379 STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult)
381 switch(operationResult)
383 case NArchive::NExtract::NOperationResult::kOK:
389 switch(operationResult)
391 case NArchive::NExtract::NOperationResult::kUnSupportedMethod:
392 PrintString(kUnsupportedMethod);
394 case NArchive::NExtract::NOperationResult::kCRCError:
395 PrintString(kCRCFailed);
397 case NArchive::NExtract::NOperationResult::kDataError:
398 PrintString(kDataError);
401 PrintString(kUnknownError);
406 if (_outFileStream != NULL)
408 if (_processedFileInfo.UTCLastWriteTimeIsDefined)
409 _outFileStreamSpec->SetLastWriteTime(&_processedFileInfo.UTCLastWriteTime);
410 RINOK(_outFileStreamSpec->Close());
412 _outFileStream.Release();
413 if (_extractMode && _processedFileInfo.AttributesAreDefined)
414 NFile::NDirectory::MySetFileAttributes(_diskFilePath, _processedFileInfo.Attributes);
420 STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password)
422 if (!PasswordIsDefined)
424 // You can ask real password here from user
425 // Password = GetPassword(OutStream);
426 // PasswordIsDefined = true;
427 PrintError("Password is not defined");
430 CMyComBSTR tempName(Password);
431 *password = tempName.Detach();
437 //////////////////////////////////////////////////////////////
438 // Archive Creating callback class
443 FILETIME CreationTime;
444 FILETIME LastAccessTime;
445 FILETIME LastWriteTime;
449 bool IsDirectory() const { return (Attributes & FILE_ATTRIBUTE_DIRECTORY) != 0 ; }
452 class CArchiveUpdateCallback:
453 public IArchiveUpdateCallback2,
454 public ICryptoGetTextPassword2,
458 MY_UNKNOWN_IMP2(IArchiveUpdateCallback2, ICryptoGetTextPassword2)
461 STDMETHOD(SetTotal)(UInt64 size);
462 STDMETHOD(SetCompleted)(const UInt64 *completeValue);
465 STDMETHOD(EnumProperties)(IEnumSTATPROPSTG **enumerator);
466 STDMETHOD(GetUpdateItemInfo)(UInt32 index,
467 Int32 *newData, Int32 *newProperties, UInt32 *indexInArchive);
468 STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value);
469 STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **inStream);
470 STDMETHOD(SetOperationResult)(Int32 operationResult);
471 STDMETHOD(GetVolumeSize)(UInt32 index, UInt64 *size);
472 STDMETHOD(GetVolumeStream)(UInt32 index, ISequentialOutStream **volumeStream);
474 STDMETHOD(CryptoGetTextPassword2)(Int32 *passwordIsDefined, BSTR *password);
477 CRecordVector<UInt64> VolumesSizes;
482 const CObjectVector<CDirItem> *DirItems;
484 bool PasswordIsDefined;
490 UStringVector FailedFiles;
491 CRecordVector<HRESULT> FailedCodes;
493 CArchiveUpdateCallback(): PasswordIsDefined(false), AskPassword(false), DirItems(0) {};
495 ~CArchiveUpdateCallback() { Finilize(); }
498 void Init(const CObjectVector<CDirItem> *dirItems)
501 m_NeedBeClosed = false;
507 STDMETHODIMP CArchiveUpdateCallback::SetTotal(UInt64 /* size */)
512 STDMETHODIMP CArchiveUpdateCallback::SetCompleted(const UInt64 * /* completeValue */)
518 STDMETHODIMP CArchiveUpdateCallback::EnumProperties(IEnumSTATPROPSTG ** /* enumerator */)
523 STDMETHODIMP CArchiveUpdateCallback::GetUpdateItemInfo(UInt32 /* index */,
524 Int32 *newData, Int32 *newProperties, UInt32 *indexInArchive)
527 *newData = BoolToInt(true);
528 if(newProperties != NULL)
529 *newProperties = BoolToInt(true);
530 if(indexInArchive != NULL)
531 *indexInArchive = UInt32(-1);
535 STDMETHODIMP CArchiveUpdateCallback::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
537 NWindows::NCOM::CPropVariant propVariant;
539 if (propID == kpidIsAnti)
542 propVariant.Detach(value);
547 const CDirItem &dirItem = (*DirItems)[index];
551 propVariant = dirItem.Name;
554 propVariant = dirItem.IsDirectory();
557 propVariant = dirItem.Size;
560 propVariant = dirItem.Attributes;
562 case kpidLastAccessTime:
563 propVariant = dirItem.LastAccessTime;
565 case kpidCreationTime:
566 propVariant = dirItem.CreationTime;
568 case kpidLastWriteTime:
569 propVariant = dirItem.LastWriteTime;
573 propVariant.Detach(value);
577 HRESULT CArchiveUpdateCallback::Finilize()
582 m_NeedBeClosed = false;
587 static void GetStream2(const wchar_t *name)
589 PrintString("Compressing ");
591 name = kEmptyFileAlias;
595 STDMETHODIMP CArchiveUpdateCallback::GetStream(UInt32 index, ISequentialInStream **inStream)
599 const CDirItem &dirItem = (*DirItems)[index];
600 GetStream2(dirItem.Name);
602 if(dirItem.IsDirectory())
606 CInFileStream *inStreamSpec = new CInFileStream;
607 CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec);
608 UString path = DirPrefix + dirItem.FullPath;
609 if(!inStreamSpec->Open(path))
611 DWORD sysError = ::GetLastError();
612 FailedCodes.Add(sysError);
613 FailedFiles.Add(path);
614 // if (systemError == ERROR_SHARING_VIOLATION)
617 PrintError("WARNING: can't open file");
618 // PrintString(NError::MyFormatMessageW(systemError));
623 *inStream = inStreamLoc.Detach();
628 STDMETHODIMP CArchiveUpdateCallback::SetOperationResult(Int32 /* operationResult */)
630 m_NeedBeClosed = true;
634 STDMETHODIMP CArchiveUpdateCallback::GetVolumeSize(UInt32 index, UInt64 *size)
636 if (VolumesSizes.Size() == 0)
638 if (index >= (UInt32)VolumesSizes.Size())
639 index = VolumesSizes.Size() - 1;
640 *size = VolumesSizes[index];
644 STDMETHODIMP CArchiveUpdateCallback::GetVolumeStream(UInt32 index, ISequentialOutStream **volumeStream)
647 ConvertUInt64ToString(index + 1, temp);
649 while (res.Length() < 2)
650 res = UString(L'0') + res;
651 UString fileName = VolName;
655 COutFileStream *streamSpec = new COutFileStream;
656 CMyComPtr<ISequentialOutStream> streamLoc(streamSpec);
657 if(!streamSpec->Create(fileName, false))
658 return ::GetLastError();
659 *volumeStream = streamLoc.Detach();
663 STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password)
665 if (!PasswordIsDefined)
669 // You can ask real password here from user
670 // Password = GetPassword(OutStream);
671 // PasswordIsDefined = true;
672 PrintError("Password is not defined");
676 *passwordIsDefined = BoolToInt(PasswordIsDefined);
677 CMyComBSTR tempName(Password);
678 *password = tempName.Detach();
684 //////////////////////////////////////////////////////////////////////////
691 main(int argc, char* argv[])
695 g_IsNT = IsItWindowsNT();
699 PrintStringLn(kCopyrightString);
703 PrintStringLn(kHelpString);
706 NWindows::NDLL::CLibrary library;
707 if (!library.Load(TEXT(kDllName)))
709 PrintError("Can not load library");
712 CreateObjectFunc createObjectFunc = (CreateObjectFunc)library.GetProcAddress("CreateObject");
713 if (createObjectFunc == 0)
715 PrintError("Can not get CreateObject");
719 AString command = argv[1];
720 UString archiveName = GetUnicodeString(argv[2], CP_OEMCP);
721 if (command.CompareNoCase("a") == 0)
723 // create archive command
726 PrintStringLn(kHelpString);
729 CObjectVector<CDirItem> dirItems;
731 for (i = 3; i < argc; i++)
734 UString name = GetUnicodeString(argv[i], CP_OEMCP);
736 NFile::NFind::CFileInfoW fileInfo;
737 if (!NFile::NFind::FindFile(name, fileInfo))
739 PrintString(UString(L"Can't find file") + name);
743 item.Attributes = fileInfo.Attributes;
744 item.Size = fileInfo.Size;
745 item.CreationTime = fileInfo.CreationTime;
746 item.LastAccessTime = fileInfo.LastAccessTime;
747 item.LastWriteTime = fileInfo.LastWriteTime;
749 item.FullPath = name;
752 COutFileStream *outFileStreamSpec = new COutFileStream;
753 CMyComPtr<IOutStream> outFileStream = outFileStreamSpec;
754 if (!outFileStreamSpec->Create(archiveName, false))
756 PrintError("can't create archive file");
760 CMyComPtr<IOutArchive> outArchive;
761 if (createObjectFunc(&CLSID_CFormat7z, &IID_IOutArchive, (void **)&outArchive) != S_OK)
763 PrintError("Can not get class object");
767 CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback;
768 CMyComPtr<IArchiveUpdateCallback2> updateCallback(updateCallbackSpec);
769 updateCallbackSpec->Init(&dirItems);
770 // updateCallbackSpec->PasswordIsDefined = true;
771 // updateCallbackSpec->Password = L"1";
773 HRESULT result = outArchive->UpdateItems(outFileStream, dirItems.Size(), updateCallback);
774 updateCallbackSpec->Finilize();
777 PrintError("Update Error");
780 for (i = 0; i < updateCallbackSpec->FailedFiles.Size(); i++)
783 PrintString((UString)L"Error for file: " + updateCallbackSpec->FailedFiles[i]);
785 if (updateCallbackSpec->FailedFiles.Size() != 0)
792 PrintStringLn(kHelpString);
797 if (command.CompareNoCase("l") == 0)
799 else if (command.CompareNoCase("x") == 0)
803 PrintError("incorrect command");
807 CMyComPtr<IInArchive> archive;
808 if (createObjectFunc(&CLSID_CFormat7z, &IID_IInArchive, (void **)&archive) != S_OK)
810 PrintError("Can not get class object");
814 CInFileStream *fileSpec = new CInFileStream;
815 CMyComPtr<IInStream> file = fileSpec;
817 if (!fileSpec->Open(archiveName))
819 PrintError("Can not open archive file");
824 CArchiveOpenCallback *openCallbackSpec = new CArchiveOpenCallback;
825 CMyComPtr<IArchiveOpenCallback> openCallback(openCallbackSpec);
826 openCallbackSpec->PasswordIsDefined = false;
827 // openCallbackSpec->PasswordIsDefined = true;
828 // openCallbackSpec->Password = L"1";
830 if (archive->Open(file, 0, openCallback) != S_OK)
832 PrintError("Can not open archive");
841 archive->GetNumberOfItems(&numItems);
842 for (UInt32 i = 0; i < numItems; i++)
845 // Get uncompressed size of file
846 NWindows::NCOM::CPropVariant propVariant;
847 archive->GetProperty(i, kpidSize, &propVariant);
848 UString s = ConvertPropVariantToString(propVariant);
854 NWindows::NCOM::CPropVariant propVariant;
855 archive->GetProperty(i, kpidPath, &propVariant);
856 UString s = ConvertPropVariantToString(propVariant);
865 CArchiveExtractCallback *extractCallbackSpec = new CArchiveExtractCallback;
866 CMyComPtr<IArchiveExtractCallback> extractCallback(extractCallbackSpec);
867 extractCallbackSpec->Init(archive, L""); // second parameter is output folder path
868 extractCallbackSpec->PasswordIsDefined = false;
869 // extractCallbackSpec->PasswordIsDefined = true;
870 // extractCallbackSpec->Password = L"1";
871 HRESULT result = archive->Extract(NULL, (UInt32)(Int32)(-1), false, extractCallback);
874 PrintError("Extract Error");