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