@ECHO off
REM Small script to start a MyEnTunnel (http://nemesis2.qx.net/pages/MyEnTunnel)
REM session for tunneled Samba and mount some shares on Windows XP/7/8. Silently 
REM does nothing if tunnel is already established and shares are already mounted.
REM Requires configured profile of preinstalled stable, non-unicode MyEnTunnel.
REM
REM Call: %0 [/V /D] myentunnel_profile_name [optional_mount_parameters]
REM Runs silently by default, /V enables verbose, /D enables debug output.
REM A mount parameter contains a drive letter followed by colon and a
REM Samba share name. Must be quoted if contains space(s).
REM Example: 
REM       myentunnel_samba.bat /V me_on_my_server H:home\documents P:public
REM establishes a MyEnTunnel session based on profile named 'me_on_my_server' 
REM then mounts folder 'documents' at 'home' Samba share as drive H: 
REM then mounts root of Samba share 'public' as drive P:
REM
REM Author: Zoltan Kovacs <kzoli@kzoli.hu> (Sorry about my terrible English.)
REM License: GNU/GPL v3+ - see: http://www.gnu.org/copyleft/gpl.html
REM Disclaimer: Released "as is", NO WARRANTY! - Use at your own risk.
REM
REM Changelog:
REM 2013-11-19 fix: improved OS recognition, added Windows 8.1 (handled as Windows 7)
REM 2013-03-12 fix: improved OS recognition, added Windows Vista (handled as Windows 7)
REM 2013-03-06 fix: encapsulated IF structures caused loss of :WHERE results
REM 2013-03-06 add: verbose and/or debug output options
REM 2012-12-06 fix: improved search for myentunnel.exe
REM 2012-11-27 fix: improved search for *-myentunnel.ini
REM 2012-11-27 fix: improved OS recognition, added Windows 8 (handled as Windows 7)
REM 2012-11-27 fix: recognizing English versions
REM 2012-09-18 fix: shortest matching filename returns from XP-style :WHERE
REM 2012-09-03 (1st test release) - tested with MyEnTunnel 3.4.2.1

SETLOCAL
rem Common literals
SET BUILD=20131119
SET TIMETRY=6
SET TIMEOUT=5

rem Getting optional verbosity/debug switches.
SET VERBOSE=
SET DEBUG=
:SWITCHES
IF "%~1" == "/V" (SET VERBOSE=YES && SHIFT && GOTO :SWITCHES)
IF "%~1" == "/v" (SET VERBOSE=YES && SHIFT && GOTO :SWITCHES)
IF "%~1" == "/D" (SET DEBUG=YES && SET VERBOSE=YES && SHIFT && GOTO :SWITCHES)
IF "%~1" == "/d" (SET DEBUG=YES && SET VERBOSE=YES && SHIFT && GOTO :SWITCHES)

rem Greeting.
CALL :INFO "myentunnel_samba build %BUILD% - mounts Samba shares via MyEnTunnel."

rem Determining Windows version (XP/XP64 as XP or Vista/W7/W8/W8.1 as W7).
VER | FIND " 5.1" >nul
IF NOT ERRORLEVEL 1 (SET VERSION=XP)
VER | FIND " 5.2" >nul
IF NOT ERRORLEVEL 1 (SET VERSION=XP)
VER | FIND " 6.0" >nul
IF NOT ERRORLEVEL 1 (SET VERSION=W7)
VER | FIND " 6.1" >nul
IF NOT ERRORLEVEL 1 (SET VERSION=W7)
VER | FIND " 6.2" >nul
IF NOT ERRORLEVEL 1 (SET VERSION=W7)
VER | FIND " 6.3" >nul
IF NOT ERRORLEVEL 1 (SET VERSION=W7)
IF "%VERSION%" == "" (
	CALL :MESSAGE "Cannot determine Windows version." && CALL :ERR_EXIT 1 )
rem Determining architecture (x86 or x64).
IF %PROCESSOR_ARCHITECTURE% == AMD64 (SET ARCH=x64 & SET PROGRAMS=%PROGRAMFILES% ^(x86^))	
IF %PROCESSOR_ARCHITECTURE% == x86 (SET ARCH=x86 & SET PROGRAMS=%PROGRAMFILES%)	
IF "%ARCH%" == "" (
	CALL :MESSAGE "Cannot determine processor architecture." && CALL :ERR_EXIT 1 )

REM Part 1: check tunnel, (re)establish when is offline.

rem Getting mandatory profile name (without trailing space).
SET MET_PROFILE=%~1 & SHIFT
SET MET_PROFILE=%MET_PROFILE: =%
IF "%MET_PROFILE%" == "" (
	CALL :MESSAGE "Provide a MyEnTunnel profile name, please." && CALL :ERR_EXIT 1 )
:PROFILE_SEARCH
rem Identifying MyEnTunnel profile file, giving up on failure.
CALL :INFO "Checking MyEnTunnel profile..."
SET MET_PROFILE_INIFILE=
IF %VERSION% == XP GOTO :PROFILE_XP
:PROFILE_W7
rem W7/W8 strategy: AppData in user's profile (VirtualStore), Security in Program Files (32 bits),
rem MyEnTunnel in Program Files (32 bits), whole Program Files (32 bits), whole user's profile.
IF "%MET_PROFILE_INIFILE%" == "" (
	CALL :WHERE "/R" "%USERPROFILE%\AppData\Local\VirtualStore" %MET_PROFILE%-myentunnel.ini MET_PROFILE_INIFILE 2>nul )
IF "%MET_PROFILE_INIFILE%" == "" (
	CALL :WHERE "/R" "%PROGRAMS%\Security" %MET_PROFILE%-myentunnel.ini MET_PROFILE_INIFILE 2>nul )
IF "%MET_PROFILE_INIFILE%" == "" (
	CALL :WHERE "/R" "%PROGRAMS%\MyEnTunnel" %MET_PROFILE%-myentunnel.ini MET_PROFILE_INIFILE 2>nul )
IF "%MET_PROFILE_INIFILE%" == "" (
	CALL :WHERE "/R" "%PROGRAMS%" %MET_PROFILE%-myentunnel.ini MET_PROFILE_INIFILE 2>nul )
IF "%MET_PROFILE_INIFILE%" == "" (
	CALL :WHERE "/R" "%USERPROFILE%" %MET_PROFILE%-myentunnel.ini MET_PROFILE_INIFILE 2>nul )
GOTO :PROFILE_FOUND
:PROFILE_XP
rem WXP strategy: Security in Program Files (32 bits), MyEnTunnel in Program Files (32 bits),
rem whole Program Files (32 bits).
IF "%MET_PROFILE_INIFILE%" == "" (
	CALL :WHERE "/R" "%PROGRAMS%\Security" %MET_PROFILE%-myentunnel.ini MET_PROFILE_INIFILE 2>nul )
IF "%MET_PROFILE_INIFILE%" == "" (
	CALL :WHERE "/R" "%PROGRAMS%\MyEnTunnel" %MET_PROFILE%-myentunnel.ini MET_PROFILE_INIFILE 2>nul )
IF "%MET_PROFILE_INIFILE%" == "" (
	CALL :WHERE "/R" "%PROGRAMS%" %MET_PROFILE%-myentunnel.ini MET_PROFILE_INIFILE 2>nul )
GOTO :PROFILE_FOUND
:PROFILE_FOUND
IF "%MET_PROFILE_INIFILE%" == "" (
	CALL :MESSAGE "MyEnTunnel profile %MET_PROFILE% isn't found." && CALL :ERR_EXIT 1 )
:PROFILE_PARSE
rem Getting tunnel datafile, giving up on failure.
CALL :INFO "Parsing MyEnTunnel profile..."
CALL :DIRNAME "%MET_PROFILE_INIFILE%" "MET_PROFILE_PATH"
SET MET_PROFILE_LTUNNEL=%MET_PROFILE_PATH%%MET_PROFILE%-localports.txt
IF NOT EXIST "%MET_PROFILE_LTUNNEL%" (
	CALL :MESSAGE "MyEnTunnel tunnels file %MET_PROFILE_LTUNNEL% isn't found." && CALL :ERR_EXIT 1 )
rem Parsing local IP and local port number of 1st tunnel defined.
FOR /F "tokens=1,2 delims=: " %%i IN ('TYPE "%MET_PROFILE_LTUNNEL%"') DO (
	SET IPNUM=%%i
	SET PORTNUM=%%j
)
IF "%IPNUM%" == "" (
	CALL :MESSAGE "Failed to determine tunnel parameters" && CALL :ERR_EXIT 1 )
IF "%PORTNUM%" == "" (
	CALL :MESSAGE "Failed to determine tunnel parameters" && CALL :ERR_EXIT 1 )
CALL :INFO "Local endpoint is %IPNUM%:%PORTNUM%"
rem Our system is listening at IPNUM:PORTNUM?
NETSTAT -n -a -p TCP | FIND "%IPNUM%:%PORTNUM%" | FIND "0.0.0.0:0" >nul 2>&1
IF NOT ERRORLEVEL 1 GOTO :TUNNEL_UP
rem We're offline, try to (re)establish connection.
rem Locating myentunnel.exe binary.
CALL :INFO "Locating MyEnTunnel utility..."
IF "%MYENTUNNEL%" == "" (
	CALL :WHERE "/R" "%PROGRAMS%\Security" "myentunnel.exe" MYENTUNNEL 2>nul ) 
IF "%MYENTUNNEL%" == "" (
	CALL :WHERE "/R" "%PROGRAMS%\MyEnTunnel" "myentunnel.exe" MYENTUNNEL 2>nul ) 
IF "%MYENTUNNEL%" == "" (
	CALL :WHERE "/R" "%PROGRAMS%" "myentunnel.exe" MYENTUNNEL 2>nul )
IF "%MYENTUNNEL%" == "" (
	CALL :MESSAGE "Cannot locate myentunnel.exe binary." && CALL :ERR_EXIT 1 )
rem Try to run them with our MyEnTunnel profile.
rem Warning: maybe running multiple instances! - TODO!
CALL :INFO "Starting tunnel..."
START "" "%MYENTUNNEL%" "%MET_PROFILE%"
rem Wait %TIMETRY% x %TIMEOUT% seconds to establish tunnel.
FOR /L %%a IN (1,1,%TIMETRY%) DO (
	PING 127.0.0.1 -n %TIMEOUT% 2>&1 >nul
	rem (re)check connection.
	NETSTAT -n -a -p TCP | FIND "%IPNUM%:%PORTNUM%" | FIND "0.0.0.0:0" >nul 2>&1
	IF NOT ERRORLEVEL 1 GOTO :TUNNEL_UP
)
rem On failure giving up.
CALL :MESSAGE "Cannot establish connection." && CALL :ERR_EXIT 1
rem We're online here.
:TUNNEL_UP
CALL :INFO "Tunnel is up!"

REM Part 2: check shares, mount them if necessary.

rem For each mount parameters...
:MOUNTS
IF "%~1" == "" GOTO :MOUNTS_END
	SET MNT_COMMAND=%~1 & SHIFT
	rem Parsing drive letter and share\pathname definition.
	FOR /F "tokens=1,2 delims=: " %%i IN ("%MNT_COMMAND%") DO (
		SET DRIVE=%%i
		SET SHARE=%%j
	)
	IF "%DRIVE%" == "" GOTO :MOUNTS
	IF "%SHARE%" == "" GOTO :MOUNTS
	rem Nothing to do if drive is readable.
	DIR %DRIVE%: >nul 2>&1
	IF NOT ERRORLEVEL 1 (CALL :INFO "Already mounted share %SHARE% as %DRIVE%:" && GOTO :MOUNTS)
	rem Try to mount, on failure giving up with this parameter.
	CALL :INFO "Mounting share %SHARE% as %DRIVE%:"
	NET USE %DRIVE%: \\%IPNUM%\%SHARE% /PERSISTENT:NO 2>nul
	IF ERRORLEVEL 1 (
	CALL :MESSAGE "Cannot mount drive %DRIVE%:" )
GOTO :MOUNTS
:MOUNTS_END

:END
CALL :INFO "Finished."
IF NOT "%DEBUG%" == "" PAUSE
ENDLOCAL
EXIT

REM *** --- ***
rem Functions
rem (keep ABC order, please!)
rem *** --- ***

rem Function set directory of the 1st parameter into a named variable.
:DIRNAME
SET %~2=%~dp1
EXIT /B

rem Function to leave on error.
:ERR_EXIT
ENDLOCAL
EXIT %~1

rem Function to write an info message.
:INFO
IF "%VERBOSE%" == "" EXIT /B
ECHO %~1 %~2 %~3 %~4 %~5 %~6 %~7 %~8 %~9
EXIT /B

rem Function to write an error message.
:MESSAGE
ECHO %~1 %~2 %~3 %~4 %~5 %~6 %~7 %~8 %~9 >&2
PAUSE
EXIT /B 0

rem Function to put a binary's pathname into a named variable.
rem call: where /R pathname filename variable (finds below pathname)
rem call: where filename variable (finds below %PATH% - Windows 7 only)
:WHERE
SETLOCAL
IF %VERSION% == XP GOTO :WHERE_XP
rem Windows 7 version.
:WHERE_W7
IF "%WHERE%" == "" SET WHERE=%SYSTEMROOT%\System32\where.exe
IF "%~1" == "/R" GOTO :WHERE_REC
	IF NOT "%DEBUG%" == "" CALL :INFO "Lookup in %~1"
	SET VARNAME=%~2
	FOR /f "tokens=*" %%a IN ('%WHERE% %~1') DO (SET PATHNAME=%%a)
	GOTO :WHERE_END
:WHERE_REC
	IF NOT "%DEBUG%" == "" CALL :INFO "Recursive lookup in %2 for %3"
	SET VARNAME=%~4
	FOR /f "tokens=*" %%a IN ('%WHERE% %~1 %2 %3') DO (SET PATHNAME=%%a) 
	GOTO :WHERE_END
rem Windows XP version.
:WHERE_XP
rem Always recursive.
IF "%~1" == "/R" SHIFT
IF NOT "%DEBUG%" == "" CALL :INFO "Recursive lookup in %1 for %2"
SET VARNAME=%~3
FOR /F "tokens=*" %%a IN ('DIR /B /N /S "%~1" ^| FIND  /I "%~2"') DO (SET PATHNAME=%%a & GOTO :WHERE_END)
:WHERE_END
IF NOT "%PATHNAME%" == "" (
	IF NOT "%DEBUG%" == "" CALL :INFO "Found %PATHNAME%")
ENDLOCAL & SET %VARNAME%=%PATHNAME%
EXIT /B 
