tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Client7z.cpp (25838B)


      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 
     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 
     83 static FString CmdStringToFString(const char *s)
     84 {
     85  return us2fs(GetUnicodeString(s));
     86 }
     87 
     88 static void Print(const char *s)
     89 {
     90  fputs(s, stdout);
     91 }
     92 
     93 static void Print(const AString &s)
     94 {
     95  Print(s.Ptr());
     96 }
     97 
     98 static void Print(const UString &s)
     99 {
    100  AString as;
    101  Convert_UString_to_AString(s, as);
    102  Print(as);
    103 }
    104 
    105 static void Print(const wchar_t *s)
    106 {
    107  Print(UString(s));
    108 }
    109 
    110 static void PrintNewLine()
    111 {
    112  Print("\n");
    113 }
    114 
    115 static void PrintStringLn(const char *s)
    116 {
    117  Print(s);
    118  PrintNewLine();
    119 }
    120 
    121 static void PrintError(const char *message)
    122 {
    123  Print("Error: ");
    124  PrintNewLine();
    125  Print(message);
    126  PrintNewLine();
    127 }
    128 
    129 static void PrintError(const char *message, const FString &name)
    130 {
    131  PrintError(message);
    132  Print(name);
    133 }
    134 
    135 
    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 
    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 
    178  CArchiveOpenCallback() : PasswordIsDefined(false) {}
    179 };
    180 
    181 STDMETHODIMP CArchiveOpenCallback::SetTotal(const UInt64 * /* files */, const UInt64 * /* bytes */)
    182 {
    183  return S_OK;
    184 }
    185 
    186 STDMETHODIMP CArchiveOpenCallback::SetCompleted(const UInt64 * /* files */, const UInt64 * /* bytes */)
    187 {
    188  return S_OK;
    189 }
    190  
    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 
    270  CArchiveExtractCallback() : PasswordIsDefined(false) {}
    271 };
    272 
    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 
    281 STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 /* size */)
    282 {
    283  return S_OK;
    284 }
    285 
    286 STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 * /* completeValue */)
    287 {
    288  return S_OK;
    289 }
    290 
    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 
    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 
    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 
    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 
    516  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 
    559  CArchiveUpdateCallback(): PasswordIsDefined(false), AskPassword(false), DirItems(0) {};
    560 
    561  ~CArchiveUpdateCallback() { Finilize(); }
    562  HRESULT Finilize();
    563 
    564  void Init(const CObjectVector<CDirItem> *dirItems)
    565  {
    566    DirItems = dirItems;
    567    m_NeedBeClosed = false;
    568    FailedFiles.Clear();
    569    FailedCodes.Clear();
    570  }
    571 };
    572 
    573 STDMETHODIMP CArchiveUpdateCallback::SetTotal(UInt64 /* size */)
    574 {
    575  return S_OK;
    576 }
    577 
    578 STDMETHODIMP CArchiveUpdateCallback::SetCompleted(const UInt64 * /* completeValue */)
    579 {
    580  return S_OK;
    581 }
    582 
    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 
    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 
    623 HRESULT CArchiveUpdateCallback::Finilize()
    624 {
    625  if (m_NeedBeClosed)
    626  {
    627    PrintNewLine();
    628    m_NeedBeClosed = false;
    629  }
    630  return S_OK;
    631 }
    632 
    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 
    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 
    674 STDMETHODIMP CArchiveUpdateCallback::SetOperationResult(Int32 /* operationResult */)
    675 {
    676  m_NeedBeClosed = true;
    677  return S_OK;
    678 }
    679 
    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 
    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 
    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 
    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 }