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