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