@echo off

rem ##############################################################################
rem #
rem # Copyright (c) 2013 VMware, Inc. All rights reserved.
rem # -- VMware Confidential
rem #
rem ##############################################################################

rem check if the rollback directory exists
set "_VC_ROLLBACK_DIR=%ROLLBACK_BACKUP_FOLDER%\VC"
if NOT EXIST "%_VC_ROLLBACK_DIR%" (
   call "%~dp0common" Cannot find the backup directory "%_VC_ROLLBACK_DIR%". Creating it...
   mkdir "%_VC_ROLLBACK_DIR%"
   if NOT EXIST "%_VC_ROLLBACK_DIR%" (
      call "%~dp0common" Cannot create the backup directory "%_VC_ROLLBACK_DIR%"
      goto endError
   )
)

rem check if this script was not invoked from logbrowser-update-trust
if NOT "%1"=="-logbrowser-update-trust" (
    rem check if vpxd is on this machine
    sc query vpxd
    if ERRORLEVEL 1 (
       call :setLastErrorAndEcho "This operation should be run on the machine running vCenter Server."
       goto endError
    )
)

rem since setlocal makes it impossible to export last_error out to the
rem ssl-updater script, we will use this file to store the last_error and
rem populate it in last_error after an endlocal
set "_VC_TMP_LOG_FILE=%_VC_ROLLBACK_DIR%\vc-update-trust-to-sso.tmp"
del /q "%_VC_TMP_LOG_FILE%" > nul 2> nul

rem a temporary file into which we will extract the SSO CA certificate.
set "_VC_SSO_CA_CERT_FILE=%_VC_ROLLBACK_DIR%\ssocacert.crt"
del /q "%_VC_SSO_CA_CERT_FILE%" > nul 2> nul

setlocal enabledelayedexpansion

if "%PROGRAMDATA%"=="" (
   set "_VC_VMWARE_DATA_DIR=%ALLUSERSPROFILE%\Application Data\VMware"
) else (
   set "_VC_VMWARE_DATA_DIR=%PROGRAMDATA%\VMware"
)

set "_VC_SSL_DIR=%_VC_VMWARE_DATA_DIR%\SSL"
set "_VC_OPENSSL_EXE_PATH=%~dp0openssl\openssl.exe"

call "%~dp0common" Validating the input parameters...
if NOT EXIST "%sso_cert_chain%" (
   call :setLastErrorAndEcho "Cannot find certificate(s) %sso_cert_chain%"
   goto endError
)

if NOT EXIST "%_VC_VMWARE_DATA_DIR%" (
   call :setLastErrorAndEcho "Cannot find the vCenter Server Application Data directory %_VC_VMWARE_DATA_DIR%"
   goto endError
)

if NOT EXIST "%_VC_SSL_DIR%" (
   call :setLastErrorAndEcho "Cannot find the vCenter Server Application Data directory %_VC_SSL_DIR%"
   goto endError
)

if "%JAVA_HOME%" == "" (
   call :setLastErrorAndEcho "Cannot find JAVA_HOME or JAVA_HOME is not set."
   goto endError
)

rem set a log file for the extract sso certificate operation
set _VC_EXTRACT_SSO_CERT_LOG=%LOGS_FOLDER%\vc-extract-sso-cert.log
rem extract the root CA certificate from the SSO certificate chain
"%JAVA_HOME%\bin\java" -Dsso-updater.tool.log-file="%_VC_EXTRACT_SSO_CERT_LOG%" ^
-cp "%~dp0\sso-updater.jar" com.vmware.sso.cfg.ExtractCA "%sso_cert_chain%" "%_VC_SSO_CA_CERT_FILE%"

if NOT EXIST "%_VC_SSO_CA_CERT_FILE%" (
   call :setLastErrorAndEcho "Cannot find Single Sign-On CA certificate(s) %_VC_SSO_CA_CERT_FILE%"
   goto endError
)

call "%~dp0common" Cleaning temporary files if any...
del /q "%_VC_ROLLBACK_DIR%\*.tmp" > nul 2> nul
if ERRORLEVEL 1 (
   call :setLastErrorAndEcho "Cannot delete the temporary files in %_VC_ROLLBACK_DIR%: %ERRORLEVEL%"
   goto endError
)

rem use temporary files to hold the new and the existing certificates for file
rem comparison time.
set "_VC_TEMP_CERT_NEW=%_VC_ROLLBACK_DIR%\newCert.tmp"
"%_VC_OPENSSL_EXE_PATH%" x509 -in "%_VC_SSO_CA_CERT_FILE%" -out "%_VC_TEMP_CERT_NEW%"

set "_VC_TEMP_CERT_NEW_FINGERPRINT=%_VC_ROLLBACK_DIR%\newCertFingerprint.tmp"
"%_VC_OPENSSL_EXE_PATH%" x509 -in "%_VC_SSO_CA_CERT_FILE%" -fingerprint -sha512 > "%_VC_TEMP_CERT_NEW_FINGERPRINT%"
set "_VC_TEMP_CERT_EXISTING=%_VC_ROLLBACK_DIR%\oldCert.tmp"
set "_VC_TEMP_CERT_EXISTING_FINGERPRINT=%_VC_ROLLBACK_DIR%\oldCertFingerprint.tmp"

call "%~dp0common" Getting the hash of the certificate...
for /f "usebackq" %%H IN (`""%_VC_OPENSSL_EXE_PATH%" x509 -hash -noout -in "%_VC_SSO_CA_CERT_FILE%""`) do set _VC_CERT_RAW_HASH=%%H
if ERRORLEVEL 1 (
   call :setLastErrorAndEcho "openssl.exe cannot get the hash for %_VC_SSO_CA_CERT_FILE%: %ERRORLEVEL%"
   goto endError
)

set "_VC_CERT_HASH_FILENAME=%_VC_CERT_RAW_HASH%.0"
set "_VC_CERT_HASH_PATH=%_VC_SSL_DIR%\%_VC_CERT_HASH_FILENAME%"

call "%~dp0common" Determining if a file with the same hash already exists...
rem simple case, the file <hash>.0 does not exist
if NOT EXIST "%_VC_CERT_HASH_PATH%" (
   call "%~dp0common" No such files. Copying "%_VC_SSO_CA_CERT_FILE%" to "%_VC_CERT_HASH_PATH%..."
   copy /v /y "%_VC_SSO_CA_CERT_FILE%" "%_VC_CERT_HASH_PATH%" > nul
   if ERRORLEVEL 1 (
      call :setLastErrorAndEcho "Cannot copy %_VC_SSO_CA_CERT_FILE% to %_VC_CERT_HASH_PATH%: %ERRORLEVEL%"
      goto endError
   )
   goto appendToOrCreateCrtFile
) else (
   rem <hash>.0 exists
   set _VC_HASH_CERT=""
   set _VC_NEW_CERT_HASH_FILE_EXT=0

   for /f "usebackq" %%C IN (`dir "%_VC_SSL_DIR%\%_VC_CERT_RAW_HASH%".* /b /ON`) DO (
      rem the unsightly stuff below is mostly due to the fact that we cannot
      rem numerically sort files using any sane command in DOS. So, we will
      rem find the max <hash>.XX in the directory.
      set "_VC_HASH_CERT=%%C"

      if "%_VC_HASH_CERT"=="" (
         call :setLastErrorAndEcho "Unable to get the certificate-hash filename."
         goto endError
      )

      rem get the extension
      for %%I IN ("!_VC_HASH_CERT!") DO (
            set _VC_TEMP_CERT_EXISTING_FILE_EXT=%%~xI
      )

      rem remove the dot in the extension
      set _VC_DIGITS_IN_EXTENSION=!_VC_TEMP_CERT_EXISTING_FILE_EXT:~1,3!
      set /A _VC_TEMP_NEW_CERT_HASH_FILE_EXT=!_VC_DIGITS_IN_EXTENSION!+1

      rem is this number the max value we have seen so far?
      if !_VC_TEMP_NEW_CERT_HASH_FILE_EXT! GTR !_VC_NEW_CERT_HASH_FILE_EXT! (
         set _VC_NEW_CERT_HASH_FILE_EXT=!_VC_TEMP_NEW_CERT_HASH_FILE_EXT!
      )

      rem Get the certificate from the data-dir to see if it matches the new
      rem cert.
      "%_VC_OPENSSL_EXE_PATH%" x509 -in "%_VC_SSL_DIR%\%%C" -out "%_VC_TEMP_CERT_EXISTING%"
      if ERRORLEVEL 1 (
         call :setLastErrorAndEcho "Cannot read "%_VC_SSL_DIR%\%%C": %ERRORLEVEL%"
         goto endError
      )

      FC "%_VC_TEMP_CERT_NEW%" "%_VC_TEMP_CERT_EXISTING%"
      if ERRORLEVEL 1 (
         call "%~dp0common" "%_VC_TEMP_CERT_NEW%" "%_VC_TEMP_CERT_EXISTING%" certificates differ
      ) else (
         rem the certificates match, check if the thumbprints match as well.
         "%_VC_OPENSSL_EXE_PATH%" x509 -in "%_VC_SSL_DIR%\%%C" -fingerprint -sha512 > "%_VC_TEMP_CERT_EXISTING_FINGERPRINT%"
         if ERRORLEVEL 1 (
            call :setLastErrorAndEcho "Cannot get the fingerprint of "%_VC_SSL_DIR%\%%C": %ERRORLEVEL%"
            goto endError
         )

         FC "%_VC_TEMP_CERT_NEW_FINGERPRINT%" "%_VC_TEMP_CERT_EXISTING_FINGERPRINT%"
         if ERRORLEVEL 1 (
            rem the fingerprints differ
            rem overwrite the old certificate
            rem TODO do we need to ask the user about this?
            call "%~dp0common" Found a pre-existing certificate with the same hash, but different fingerprint. Overwriting...
            copy /v /y "%_VC_TEMP_CERT_NEW%" "%_VC_TEMP_CERT_EXISTING%"
            if ERRORLEVEL 1 (
               call :setLastErrorAndEcho "Cannot overwrite %_VC_TEMP_CERT_EXISTING%:  %ERRORLEVEL%"
               goto endError
            )
            goto finalize
         ) else (
            rem the fingerprint match as well so nothing to do here.
            call "%~dp0common" Found a pre-existing certificate that matches.
            goto finalize
         )
      )
   )

   rem if we got here, it means that we have not found a match
   rem so, write to a new file; if <hash>.0, <hash.3> exist, the new file will
   rem be at <hash>.4
   set "_VC_NEW_CERT_HASH_FILE_PATH=%_VC_SSL_DIR%\%_VC_CERT_RAW_HASH%.!_VC_NEW_CERT_HASH_FILE_EXT!"
   call "%~dp0common" New Certificate hash file path: "!_VC_NEW_CERT_HASH_FILE_PATH!"

   call "%~dp0common" Copying the new cert to "!_VC_NEW_CERT_HASH_FILE_PATH!"
   copy /y /v "%_VC_TEMP_CERT_NEW%"  "!_VC_NEW_CERT_HASH_FILE_PATH!"
   if ERRORLEVEL 1 (
      call :setLastErrorAndEcho Cannot write "!_VC_NEW_CERT_HASH_FILE_PATH!":  %ERRORLEVEL%
      goto endError
   )
)


:finalize

:appendToOrCreateCrtFile
call "%~dp0common" Setup complete...

rem append the contents of the certificate to ca_certificates.crt or create
rem a new ca_certificates.crt if one does not exist
call "%~dp0common" Refreshing "%_VC_SSL_DIR%\ca_certificates.crt" with the new certificate...
if NOT EXIST "%_VC_SSL_DIR%\ca_certificates.crt" (
   copy /v /y "%_VC_SSO_CA_CERT_FILE%" "%_VC_SSL_DIR%\ca_certificates.crt"
) else (
   call "%~dp0common" backing up %_VC_SSL_DIR%\ca_certificates.crt to %_VC_ROLLBACK_DIR%
   copy /y /v "%_VC_SSL_DIR%\ca_certificates.crt" "%_VC_ROLLBACK_DIR%\ca_certificates.crt"
   echo. >> "%_VC_SSL_DIR%\ca_certificates.crt"
   type "%_VC_SSO_CA_CERT_FILE%" >> "%_VC_SSL_DIR%\ca_certificates.crt"
)
if ERRORLEVEL 1 (
   call :setLastErrorAndEcho "Cannot append %_VC_SSO_CA_CERT_FILE% to %_VC_SSL_DIR%\ca_certificates.crt: %ERRORLEVEL%"
   goto endError
)

:end
del /q "%_VC_SSO_CA_CERT_FILE%" > nul 2> nul
call "%~dp0common" Successfully updated the vCenter Server trust to Single Sign-On
exit /B 0

:endError
if exist %_VC_SSO_CA_CERT_FILE% del /q %_VC_SSO_CA_CERT_FILE% > nul 2> nul
call "%~dp0common" Updating the vCenter Server trust to Single Sign-On failed.

endlocal
for /f "usebackq tokens=*" %%H IN (`type "%_VC_TMP_LOG_FILE%"`) do set last_error="%%H"
if ERRORLEVEL 1 (
   set last_error="Cannot retrieve the last_error. Check the log for more information."
   call "%~dp0common" "%last_error%"
)
exit /B 1


:setLastErrorAndEcho
set last_error=%1
call "%~dp0common" "%last_error%"
rem only ever store the last error in this file.
echo %last_error% > "%_VC_TMP_LOG_FILE%"
exit /B 0
