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