AutoHotkey: Launch a putty session out of an active sftp-plugin connection within TC

From TotalcmdWiki
Jump to navigation Jump to search
; AHK script to launch a putty session out of active sftp-plugin connection within TC
;   using "server", "user" and "password" parameters (client certificate not supported here)
; author: TWatcher
; history:
; version 18.01.2009
; changed: within sftpplug.ini the order of parameters for a stfp connection may be not in fix order,
;          other paramters may be between "server", "user" and "password".
;          Fixed, now the sript seraches for server, user and password in all lines (max. 15 lines) in the ini section
; version 29.09.2008
;         first published
;
; This works only with sftp-plugin provided by Mr. Ghisler.
; precondition: putty.exe has to be found within PC's path
; parameter: "-logToFile" will generate some log statements in file TcStrtPty.log, located in same dir as wincmd.ini
; preparation:
;  (1) save this AHK script to a file, e.g. "TcStrtPty.ahk"
;  (2) compile this AHK script to an exe, execute: "Ahk2Exe.exe /in TcStrtPty.ahk"
;      result should be "TcStrtPty.exe"
;  (3) Drag "TcStrtPty.exe" with the mouse to TC's Button bar.
; usage:
;  (4) within TC, within an active SFTP Session (!) just click on "TcStrtPty.exe-Button" in TC's Button bar.
;      A putty window should open asking for username (if not stored in plugin-session settings)
;      and the password (if not stored in plugin-session settings).
;      If "user" and "password" are stored in plugin-session settings, then the scripts logs in automatically
;      without any further user interaction and the script tries to send a "cd" command to change
;      into directory which is the active directory in TotalCommander's sftp-plugin connection.
; Note:
;   To avoid undesired user input during login/changing directory the AHK script blocks user input
;   (keyboard and mouse) for about 3 seconds.
; Note:
;   Storage of passwords is a security risk and is not recommended,
;   however it is user's decision to use it or not and depending on the environment
;   (e.g. separate network) it is reasonable to use it.

#IfWinExist, ahk_class TTOTAL_CMD

title = AHK script to lauch a putty session out of active sftp-plugin connection within TotalCommander

puttyErrMsg = The programm 'putty.exe' was not found or could not be launched`, please check your 'path' settings`!

version = 18.01.2009
startprg = putty.exe
SearchStrg = \\\Secure FTP\

if not WinActive( "ahk_class TTOTAL_CMD" )
{
   MsgBox,16,, TC is not active, doing nothing
   return
}

; Read current TC dir from the panel left of the command line
ControlGetText, TCPath, TMyPanel2

DoLogToFile =

if 0 > 0
{
  par = %1%
  if ( par = "-h" ) {
    msgstring := title . "`n`n"
    msgstring := msgstring . "Usage:`n"
    msgstring := msgstring . A_ScriptName . " [-h]`n"
    msgstring := msgstring . A_ScriptName . " [-logToFile]`n"
    msgstring := msgstring . "   '-h' : this help text`n"
    msgstring := msgstring . "   '-logToFile' : logs to log file`n"
    MsgBox, 64, ,%msgstring%
    exitapp
  }
  loop, %0%
  {
    par := %A_Index%
    if ( par = "-logToFile" ) {
      DoLogToFile = True
    }
    if ( par = "-showClearPw" ) {
      ShowClearPw = True
    }
  }
}



iniPath = %COMMANDER_INI%
StringLen, pos_prev, iniPath
pos_prev += 1
StringGetPos, pos, iniPath, \, R1
pos += 1
StringLeft, iniPath, iniPath, pos


if DoLogToFile = True
{
  LogFile = %iniPath%TcStrtPty.log
  FileDelete, %LogFile%
  Log(A_ScriptName . " : " . title)
  Log("version: " . version)
  Log("Param: '-logToFile' : Logging to file is active, LogFile: " . LogFile)
}

if showClearPw
{
  Log("Param: '-showClearPw' : Show clear password in log is active")
}

FormatTime, TimeString
Log("Start " . TimeString)
Log("iniPath: " . iniPath )

Log("Found running TC")
Log("Active TCPath is: '" . TCPath )

; Check whether TCPath starts with SearchStrg
if ( InStr( TCPath, SearchStrg) > 0 )
{
  Log("Found SFTP within path")
  ; Replace SearchStrg
  StringReplace, TCPath, TCPath, %SearchStrg%,
  ; Replace the trailing > with a \
  StringReplace, TCPath, TCPath, >, \
  ; now replace any "\" by   "/"
  StringReplace, TCPath, TCPath, \ , / , all
  ; now find 1st "/"
  StringGetPos, pos, TCPath, /
  if pos >= 0
  {
    ; save startdir
    StringRight, startdir, TCPath, StrLen(TCPath) - pos
    ; and Remove rest
    StringLeft, Host, TCPath, pos
  }
}
Log("Host: '" . Host . "'")

iniFile = %iniPath%sftpplug.ini
;##############################
if Host
{
  Log("SFTP-Ini File is : " . iniFile )
  Gosub, SearchSection
}
else
{
  Log("Did not detect active 'Secure FTP' window in TC")
  MsgBox, 16,, Did not detect active 'Secure FTP' window in TC, can do nothing, aborting now...
}

Log("done...")

exitApp


;##############################

SearchSection:
ArrayCount = 0
SectionFound =
Loop, Read, %iniFile%
{
  ArrayCount += 1  ; Keep track of how many items are in the array.
  Array%ArrayCount% := A_LoopReadLine  ; Store this line in the next array element.
}

; Read from the array:
Loop %ArrayCount%
{
  If not SectionFound
  {
    element := Array%A_Index%
    if ( InStr( element, "[" ) = 1 )
    {
      Log("Found section: " . element)
    }
    IfInString, element, %Host%
      SectionFound := true
  }
  If SectionFound
  {
    Log("--> found Host in charge")
    Indextmp = %A_Index%

    MAXLOOP = 15 ; MAXLOOPs lines should be enough
    Loop , %MAXLOOP%
    {
      Indextmp += 1
      if Indextmp > %ArrayCount%
      {
        Log("serach for params: end of ini file reached: exiting loop")
        break
      }
      element2 := Array%Indextmp%
      Log("next line: "  . a_index . " : " . element2 )
      if ( InStr( element2, "[" ) = 1 )
      {
        Log("serach for params: next section found: exiting loop")
        break
      }

      if ( InStr( element2, "server=" ) = 1 )
      {
        ItemServer := Array%Indextmp%
      }
      if ( InStr( element2, "user=" ) = 1 )
      {
        ItemUser := Array%Indextmp%
      }
      if ( InStr( element2, "password=" ) = 1 )
      {
        ItemPassword := Array%Indextmp%
      }
      if ( ItemServer and ItemUser and ItemPassword )
        break
      if ( a_index = MAXLOOP )
      {
        Log("serach for params: searched " . MAXLOOP . " lines, it is enough, exiting loop")
        break
      }
    }

    Log(ItemServer . "   " . ItemUser . "   " . ItemPassword)

    StringReplace, ItemServer, ItemServer, server=,
    ; now replace any "\" by   "/"
    StringReplace, ItemServer, ItemServer, \ , / , all

    ; now find 1st "/"
    StringGetPos, pos, ItemServer, /
    if pos >= 0
    {
      ; Remove rest
      StringLeft, ItemServer, ItemServer, pos
    }

    StringReplace, ItemUser, ItemUser, user=,
    StringReplace, ItemPassword, ItemPassword, password=,

    Log("server='" . ItemServer . "'  user='" . ItemUser . "'  password='" . ItemPassword . "'")
    Log("startdir='" . startdir . "'")

    if not ItemServer
    {
      Log("Empty entry for server in section '" . element . "' can do nothing, aborting now..")
      MsgBox,16,, Empty entry for server in section '%element%' can do nothing, aborting now..
      break
    }

    passwordClear =
    if ItemPassword
    {
      passwordClear := DecryptPW(ItemPassword)
    }

    if ItemUser
    {
      Log("Now trying to start putty session to  " . ItemUser . "@" . ItemServer )
      if passwordClear
      {
        if ShowClearPw
        {
          Log("Run, " . startprg . " -ssh " . ItemServer . " -l " . ItemUser . " -pw " . passwordClear)
        }
        else
        {
          Log("Run, " . startprg . " -ssh " . ItemServer . " -l " . ItemUser . " -pw ******")
        }

        Run, %startprg% -ssh %ItemServer% -l %ItemUser% -pw %passwordClear%, , UseErrorLevel  ;
        if ErrorLevel = ERROR
        {
          MsgBox,16,, %puttyErrMsg%
          ;exit
        }
        else
        {
          if startdir
          {
            Log("ItemServer: '" . ItemServer . "'")

            SetTitleMatchMode, 2
            WinWait, %ItemServer%,, 10
            if not ErrorLevel
            {
              Log("found window: '" . ItemServer . "'")
              Log("switch BlockInput On ...")
              BlockInput On
              Log("going to sleep for 1000 ms ...")
              Sleep, 1000

              IfWinActive, PuTTY Security
              {
                Log("-found active window: PuTTY Security")
                Log("-switch BlockInput Off ...")
                BlockInput Off
                WinWaitNotActive, Security, , 55
                if ErrorLevel
                {
                  Log("-active window: PuTTY Security was not closed within 55 seconds, aborting now")
                  ExitApp ;
                }
                Log("-active window: PuTTY Security was closed")
                Log("going to sleep for 500 ms ...")
                Sleep, 500
              }

              Log("after (optional) handling of Security alert ...")

              IfWinExist, %ItemServer%
              {
                Log("+found existing window: '" . ItemServer . "'")
                IfWinNotActive, %ItemServer%
                {
                  Log("++window: '" . ItemServer . "' is not active, try to activate, wait 5 s")
                  WinWaitActive, %ItemServer%,, 5
                  if ErrorLevel
                  {
                    Log("ErrorLevel while wait for WinWaitActive for '" . ItemServer . "', ErrorLevel:'" . ErrorLevel . "'")
                  }
                }
                Log("+found active window: '" . ItemServer . "'")
                Log("+switch BlockInput On ...")
                BlockInput On
                Log("+going to sleep for 500 ms ...")
                Sleep, 500
                ; check again
                IfWinActive, %ItemServer%
                {
                  Log("++found again active window: '" . ItemServer . "'")
                  Log("++going to sleep for 1000 ms ...")
                  Sleep, 1000
                  Log("++Sending input string startdir '" . startdir . "' ...")
                  SendInput, cd %startdir%{Enter}
                }
                Log("+switch BlockInput Off ...")
                BlockInput Off

                Sleep, 2000
              }
              else
              {
                Log("+did not find window: '" . ItemServer . "', establishing of Putty session not successful, aborting now...")
                MsgBox Establishing of Putty session not successful, aborting now...
                Sleep, 2000
              }
            }
          }
        }
      }
      else
      {
        Log("Run, " . startprg . " -ssh " . ItemServer . " -l " . ItemUser )
        Run, %startprg% -ssh %ItemServer% -l %ItemUser%, , UseErrorLevel  ;
        if ErrorLevel = ERROR
        {
          MsgBox,16,, %puttyErrMsg%
        }
      }
    }
    else
    {
      Log("Now trying to start putty session to  " . ItemServer )
      Log("Run, " . startprg . " -ssh " . ItemServer )
      Run, %startprg% -ssh %ItemServer% , , UseErrorLevel  ;
      if ErrorLevel = ERROR
      {
        MsgBox,16,,  %puttyErrMsg%
      }
    }
    break
    return
  }
}
Log("After loop, now return")
return



;##############################
;functions:
;##############################
Log(LogString)
{
  global
  if DoLogToFile = True
  {
    if LogFile
      FormatTime, TimeString , , hh:mm:ss
      FileAppend, %TimeString% %LogString% `n,  %LogFile%
  }
}

DecryptPW(pwString)
{
  g_pszKey = unpzScGeCInX7XcRM2z+svTK+gegRLhz9KXVbYKJl5boSvVCcfym
  iKeyLength := StrLen(g_pszKey)
  Log("called DecryptPW with pwString: " . pwString)
  iEncryptedLength := StrLen(pwString)
  EnvDiv, iEncryptedLength, 3
  Log("iEncryptedLength: " . iEncryptedLength)
  iPos := mod(iEncryptedLength,iKeyLength)
  pwResult=
  Loop %iEncryptedLength%
  {
    SetFormat, integer, hex
    StringLeft, i3Digits, pwString, 3
    if i3Digits is not integer
    {
      Log("--: i3Digits: should have digits only!, aborted")
      return
    }
    StringRight, pwString, pwString, StrLen(pwString)-3
    encryptCharPos :=  Mod(A_Index + iPos,iKeyLength)
    encryptChar := SubStr(g_pszKey, encryptCharPos , 1)
    iEncryptChar := asc(encryptChar)
    Transform, result2, BitXOr, %iEncryptChar%, %i3Digits%
    pwResult:=pwResult . chr(result2)
    SetFormat, integer, d
  }
  global ShowClearPw
  if ShowClearPw
    Log("pwResult:: '" . pwResult . "'" )
  Log("DecryptPW done")
return pwResult
}

Back to AutoHotkey