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