@echo off
rem ##############################################################################
rem #
rem # Copyright (c) 2013 VMware, Inc. All rights reserved.
rem # -- VMware Confidential
rem #
rem ##############################################################################
if "%PROGRAMDATA%"=="" (
   set "_VC_DATA_DIR=%ALLUSERSPROFILE%\Application Data\VMware\VMware VirtualCenter"
) else (
   set "_VC_DATA_DIR=%PROGRAMDATA%\VMware\VMware VirtualCenter"
)

set _VC_REG_KEY="HKLM\SOFTWARE\VMware, Inc.\VMware VirtualCenter"
set _VC_INSTALL_REG_KEY="HKLM\SOFTWARE\VMware, Inc.\VMware VirtualCenter\Install"
set _VC_REG_EXE=%windir%\system32\reg.exe
set _VC_INSTALL_DIR=
call :GetVCInstallDir > nul 2>&1
if "%_VC_INSTALL_DIR%"=="" (
   call :setLastErrorAndEcho "Failed to load the vCenter install path from the registry."
   exit /B 2
)

set "_VC_CERT_CHAIN=%vc_cert_chain%"
set "_VC_PRIVATE_KEY=%vc_private_key%"
set "_VC_SSL_DIR=%_VC_DATA_DIR%\SSL"
set "_VC_OPENSSL_EXE_PATH=%~dp0openssl\openssl.exe"
set "_VC_PY2EXE_EXE_PATH=%~dp0vcutils\bin\reloadCerts.exe"
set "_VC_VPXD=%_VC_INSTALL_DIR%\vpxd.exe"
set _VC_ROLLBACK=0
set "_VC_ROLLBACK_DIR=%ROLLBACK_BACKUP_FOLDER%\VC"
set "_VC_REG_TOOL_CMD=%_VC_INSTALL_DIR%\SsoRegTool\regTool.cmd"
set errorCount=0

call "%~dp0common" Validating the configuration and state of vCenter Server
rem if vpxd.cfg has references to sso.crt
rem then we might hit PR997919 if we proceed

FIND /I "%_VC_DATA_DIR%\SSL\sso.crt" "%_VC_DATA_DIR%\vpxd.cfg"
IF %ERRORLEVEL% == 0 (
   rem if we got here, it means that the symptoms match for PR997919 and so let us
   rem warn the user about the inconsistency and pause for user input.

   call :setLastErrorAndEcho "vpxd.cfg refers to sso.crt, manual intervention required."
   echo "Warning: Different certificates are being used for SSL and Solution users." 1>&2
   echo "Manual intervention is required. For details, see KB 2048202." 1>&2
   echo "After performing the steps described in the KB article, continue with this operation." 1>&2
   rem wait for the user

   CHOICE /M "Do you want to continue?" 1>&2
   IF ERRORLEVEL 2 (
      rem NO
      call :setLastErrorAndEcho "Aborted vCenter Server certificate update per user request."
      goto :endError
   )
   rem YES
   call :setLastErrorAndEcho "User pressed 'Y'. Continuing..."
)

call "%~dp0common" Validating the input parameters...

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

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

if NOT EXIST "%_VC_INSTALL_DIR%" (
   call :setLastErrorAndEcho "Cannot find the vCenter Server Installation directory %_VC_INSTALL_DIR%"
   goto endInputError
)

if NOT EXIST "%_VC_VPXD%" (
   call :setLastErrorAndEcho "Cannot find the vCenter Server Installation %_VC_VPXD%"
   goto endInputError
)

sc query vpxd | FIND ": 4  RUNNING "
if ERRORLEVEL 1 (
   call :setLastErrorAndEcho "vCenter Server must be running for this operation to proceed."
   goto endError
)


if "%vc_db_password%"=="" (
   call :setLastErrorAndEcho "vCenter Server database password was not specified."
   goto endInputError
)

rem test whether we can log into the MOB before we proceed any further
"%_VC_PY2EXE_EXE_PATH%" -u "%vc_username%" -p "%vc_password%" -d true
if ERRORLEVEL 1 (
   if "%1"=="-rollback" (
      set last_error="Cannot log in to vCenter. Please check %LOGS_FOLDER%\rollback-vc.log for more details"
   ) else (
      set last_error="Cannot log in to vCenter. Please check %LOGS_FOLDER%\vc-update-ssl.log for more details"
   )
   call "%~dp0common" "Cannot log in to vCenter."
   goto endError
)

set _VC_LS_URL=
call :GetLSUrl > nul 2>&1
if "%_VC_LS_URL%"=="" (
   call :setLastErrorAndEcho "Failed to load the vCenter Server Lookup Service URL from the registry."
   exit /B 2
)

call "%~dp0common" Validating Lookup Service connection
call "%_VC_REG_TOOL_CMD%" validateLsConnection --cert "%_VC_ROLLBACK_DIR%" --ls-url "%_VC_LS_URL%" --username "%sso_admin_user%" --password "%sso_admin_password%"
if NOT "%ERRORLEVEL%"=="0" (
   call :setLastErrorAndEcho "Cannot validate the Lookup Service connection: %ERRORLEVEL%"
   goto endError
)


if "%1"=="-rollback" (
   if NOT EXIST "%_VC_ROLLBACK_DIR%" (
      call :setLastErrorAndEcho "Cannot find the backup directory %_VC_ROLLBACK_DIR%."
      goto endError
   )

   call "%~dp0common" Rolling back previous changes from "%_VC_ROLLBACK_DIR%"
   if NOT EXIST "%_VC_ROLLBACK_DIR%\rui.crt" (
      call :setLastErrorAndEcho "Cannot find the backup file %_VC_ROLLBACK_DIR%\rui.crt: %ERRORLEVEL%"
      goto endError
   )

   if NOT EXIST "%_VC_ROLLBACK_DIR%\rui.key" (
      call :setLastErrorAndEcho "Cannot find the backup file %_VC_ROLLBACK_DIR%\rui.key: %ERRORLEVEL%"
      goto endError
   )

   if NOT EXIST "%_VC_ROLLBACK_DIR%\rui.pfx" (
      call :setLastErrorAndEcho "Cannot find the backup file %_VC_ROLLBACK_DIR%\rui.pfx: %ERRORLEVEL%"
      goto endError
   )

   set _VC_ROLLBACK=1
   goto revertFiles
)

if NOT EXIST "%_VC_ROLLBACK_DIR%" (
   call :setLastErrorAndEcho "Cannot find the backup directory %_VC_ROLLBACK_DIR%. Creating it..."
   mkdir "%_VC_ROLLBACK_DIR%"

   if NOT EXIST "%_VC_ROLLBACK_DIR%" (
      call :setLastErrorAndEcho "Cannot create the backup directory %_VC_ROLLBACK_DIR%"
      goto endInputError
   )
)

if NOT EXIST "%_VC_CERT_CHAIN%" (
   call :setLastErrorAndEcho "Cannot find the certificates %_VC_CERT_CHAIN%"
   goto endInputError
)

if NOT EXIST "%_VC_PRIVATE_KEY%" (
   call :setLastErrorAndEcho "Cannot find the private key %_VC_PRIVATE_KEY%"
   goto endInputError
)

call "%~dp0common" Cleaning any temporary files
del /q "%_VC_ROLLBACK_DIR%\rui.crt" > nul 2> nul

if ERRORLEVEL 1 (
   call :setLastErrorAndEcho "Cannot delete the backup file %_VC_ROLLBACK_DIR%\rui.crt: %ERRORLEVEL%"
   goto endError
)


del /q "%_VC_ROLLBACK_DIR%\rui.key" > nul 2> nul
if ERRORLEVEL 1 (
   call :setLastErrorAndEcho "Cannot delete the backup file %_VC_ROLLBACK_DIR%\rui.key: %ERRORLEVEL%"
   goto endError
)

del /q "%_VC_ROLLBACK_DIR%\rui.pfx" > nul 2> nul
if ERRORLEVEL 1 (
   call :setLastErrorAndEcho "Cannot delete the backup file %_VC_ROLLBACK_DIR%\rui.pfx: %ERRORLEVEL%"
   goto endError
)

call "%~dp0common" Backing up the certificates and keys from "%_VC_SSL_DIR%..."
copy /v /y "%_VC_SSL_DIR%\rui.crt" "%_VC_ROLLBACK_DIR%\rui.crt"
if ERRORLEVEL 1 (
   call :setLastErrorAndEcho "Cannot backup %_VC_SSL_DIR%\rui.crt to %_VC_ROLLBACK_DIR%\rui.crtk: %ERRORLEVEL%"
   goto endError
)

copy /v /y "%_VC_SSL_DIR%\rui.key" "%_VC_ROLLBACK_DIR%\rui.key"
if ERRORLEVEL 1 (
   call :setLastErrorAndEcho "Cannot backup %_VC_SSL_DIR%\rui.key to %_VC_ROLLBACK_DIR%\rui.key: %ERRORLEVEL%"
   del "%_VC_ROLLBACK_DIR%\rui.crt"
   goto endError
)

copy /v /y "%_VC_SSL_DIR%\rui.pfx" "%_VC_ROLLBACK_DIR%\rui.pfx"
if ERRORLEVEL 1 (
   call :setLastErrorAndEcho "Cannot backup %_VC_SSL_DIR%\rui.pfx to %_VC_ROLLBACK_DIR%\rui.pfx: %ERRORLEVEL%"
   del "%_VC_ROLLBACK_DIR%\rui.crt"
   del "%_VC_ROLLBACK_DIR%\rui.key"
   goto endError
)

call "%~dp0common" Copying the new certificates and keys to "%_VC_SSL_DIR%..."
copy /v /y "%_VC_CERT_CHAIN%" "%_VC_SSL_DIR%\rui.crt" > nul
if ERRORLEVEL 1 (
   call :setLastErrorAndEcho "Cannot copy rui.crt: %ERRORLEVEL%"
   goto endErrorUndoCopy
)

copy /v /y "%_VC_PRIVATE_KEY%" "%_VC_SSL_DIR%\rui.key" > nul
if ERRORLEVEL 1 (
   call :setLastErrorAndEcho "Cannot copy rui.key: %ERRORLEVEL%"
   goto endErrorUndoCopy
)

call "%~dp0common" Creating the PKCS certificate file...
"%_VC_OPENSSL_EXE_PATH%" pkcs12 -export -out "%_VC_SSL_DIR%\rui.pfx" -in "%_VC_SSL_DIR%\rui.crt" -inkey "%_VC_SSL_DIR%\rui.key" -name rui -passout pass:testpassword
if ERRORLEVEL 1 (
   call :setLastErrorAndEcho "openssl.exe cannot generate rui.pfx: %ERRORLEVEL%"
   goto endErrorUndoCopy
)

rem From here on, any errors mean that a rollback needs to happen
set _VC_ROLLBACK=1

:updateServices
rem a utility to login to the MOB and reloadSslCertificates.
"%_VC_PY2EXE_EXE_PATH%" -u "%vc_username%" -p "%vc_password%" -d false
if ERRORLEVEL 1 (
   call :setLastErrorAndEcho "Cannot reload the vCenter Server SSL certificates. The certificate might not be unique."
   goto endErrorUndoCopy
)


call "%~dp0common" Encrypting the password with the certificates...
"%_VC_VPXD%" -P "%vc_db_password%"
if ERRORLEVEL 1 (
   call :setLastErrorAndEcho "Cannot encrypt the vCenter password with the new certificate: %ERRORLEVEL%"
   goto endErrorUndoCopy
)

call "%~dp0common" Setup complete. Restarting services...
call "%~dp0common" Restarting vCenter Server...
echo "Restarting services... (This can take some time)" 1>&2

sc stop vctomcat

rem stopping and starting vcenter services can take some time. Arbitrarily wait
rem 300 seconds before declaring failure.
rem
rem Note: sc query <service> | FIND ": 1  STOPPED "
rem Since there is no effective way to wait for a service to stop and start and
rem testing for errorlevel is getting tricky, I've chosed to search for the
rem string like ": 1  STOPPED" and ": 4  RUNNING" which are fairly unique and
rem locale agnostic.

set loopCount=0

call "%~dp0common" Stopping vCenter Web Services...
echo "Stopping vCenter Web Services..." 1>&2

:stopVctomcat
set /a loopCount=loopCount+1
sc query vctomcat | FIND ": 1  STOPPED "
if ERRORLEVEL 1 (
   call :setLastErrorAndEcho "Cannot stop the vCenter Server Web Services: %ERRORLEVEL%"
   timeout /t 5 /nobreak > nul
   if %loopCount% GTR 60 goto endErrorUndoCopy
   goto stopVctomcat
)

sc stop vpxd

set loopCount=0
call "%~dp0common" Stopping vCenter Server...
echo "Stopping vCenter Server..." 1>&2

:stopVpxd
set /a loopCount=loopCount+1
sc query vpxd | FIND ": 1  STOPPED "
if ERRORLEVEL 1 (
   call :setLastErrorAndEcho "Cannot stop vCenter Server: %ERRORLEVEL%"
   timeout /t 5 /nobreak > nul
   if %loopCount% GTR 60 goto endError
   goto stopVpxd
)

sc start vpxd

set loopCount=0
call "%~dp0common" Starting vCenter Server...
echo "Starting vCenter Server and other services..." 1>&2

:startVpxd
set /a loopCount=loopCount+1
sc query vpxd | FIND ": 4  RUNNING "
if ERRORLEVEL 1 (
   call :setLastErrorAndEcho "Cannot start vCenter Server: %ERRORLEVEL%"
   timeout /t 5 /nobreak > nul
   if %loopCount% GTR 60 goto endError
   goto startVpxd
)

sc start vctomcat
if ERRORLEVEL 1 (
   call :setLastErrorAndEcho "Cannot start vCenter Server Web Services: %ERRORLEVEL%"
   goto endErrorUndoCopy
)

set loopCount=0
call "%~dp0common" Restarting vSphere Profile-Driven Storage Service...
sc stop vimPBSM

:stopVimPBSM
set /a loopCount=loopCount+1
sc query vimPBSM | FIND ": 1  STOPPED "
if ERRORLEVEL 1 (
   call :setLastErrorAndEcho "Cannot stop vSphere Profile-Driven Storage Service: %ERRORLEVEL%"
   timeout /t 5 /nobreak > nul
   if %loopCount% GTR 60 goto endErrorUndoCopy
   goto stopVimPBSM
)

sc start vimPBSM
if ERRORLEVEL 1 (
   call :setLastErrorAndEcho "Cannot start vCenter Profile-Driven Storage Service: %ERRORLEVEL%"
   goto endErrorUndoCopy
)

if %errorCount% GTR 0 goto endError

:end
call "%~dp0common" vCenter certificates updated.
if [%~1] == [-rollback] if [%ERRORLEVEL%] == [0] (
   call "%~dp0common" "The trust between vCenter Server to Inventory Service needs to be updated so invoke the operation (Update vCenter Server Trust to Inventory Service)" 1>&2
   call "%~dp0common" "The trust between Inventory Service to vCenter Server needs to be updated so invoke the operation (Update Inventory Service Trust to vCenter Server)" 1>&2
   call "%~dp0common" "The vSphere Update Manager trust to vCenter Server needs to be updated so invoke the operation (Update vSphere Update Manager(VUM) Trust to vCenter Server)" 1>&2
)
exit /B 0

:endErrorUndoCopy

if %errorCount% GTR 1 goto endError
set /a errorCount=errorCount+1
set error_msg=%last_error%
echo "Cannot continue with the operation due to errors." 1>&2

:revertFiles
call "%~dp0common" Deleting the new certificates and keys...
del /q "%_VC_SSL_DIR%\rui.crt" > nul
del /q "%_VC_SSL_DIR%\rui.key" > nul
del /q "%_VC_SSL_DIR%\rui.pfx" > nul

call "%~dp0common" Restoring the original certificates and keys...
copy /v /y "%_VC_ROLLBACK_DIR%\rui.crt" "%_VC_SSL_DIR%\rui.crt"
copy /v /y "%_VC_ROLLBACK_DIR%\rui.key" "%_VC_SSL_DIR%\rui.key"
copy /v /y "%_VC_ROLLBACK_DIR%\rui.pfx" "%_VC_SSL_DIR%\rui.pfx"

rem conditional to see whether we should fail the update operation or rollback
:endErrorOrRollback
if "%_VC_ROLLBACK%"=="1" (
   set _VC_ROLLBACK=0
   call "%~dp0common" Attempting rollback...
   echo "Attempting rollback..." 1>&2
   goto updateServices
)
goto endError

:GetVCInstallDir
set _VC_INSTALL_DIR=
for /F "usebackq tokens=1,2*" %%i in (`%_VC_REG_EXE% query %_VC_REG_KEY% /v "InstallPath"`) DO (
   if "%%i"=="InstallPath" (
      SET "_VC_INSTALL_DIR=%%k"
   )
)
exit /B 0

:endError
if %errorCount% GTR 0 (
   set last_error=%error_msg%
)
call "%~dp0common" The vCenter certificate update failed.
exit /B 1

:endInputError
call "%~dp0common" Invalid input parameters specified. The vCenter Server SSL certificate update failed.
exit /B 3

:setLastErrorAndEcho
set last_error=%1
call "%~dp0common" "%last_error%"
exit /B 0

:GetLSUrl
set _VC_LS_URL=
for /F "usebackq tokens=1,2*" %%i in (`%_VC_REG_EXE% query %_VC_INSTALL_REG_KEY% /v "LsUrl"`) DO (
   if "%%i"=="LsUrl" (
      SET "_VC_LS_URL=%%k"
   )
)
exit /B 0

call "%~dp0common" Successfully updated the vCenter Server SSL certificate.
