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