tor-browser

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

SfxSetup.c (15375B)


      1 /* SfxSetup.c - 7z SFX Setup
      2 2017-04-04 : Igor Pavlov : Public domain */
      3 
      4 #include "Precomp.h"
      5 
      6 #ifndef UNICODE
      7 #define UNICODE
      8 #endif
      9 
     10 #ifndef _UNICODE
     11 #define _UNICODE
     12 #endif
     13 
     14 #ifdef _CONSOLE
     15 #include <stdio.h>
     16 #endif
     17 
     18 #include "../../7z.h"
     19 #include "../../7zAlloc.h"
     20 #include "../../7zCrc.h"
     21 #include "../../7zFile.h"
     22 #include "../../CpuArch.h"
     23 #include "../../DllSecur.h"
     24 
     25 #define k_EXE_ExtIndex 2
     26 
     27 #define kInputBufSize ((size_t)1 << 18)
     28 
     29 static const char * const kExts[] =
     30 {
     31    "bat"
     32  , "cmd"
     33  , "exe"
     34  , "inf"
     35  , "msi"
     36  #ifdef UNDER_CE
     37  , "cab"
     38  #endif
     39  , "html"
     40  , "htm"
     41 };
     42 
     43 static const char * const kNames[] =
     44 {
     45    "setup"
     46  , "install"
     47  , "run"
     48  , "start"
     49 };
     50 
     51 static unsigned FindExt(const wchar_t *s, unsigned *extLen)
     52 {
     53  unsigned len = (unsigned)wcslen(s);
     54  unsigned i;
     55  for (i = len; i > 0; i--)
     56  {
     57    if (s[i - 1] == '.')
     58    {
     59      *extLen = len - i;
     60      return i - 1;
     61    }
     62  }
     63  *extLen = 0;
     64  return len;
     65 }
     66 
     67 #define MAKE_CHAR_UPPER(c) ((((c) >= 'a' && (c) <= 'z') ? (c) -= 0x20 : (c)))
     68 
     69 static unsigned FindItem(const char * const *items, unsigned num, const wchar_t *s, unsigned len)
     70 {
     71  unsigned i;
     72  for (i = 0; i < num; i++)
     73  {
     74    const char *item = items[i];
     75    unsigned itemLen = (unsigned)strlen(item);
     76    unsigned j;
     77    if (len != itemLen)
     78      continue;
     79    for (j = 0; j < len; j++)
     80    {
     81      unsigned c = (Byte)item[j];
     82      if (c != s[j] && MAKE_CHAR_UPPER(c) != s[j])
     83        break;
     84    }
     85    if (j == len)
     86      return i;
     87  }
     88  return i;
     89 }
     90 
     91 #ifdef _CONSOLE
     92 static BOOL WINAPI HandlerRoutine(DWORD ctrlType)
     93 {
     94  UNUSED_VAR(ctrlType);
     95  return TRUE;
     96 }
     97 #endif
     98 
     99 static void PrintErrorMessage(const char *message)
    100 {
    101  #ifdef _CONSOLE
    102  printf("\n7-Zip Error: %s\n", message);
    103  #else
    104  #ifdef UNDER_CE
    105  WCHAR messageW[256 + 4];
    106  unsigned i;
    107  for (i = 0; i < 256 && message[i] != 0; i++)
    108    messageW[i] = message[i];
    109  messageW[i] = 0;
    110  MessageBoxW(0, messageW, L"7-Zip Error", MB_ICONERROR);
    111  #else
    112  MessageBoxA(0, message, "7-Zip Error", MB_ICONERROR);
    113  #endif
    114  #endif
    115 }
    116 
    117 static WRes MyCreateDir(const WCHAR *name)
    118 {
    119  return CreateDirectoryW(name, NULL) ? 0 : GetLastError();
    120 }
    121 
    122 #ifdef UNDER_CE
    123 #define kBufferSize (1 << 13)
    124 #else
    125 #define kBufferSize (1 << 15)
    126 #endif
    127 
    128 #define kSignatureSearchLimit (1 << 22)
    129 
    130 static Bool FindSignature(CSzFile *stream, UInt64 *resPos)
    131 {
    132  Byte buf[kBufferSize];
    133  size_t numPrevBytes = 0;
    134  *resPos = 0;
    135  for (;;)
    136  {
    137    size_t processed, pos;
    138    if (*resPos > kSignatureSearchLimit)
    139      return False;
    140    processed = kBufferSize - numPrevBytes;
    141    if (File_Read(stream, buf + numPrevBytes, &processed) != 0)
    142      return False;
    143    processed += numPrevBytes;
    144    if (processed < k7zStartHeaderSize ||
    145        (processed == k7zStartHeaderSize && numPrevBytes != 0))
    146      return False;
    147    processed -= k7zStartHeaderSize;
    148    for (pos = 0; pos <= processed; pos++)
    149    {
    150      for (; pos <= processed && buf[pos] != '7'; pos++);
    151      if (pos > processed)
    152        break;
    153      if (memcmp(buf + pos, k7zSignature, k7zSignatureSize) == 0)
    154        if (CrcCalc(buf + pos + 12, 20) == GetUi32(buf + pos + 8))
    155        {
    156          *resPos += pos;
    157          return True;
    158        }
    159    }
    160    *resPos += processed;
    161    numPrevBytes = k7zStartHeaderSize;
    162    memmove(buf, buf + processed, k7zStartHeaderSize);
    163  }
    164 }
    165 
    166 static Bool DoesFileOrDirExist(const WCHAR *path)
    167 {
    168  WIN32_FIND_DATAW fd;
    169  HANDLE handle;
    170  handle = FindFirstFileW(path, &fd);
    171  if (handle == INVALID_HANDLE_VALUE)
    172    return False;
    173  FindClose(handle);
    174  return True;
    175 }
    176 
    177 static WRes RemoveDirWithSubItems(WCHAR *path)
    178 {
    179  WIN32_FIND_DATAW fd;
    180  HANDLE handle;
    181  WRes res = 0;
    182  size_t len = wcslen(path);
    183  wcscpy(path + len, L"*");
    184  handle = FindFirstFileW(path, &fd);
    185  path[len] = L'\0';
    186  if (handle == INVALID_HANDLE_VALUE)
    187    return GetLastError();
    188  
    189  for (;;)
    190  {
    191    if (wcscmp(fd.cFileName, L".") != 0 &&
    192        wcscmp(fd.cFileName, L"..") != 0)
    193    {
    194      wcscpy(path + len, fd.cFileName);
    195      if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
    196      {
    197        wcscat(path, WSTRING_PATH_SEPARATOR);
    198        res = RemoveDirWithSubItems(path);
    199      }
    200      else
    201      {
    202        SetFileAttributesW(path, 0);
    203        if (DeleteFileW(path) == 0)
    204          res = GetLastError();
    205      }
    206    
    207      if (res != 0)
    208        break;
    209    }
    210  
    211    if (!FindNextFileW(handle, &fd))
    212    {
    213      res = GetLastError();
    214      if (res == ERROR_NO_MORE_FILES)
    215        res = 0;
    216      break;
    217    }
    218  }
    219  
    220  path[len] = L'\0';
    221  FindClose(handle);
    222  if (res == 0)
    223  {
    224    if (!RemoveDirectoryW(path))
    225      res = GetLastError();
    226  }
    227  return res;
    228 }
    229 
    230 #ifdef _CONSOLE
    231 int MY_CDECL main()
    232 #else
    233 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    234  #ifdef UNDER_CE
    235  LPWSTR
    236  #else
    237  LPSTR
    238  #endif
    239  lpCmdLine, int nCmdShow)
    240 #endif
    241 {
    242  CFileInStream archiveStream;
    243  CLookToRead2 lookStream;
    244  CSzArEx db;
    245  SRes res = SZ_OK;
    246  ISzAlloc allocImp;
    247  ISzAlloc allocTempImp;
    248  WCHAR sfxPath[MAX_PATH + 2];
    249  WCHAR path[MAX_PATH * 3 + 2];
    250  #ifndef UNDER_CE
    251  WCHAR workCurDir[MAX_PATH + 32];
    252  #endif
    253  size_t pathLen;
    254  DWORD winRes;
    255  const wchar_t *cmdLineParams;
    256  const char *errorMessage = NULL;
    257  Bool useShellExecute = True;
    258  DWORD exitCode = 0;
    259 
    260  LoadSecurityDlls();
    261 
    262  #ifdef _CONSOLE
    263  SetConsoleCtrlHandler(HandlerRoutine, TRUE);
    264  #else
    265  UNUSED_VAR(hInstance);
    266  UNUSED_VAR(hPrevInstance);
    267  UNUSED_VAR(lpCmdLine);
    268  UNUSED_VAR(nCmdShow);
    269  #endif
    270 
    271  CrcGenerateTable();
    272 
    273  allocImp.Alloc = SzAlloc;
    274  allocImp.Free = SzFree;
    275 
    276  allocTempImp.Alloc = SzAllocTemp;
    277  allocTempImp.Free = SzFreeTemp;
    278 
    279  FileInStream_CreateVTable(&archiveStream);
    280  LookToRead2_CreateVTable(&lookStream, False);
    281  lookStream.buf = NULL;
    282 
    283  winRes = GetModuleFileNameW(NULL, sfxPath, MAX_PATH);
    284  if (winRes == 0 || winRes > MAX_PATH)
    285    return 1;
    286  {
    287    cmdLineParams = GetCommandLineW();
    288    #ifndef UNDER_CE
    289    {
    290      Bool quoteMode = False;
    291      for (;; cmdLineParams++)
    292      {
    293        wchar_t c = *cmdLineParams;
    294        if (c == L'\"')
    295          quoteMode = !quoteMode;
    296        else if (c == 0 || (c == L' ' && !quoteMode))
    297          break;
    298      }
    299    }
    300    #endif
    301  }
    302 
    303  {
    304    unsigned i;
    305    DWORD d;
    306    winRes = GetTempPathW(MAX_PATH, path);
    307    if (winRes == 0 || winRes > MAX_PATH)
    308      return 1;
    309    pathLen = wcslen(path);
    310    d = (GetTickCount() << 12) ^ (GetCurrentThreadId() << 14) ^ GetCurrentProcessId();
    311    
    312    for (i = 0;; i++, d += GetTickCount())
    313    {
    314      if (i >= 100)
    315      {
    316        res = SZ_ERROR_FAIL;
    317        break;
    318      }
    319      wcscpy(path + pathLen, L"7z");
    320 
    321      {
    322        wchar_t *s = path + wcslen(path);
    323        UInt32 value = d;
    324        unsigned k;
    325        for (k = 0; k < 8; k++)
    326        {
    327          unsigned t = value & 0xF;
    328          value >>= 4;
    329          s[7 - k] = (wchar_t)((t < 10) ? ('0' + t) : ('A' + (t - 10)));
    330        }
    331        s[k] = '\0';
    332      }
    333 
    334      if (DoesFileOrDirExist(path))
    335        continue;
    336      if (CreateDirectoryW(path, NULL))
    337      {
    338        wcscat(path, WSTRING_PATH_SEPARATOR);
    339        pathLen = wcslen(path);
    340        break;
    341      }
    342      if (GetLastError() != ERROR_ALREADY_EXISTS)
    343      {
    344        res = SZ_ERROR_FAIL;
    345        break;
    346      }
    347    }
    348    
    349    #ifndef UNDER_CE
    350    wcscpy(workCurDir, path);
    351    #endif
    352    if (res != SZ_OK)
    353      errorMessage = "Can't create temp folder";
    354  }
    355 
    356  if (res != SZ_OK)
    357  {
    358    if (!errorMessage)
    359      errorMessage = "Error";
    360    PrintErrorMessage(errorMessage);
    361    return 1;
    362  }
    363 
    364  if (InFile_OpenW(&archiveStream.file, sfxPath) != 0)
    365  {
    366    errorMessage = "can not open input file";
    367    res = SZ_ERROR_FAIL;
    368  }
    369  else
    370  {
    371    UInt64 pos = 0;
    372    if (!FindSignature(&archiveStream.file, &pos))
    373      res = SZ_ERROR_FAIL;
    374    else if (File_Seek(&archiveStream.file, (Int64 *)&pos, SZ_SEEK_SET) != 0)
    375      res = SZ_ERROR_FAIL;
    376    if (res != 0)
    377      errorMessage = "Can't find 7z archive";
    378  }
    379 
    380  if (res == SZ_OK)
    381  {
    382    lookStream.buf = ISzAlloc_Alloc(&allocImp, kInputBufSize);
    383    if (!lookStream.buf)
    384      res = SZ_ERROR_MEM;
    385    else
    386    {
    387      lookStream.bufSize = kInputBufSize;
    388      lookStream.realStream = &archiveStream.vt;
    389      LookToRead2_Init(&lookStream);
    390    }
    391  }
    392 
    393  SzArEx_Init(&db);
    394  
    395  if (res == SZ_OK)
    396  {
    397    res = SzArEx_Open(&db, &lookStream.vt, &allocImp, &allocTempImp);
    398  }
    399  
    400  if (res == SZ_OK)
    401  {
    402    UInt32 executeFileIndex = (UInt32)(Int32)-1;
    403    UInt32 minPrice = 1 << 30;
    404    UInt32 i;
    405    UInt32 blockIndex = 0xFFFFFFFF; /* it can have any value before first call (if outBuffer = 0) */
    406    Byte *outBuffer = 0; /* it must be 0 before first call for each new archive. */
    407    size_t outBufferSize = 0;  /* it can have any value before first call (if outBuffer = 0) */
    408    
    409    for (i = 0; i < db.NumFiles; i++)
    410    {
    411      size_t offset = 0;
    412      size_t outSizeProcessed = 0;
    413      WCHAR *temp;
    414 
    415      if (SzArEx_GetFileNameUtf16(&db, i, NULL) >= MAX_PATH)
    416      {
    417        res = SZ_ERROR_FAIL;
    418        break;
    419      }
    420      
    421      temp = path + pathLen;
    422      
    423      SzArEx_GetFileNameUtf16(&db, i, temp);
    424      {
    425        res = SzArEx_Extract(&db, &lookStream.vt, i,
    426          &blockIndex, &outBuffer, &outBufferSize,
    427          &offset, &outSizeProcessed,
    428          &allocImp, &allocTempImp);
    429        if (res != SZ_OK)
    430          break;
    431      }
    432      {
    433        CSzFile outFile;
    434        size_t processedSize;
    435        size_t j;
    436        size_t nameStartPos = 0;
    437        for (j = 0; temp[j] != 0; j++)
    438        {
    439          if (temp[j] == '/')
    440          {
    441            temp[j] = 0;
    442            MyCreateDir(path);
    443            temp[j] = CHAR_PATH_SEPARATOR;
    444            nameStartPos = j + 1;
    445          }
    446        }
    447 
    448        if (SzArEx_IsDir(&db, i))
    449        {
    450          MyCreateDir(path);
    451          continue;
    452        }
    453        else
    454        {
    455          unsigned extLen;
    456          const WCHAR *name = temp + nameStartPos;
    457          unsigned len = (unsigned)wcslen(name);
    458          unsigned nameLen = FindExt(temp + nameStartPos, &extLen);
    459          unsigned extPrice = FindItem(kExts, sizeof(kExts) / sizeof(kExts[0]), name + len - extLen, extLen);
    460          unsigned namePrice = FindItem(kNames, sizeof(kNames) / sizeof(kNames[0]), name, nameLen);
    461 
    462          unsigned price = namePrice + extPrice * 64 + (nameStartPos == 0 ? 0 : (1 << 12));
    463          if (minPrice > price)
    464          {
    465            minPrice = price;
    466            executeFileIndex = i;
    467            useShellExecute = (extPrice != k_EXE_ExtIndex);
    468          }
    469         
    470          if (DoesFileOrDirExist(path))
    471          {
    472            errorMessage = "Duplicate file";
    473            res = SZ_ERROR_FAIL;
    474            break;
    475          }
    476          if (OutFile_OpenW(&outFile, path))
    477          {
    478            errorMessage = "Can't open output file";
    479            res = SZ_ERROR_FAIL;
    480            break;
    481          }
    482        }
    483  
    484        processedSize = outSizeProcessed;
    485        if (File_Write(&outFile, outBuffer + offset, &processedSize) != 0 || processedSize != outSizeProcessed)
    486        {
    487          errorMessage = "Can't write output file";
    488          res = SZ_ERROR_FAIL;
    489        }
    490        
    491        #ifdef USE_WINDOWS_FILE
    492        if (SzBitWithVals_Check(&db.MTime, i))
    493        {
    494          const CNtfsFileTime *t = db.MTime.Vals + i;
    495          FILETIME mTime;
    496          mTime.dwLowDateTime = t->Low;
    497          mTime.dwHighDateTime = t->High;
    498          SetFileTime(outFile.handle, NULL, NULL, &mTime);
    499        }
    500        #endif
    501        
    502        {
    503          SRes res2 = File_Close(&outFile);
    504          if (res != SZ_OK)
    505            break;
    506          if (res2 != SZ_OK)
    507          {
    508            res = res2;
    509            break;
    510          }
    511        }
    512        #ifdef USE_WINDOWS_FILE
    513        if (SzBitWithVals_Check(&db.Attribs, i))
    514          SetFileAttributesW(path, db.Attribs.Vals[i]);
    515        #endif
    516      }
    517    }
    518 
    519    if (res == SZ_OK)
    520    {
    521      if (executeFileIndex == (UInt32)(Int32)-1)
    522      {
    523        errorMessage = "There is no file to execute";
    524        res = SZ_ERROR_FAIL;
    525      }
    526      else
    527      {
    528        WCHAR *temp = path + pathLen;
    529        UInt32 j;
    530        SzArEx_GetFileNameUtf16(&db, executeFileIndex, temp);
    531        for (j = 0; temp[j] != 0; j++)
    532          if (temp[j] == '/')
    533            temp[j] = CHAR_PATH_SEPARATOR;
    534      }
    535    }
    536    ISzAlloc_Free(&allocImp, outBuffer);
    537  }
    538 
    539  SzArEx_Free(&db, &allocImp);
    540 
    541  ISzAlloc_Free(&allocImp, lookStream.buf);
    542 
    543  File_Close(&archiveStream.file);
    544 
    545  if (res == SZ_OK)
    546  {
    547    HANDLE hProcess = 0;
    548    
    549    #ifndef UNDER_CE
    550    WCHAR oldCurDir[MAX_PATH + 2];
    551    oldCurDir[0] = 0;
    552    {
    553      DWORD needLen = GetCurrentDirectory(MAX_PATH + 1, oldCurDir);
    554      if (needLen == 0 || needLen > MAX_PATH)
    555        oldCurDir[0] = 0;
    556      SetCurrentDirectory(workCurDir);
    557    }
    558    #endif
    559    
    560    if (useShellExecute)
    561    {
    562      SHELLEXECUTEINFO ei;
    563      UINT32 executeRes;
    564      BOOL success;
    565      
    566      memset(&ei, 0, sizeof(ei));
    567      ei.cbSize = sizeof(ei);
    568      ei.lpFile = path;
    569      ei.fMask = SEE_MASK_NOCLOSEPROCESS
    570          #ifndef UNDER_CE
    571          | SEE_MASK_FLAG_DDEWAIT
    572          #endif
    573          /* | SEE_MASK_NO_CONSOLE */
    574          ;
    575      if (wcslen(cmdLineParams) != 0)
    576        ei.lpParameters = cmdLineParams;
    577      ei.nShow = SW_SHOWNORMAL; /* SW_HIDE; */
    578      success = ShellExecuteEx(&ei);
    579      executeRes = (UINT32)(UINT_PTR)ei.hInstApp;
    580      if (!success || (executeRes <= 32 && executeRes != 0))  /* executeRes = 0 in Windows CE */
    581        res = SZ_ERROR_FAIL;
    582      else
    583        hProcess = ei.hProcess;
    584    }
    585    else
    586    {
    587      STARTUPINFOW si;
    588      PROCESS_INFORMATION pi;
    589      WCHAR cmdLine[MAX_PATH * 3];
    590 
    591      wcscpy(cmdLine, path);
    592      wcscat(cmdLine, cmdLineParams);
    593      memset(&si, 0, sizeof(si));
    594      si.cb = sizeof(si);
    595      if (CreateProcessW(NULL, cmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi) == 0)
    596        res = SZ_ERROR_FAIL;
    597      else
    598      {
    599        CloseHandle(pi.hThread);
    600        hProcess = pi.hProcess;
    601      }
    602    }
    603    
    604    if (hProcess != 0)
    605    {
    606      WaitForSingleObject(hProcess, INFINITE);
    607      if (!GetExitCodeProcess(hProcess, &exitCode))
    608        exitCode = 1;
    609      CloseHandle(hProcess);
    610    }
    611    
    612    #ifndef UNDER_CE
    613    SetCurrentDirectory(oldCurDir);
    614    #endif
    615  }
    616 
    617  path[pathLen] = L'\0';
    618  RemoveDirWithSubItems(path);
    619 
    620  if (res == SZ_OK)
    621    return (int)exitCode;
    622  
    623  {
    624    if (res == SZ_ERROR_UNSUPPORTED)
    625      errorMessage = "Decoder doesn't support this archive";
    626    else if (res == SZ_ERROR_MEM)
    627      errorMessage = "Can't allocate required memory";
    628    else if (res == SZ_ERROR_CRC)
    629      errorMessage = "CRC error";
    630    else
    631    {
    632      if (!errorMessage)
    633        errorMessage = "ERROR";
    634    }
    635 
    636    if (errorMessage)
    637      PrintErrorMessage(errorMessage);
    638  }
    639  return 1;
    640 }