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