IdleLogout.rbbas 19.9 KB
Newer Older
1
#tag Class
Protected Class IdleLogout
Inherits Application
	#tag Event
		Sub Open()
		  Dim debugFileName as string
		  Dim theDate as date
		  dim mXMLtreeToFollow(-1) as string
		  dim mPrefKeyFoundData(-1) as string
		  dim mTempFoundPlistData as string = "" // Local variable to use for gathering plist settings
		  theDate = new Date
		  
		  Globals.gAppFolderItem = GetFolderItem("/Users/Shared/", FolderItem.PathTypeShell)
		  if Globals.gAppFolderItem.Exists = false then
		    Globals.gAppFolderItem = GetFolderItem("/tmp/", FolderItem.PathTypeShell)
		    MsgBox "Can't find /Users/Shared! Using /tmp/ for logs"
		  end
		  
		  
		  // Set the name of the log
		  debugFileName = "psuIdleLogout.RUN" + MiscMethods.PadData("0",2,str(theDate.Year),Globals.kLogToFileDisable) _
		  + "-" + MiscMethods.PadData("0",2,str(theDate.Month),Globals.kLogToFileDisable) + "-"
		  debugFileName = debugFileName + MiscMethods.PadData("0",2,str(theDate.Day),Globals.kLogToFileDisable) + _
		  "-" + MiscMethods.PadData("0",2,str(theDate.Hour),Globals.kLogToFileDisable) + "-"
		  debugFileName = debugFileName + MiscMethods.PadData("0",2,str(theDate.Minute),Globals.kLogToFileDisable) + _
		  "-" + MiscMethods.PadData("0",2,str(theDate.Second),Globals.kLogToFileDisable) + ".log"
		  
		  // Initilize the log, quit if we can't create the log
		  if not (LogToFile.Initialize(debugFileName,Globals.gAppFolderItem)) then
		    beep
		    MsgBox "Error creating run log file! Exiting..."
		    quit
		  end if
		  
		  // Keep only the last 5 logs
		  if ( LogToFile.DeleteOldLogs(5) ) then
		    LogToFile("Deleted Old Logs")
		  end if
		  
		  
		  // CFPref calls
		  
		  'dim ComputerIdleAfterNumSeconds as Integer = "900"
		  'dim IdleLoopDelaySeconds as Integer = "60"
		  'dim WaitForUserPromptSeconds = "90"
		  
		  // Set version number in pref file
		  // prefs.Value ("version") = App.pAppVersion
		  // dim AppVersionPref as String = prefs.Value("version", App.pAppVersion) // Get version from plist file
		  
		  'dim ComputerIdleAfterNumSecondsPref as string = prefs.Value("ComputerIdleAfterNumSeconds", App.pComputerIdleAfterNumSeconds)
		  'dim IdleLoopDelaySecondsPref as string = prefs.Value("IdleLoopDelaySeconds", App.pIdleLoopDelaySeconds)
		  'dim WaitForUserPromptSecondsPref as string = prefs.Value("WaitForUserPromptSeconds", App.pWaitForUserPromptSeconds)
		  '
		  'LogToFile("CFPrefs Return of ComputerIdleAfterNumSeconds: " + ComputerIdleAfterNumSecondsPref )
		  
		  
		  
		  // Are there any users that we should ignore running for?
		  if ( PlistHelper.readPlist(pDefaultPrefsFSPath, pDefaultPrefsFileName, "IgnoreUser", mTempFoundPlistData ) ) then
		    
		    pIgnoreUser = mTempFoundPlistData
		    
		    LogToFile(CurrentMethodName + ": Found the default key data, = " + str(pIgnoreUser) )
		    
		  else // Failed!
		    
		    LogToFile(CurrentMethodName + ": Warning! Failed to find the default key 'IgnoreUser', using default of "+ str (pIgnoreUser) )
		    
		  end if
		  
		  // Are there any groups that we should ignore running for?
		  if ( PlistHelper.readPlist(pDefaultPrefsFSPath, pDefaultPrefsFileName, "IgnoreGroup", mTempFoundPlistData ) ) then
		    
		    pIgnoreGroup = mTempFoundPlistData
		    
		    LogToFile(CurrentMethodName + ": Found the default key data, = " + str(pIgnoreGroup) )
		    
		  else // Failed!
		    
		    LogToFile(CurrentMethodName + ": Warning! Failed to find the default key 'IgnoreGroup', using default of "+ str (pIgnoreGroup) )
		    
		  end if
		  
		  
		  // Is this user macadmin?
		  if ( MiscMethods.CurrentUsername(pIgnoreUser) ) then
		    // It is macadmin, quit the app
		    LogToFile("Current user is " + pIgnoreUser + ", quiting app")
		    quit
		  else
		    LogToFile("User is not " + pIgnoreUser + ", continuing")
		  end if
		  
		  // Is this user an admin?
		  if ( MiscMethods.CurrentGroup(pIgnoreGroup) ) then
		    // It is macadmin, quit the app
		    LogToFile("Current user is an " + pIgnoreGroup + ", quiting app")
		    quit
		  else
		    LogToFile("User is not an " + pIgnoreGroup + ", continuing")
		  end if
		  
		  // What should the Title Be in the Popup Window
		  if ( PlistHelper.readPlist(pDefaultPrefsFSPath, pDefaultPrefsFileName, "WindowTitle", mTempFoundPlistData ) ) then
		    
		    LogoutWarning.WarningTitle.setString(str(mTempFoundPlistData))
		    
		    LogToFile(CurrentMethodName + ": Found the default key data, = " + str(mTempFoundPlistData) )
		    
		  else // Failed!
		    
		    LogToFile(CurrentMethodName + ": Warning! Failed to find the default key 'WindowTitle', using default of "+ str(LogoutWarning.WarningTitle.Text))
		    
		  end if
		  
		  // How long should we wait before considering the Mac is idle too long?
		  if ( PlistHelper.readPlist(pDefaultPrefsFSPath, pDefaultPrefsFileName, "ComputerIdleAfterNumSeconds", mTempFoundPlistData ) ) then
		    
		    
		    pComputerIdleAfterNumSeconds = val( mTempFoundPlistData )
		    
		    LogToFile(CurrentMethodName + ": Found the default key data, = " + str(pComputerIdleAfterNumSeconds) )
		    
		    
		  else // Failed!
		    
		    LogToFile(CurrentMethodName + ": Warning! Failed to find the default key 'ComputerIdleAfterNumSeconds', using default of "+ str (pComputerIdleAfterNumSeconds) )
		    
		  end if
		  
		  // How often should we check to see how long the Mac has been idle?
		  if ( PlistHelper.readPlist(pDefaultPrefsFSPath, pDefaultPrefsFileName, "IdleLoopDelaySeconds", mTempFoundPlistData ) ) then
		    
		    pIdleLoopDelaySeconds = val( mTempFoundPlistData )
		    
		    LogToFile(CurrentMethodName + ": Found the default key data, = " + str(pIdleLoopDelaySeconds) )
		    
		  else // Failed!
		    
		    LogToFile(CurrentMethodName + ": Warning! Failed to find the default key 'IdleLoopDelaySeconds', using default of "+ str (pIdleLoopDelaySeconds) )
		    
		  end if
		  
		  
		  // How long should we wait for the user to respond for More Time or to Log Out?
		  if ( PlistHelper.readPlist(pDefaultPrefsFSPath, pDefaultPrefsFileName, "WaitForUserPromptSeconds", mTempFoundPlistData ) ) then
		    
		    pWaitForUserPromptSeconds = val( mTempFoundPlistData )
		    
		    LogToFile(CurrentMethodName + ": Found the default key data, = " + str(pWaitForUserPromptSeconds) )
		    
		  else // Failed!
		    
		    LogToFile(CurrentMethodName + ": Warning! Failed to find the default key 'WaitForUserPromptSeconds', using default of "+ str (pWaitForUserPromptSeconds) )
		    
		  end if
		  
		  // Hide window from view at start
		  LogoutWarning.Hide()
		  // Start IdleThread
		  LogToFile(CurrentMethodName + ": Running IdleThread")
		  // start thread to watch for idle
		  StartIdleWatch()
		  
		  // exit app now
		  LogToFile(CurrentMethodName + ": <---")
		End Sub
	#tag EndEvent


	#tag Method, Flags = &h0
		Sub forceLogout()
		  LogToFile(CurrentMethodName + ": --->")
		  
		  // Testing mode, use say instead of logout
		  Dim s As Shell
		  s=New Shell
		  dim ShellResults as string
		  s.Mode = 0
		  
		  // Log 
		  LogToFile(CurrentMethodName + ": START Killing Apps")
		  
		  // kill the apps that are open from /Apps
		  LogToFile(CurrentMethodName + ": Killing all launched from /Applications")
		  // s.Execute("kill -9 `ps axxx | grep ""/Applications"" | awk '{print $1}'`")
		  ShellResults = s.Result
		  LogToFile(CurrentMethodName + ": App Kill Results: " + ShellResults)
		  
		  // Kill PSU specific Applescript that can cause issues
		  LogToFile(CurrentMethodName + ": Killing the psuStartupManger")
		  //s.Execute("kill -9 `ps axxx | grep ""/Library/CLMshared/Startup Items/psuStartupManager.app"" |grep applet | awk '{print $1}'`")
		  ShellResults = s.Result
		  LogToFile(CurrentMethodName + ": StartupManager Kill Results: " + ShellResults)
		  
		  // Log out our user with force
		  LogToFile(CurrentMethodName + ": Tell AppleScript to log it out")
		  // New method to logout with osascript.
		  //s.Execute("osascript -e 'tell application ""System Events"" to keystroke ""q"" using {command down, shift down, option down}'")
		  ShellResults = s.Result
		  LogToFile(CurrentMethodName + ": LogOut AS Results: " + ShellResults)
		  
		  // Log and quit
		  LogToFile(CurrentMethodName + ": <---")
		  
		  Quit
		End Sub
	#tag EndMethod

	#tag Method, Flags = &h0
		Function IdleSeconds() As Integer
		  // Set up variables for idle time
		  // Shell result
		  Dim mIdleSecs As String
		  // Shell exit code
		  Dim merrCode As  Integer
		  // Set up shell
		  Dim s As Shell
		  s=New Shell
		  s.Mode = 0
		  
		  // Check idle time
		  //s.Execute code to check idle time from USB input devices
		  s.Execute "/bin/echo $((`/usr/sbin/ioreg -c IOHIDSystem | /usr/bin/sed -e '/HIDIdleTime/ !{ d' -e 't' -e '}' -e 's/.* = //g' -e 'q'` / 1000000000))"
		  // Set results to mIdleResult
		  mIdleSecs = s.Result
		  // Set error code to mIdleError
		  merrCode = s.ErrorCode
		  // Log mIdleResult for debugging
		  // System.Log(System.LogLevelError, "Method: " + midleSecs)
		  // LogToFile("mIdleSeconds: " + str(mIdleSecs))
		  Return val(mIdleSecs)
		End Function
	#tag EndMethod

	#tag Method, Flags = &h0
		Function makeFolder(mkdirPath As string) As Boolean
		  // Set up variables for idle time
		  // Shell result = mkdirPath
		  // Shell exit code
		  Dim merrCode As  Integer
		  // Set up shell
		  Dim s As Shell
		  s=New Shell
		  s.Mode = 0
		  
		  // Check idle time
		  //s.Execute code to check idle time from USB input devices
		  s.Execute "mkdir -p /Users/Shared"
		  // Set results to mIdleResult
		  mkdirPath = s.Result
		  // Set error code to mIdleError
		  merrCode = s.ErrorCode
		  // Log mIdleResult for debugging
		  // System.Log(System.LogLevelError, "Method: " + midleSecs)
		  // LogToFile("mIdleSeconds: " + str(mIdleSecs))
		  if mkdirPath = "0" then
		    return true
		  else
		    return false
		  end
		End Function
	#tag EndMethod

	#tag Method, Flags = &h0
		Function RemoteControlCheck() As Boolean
		  // Set up variables for idle time
		  Dim stablished as String = "ESTABLISHED"
		  
		  // Shell result
		  Dim rccheck As String
		  // Shell exit code
		  Dim merrCode As  Integer
		  // Set up shell
		  Dim s As Shell
		  s=New Shell
		  s.Mode = 0
		  
		  
		  // Check idle time
		  //s.Execute code to check for connection
		  s.Execute "/usr/sbin/netstat -n | /usr/bin/grep '.5900'| /usr/bin/awk '{print $6}'"
		  
		  // Set results to rccheck
		  // Make the entire returned text uppercase in case Apple ever changes the case to CaMeL CaSe, which would break our code:
		  rccheck = Uppercase(s.Result)
		  
		  //LogToFile("rccheck: " + rccheck)
		  // Set error code to merrCode
		  merrCode = s.ErrorCode
		  
		  // LogToFile("rccheck: "+ rccheck)
		  // LogToFile("stablished: "+ stablished)
		  
		  If ( InStr( rccheck, stablished ) > 0 ) then
		    return true
		  else
		    //LogToFile("rccheck: false")
		    return false
		  End if
		  
		  // System.Log(System.LogLevelError, "Method: " + midleSecs)
		  // LogToFile("mIdleSeconds: " + str(mIdleSecs))
		  
		  
		  
		End Function
	#tag EndMethod

	#tag Method, Flags = &h0
		Sub StartIdleWatch()
		  // if the new WatchForIdleThread is not created, do it now
		  if ( pWatchForIdleThread = nil ) then
		    // instatiate new thread with enumerated type
		    pWatchForIdleThread = _
		    new WatchForIdleThread(Int32(eTaskType.StartIdleWatch ))
		    // add custom methods for run and finished handlers
		    AddHandler pWatchForIdleThread.Run, WeakAddressOf Thread_WaitForIdleTime
		    AddHandler pWatchForIdleThread.Finished, WeakAddressOf Thread_Finished
		  end if
		  // Run the thread if it's not already running:
		  if ( pWatchForIdleThread.State <> Thread.Running ) then
		    // Hide window during idle watch
		    LogoutWarning.Hide()
		    // Set label to default countdown time
		    LogoutWarning.TimeLabel.setString(str(pWaitForUserPromptSeconds))
		    // run the new thread
		    pWatchForIdleThread.Run
		  end if
		End Sub
	#tag EndMethod

	#tag Method, Flags = &h0
		Sub StartUserCountDown()
		  // if the new WatchForIdleThread is not created, do it now
		  if ( pIdleCountDownThread = nil ) then
		    // instatiate new thread with enumerated type
		    pIdleCountDownThread = _
		    new WatchForIdleThread(Int32(eTaskType.StartUserCountDown ))
		    // add custom methods for run and finished handlers
		    AddHandler pIdleCountDownThread.Run, WeakAddressOf Thread_WaitForUserInput
		    AddHandler pIdleCountDownThread.Finished, WeakAddressOf Thread_Finished
		  end if
		  // Run the thread if it's not already running
		  if ( pIdleCountDownThread.State <> Thread.Running ) then
		    // run the new thread
		    pIdleCountDownThread.Run
		  end if
		End Sub
	#tag EndMethod

	#tag Method, Flags = &h21
		Private Sub Thread_Finished(paramThread as WatchForIdleThread)
		  LogToFile(CurrentMethodName + ": --->")
		  
		  // Kill the thread that was used to get here
		  paramThread.Kill
		  // Found out which thread just finished
		  select case Int32 ( paramThread.pTaskType )
		    // If the StartIdleWatch thread just finished
		  case int32( eTaskType.StartIdleWatch )
		    
		    // LogToFile(CurrentMethodName + ": Idle time has expired.")
		    // Show Login Window
		    LogoutWarning.Show()
		    // start logout countdown window
		    StartUserCountDown()
		    
		  case int32 ( eTaskType.StartUserCountDown )
		    
		    // LogToFile(CurrentMethodName + ": No user response from countdown.")
		    // LogToFile(CurrentMethodName + ": LogoutWarning.pMoreTimeAskedFor = " +str(pMoreTimeAskedFor))
		    
		    // If the user (or anyone) asked for more time
		    if (pMoreTimeAskedFor) then
		      // if the user asks for more time, start the idle watch thread again
		      // Restart Idle Watch 
		      StartIdleWatch()
		    else
		      // if the user ran out of time, start the logout
		      // kill user logins
		      forceLogout()
		    end if
		  else
		    
		  end select
		  
		End Sub
	#tag EndMethod

	#tag Method, Flags = &h21
		Private Sub Thread_WaitForIdleTime(paramThread as WatchForIdleThread)
		  // Set up new integer to store idle seconds
		  Dim mIdleResult As Integer
		  // get current idle seconds
		  mIdleResult = IdleSeconds()
		  
		  // LogToFile(CurrentMethodName + " Idle Seconds: " + str(mIdleResult))
		  // LogToFile(CurrentMethodName + " waiting for " + str(LogoutWarning.pComputerIdleAfterNumSeconds))
		  
		  // While our idle result is less than max time
		  while ( mIdleResult < pComputerIdleAfterNumSeconds )
		    // Wait pIdleLoopDelaySeconds each loop
		    App.SleepCurrentThread( pIdleLoopDelaySeconds * 1000 )
		    // Check idle time:
		    mIdleResult = IdleSeconds()
		    // Log our time idle
		    LogToFile(CurrentMethodName + ": Idle Seconds: " + str(mIdleResult))
		  wend
		  
		  // while there is a ARD/VNC session, don't idle out  (netstat -n | grep '.5900')
		  while (RemoteControlCheck() )
		    // Wait pIdleLoopDelaySeconds each loop
		    App.SleepCurrentThread( pIdleLoopDelaySeconds * 1000 )
		    // Log
		    LogToFile(CurrentMethodName + ": Someone is controlling via ARD/VNC, waiting...")
		  wend
		  // When there is not remote control
		  LogToFile(CurrentMethodName + ": No one is controlling via ARD/VNC, continuing...")
		  
		  
		End Sub
	#tag EndMethod

	#tag Method, Flags = &h21
		Private Sub Thread_WaitForUserInput(paramThread as WatchForIdleThread)
		  LogToFile(CurrentMethodName + ": --->")
		  
		  // Set how long to wait before logging out user, set from global variable for LogOutDelay
		  Dim pLogoutDelay as Integer = pWaitForUserPromptSeconds
		  
		  // While we're waiting for the countdown to finish...
		  While pLogoutDelay >= 0
		    // LogToFile(CurrentMethodName + "Time Left: " + str(pLogoutDelay))
		    // Set global variable to the time left
		    pCountDownTime = pLogoutDelay
		    // call updateUI to set countdown label
		    pIdleCountDownThread.UpdateUI()
		    // take a second away from the time left
		    pLogoutDelay = pLogoutDelay - 1
		    // do nothing for 1 second
		    App.SleepCurrentThread( 1000 ) // sleep 1 second
		  Wend
		  
		  // If we're here, the user didn't cancel or log out manaully
		  // user did not ask for more time
		  pMoreTimeAskedFor = false
		  // Log
		  LogToFile(CurrentMethodName + ": Time Out for Response. Log User Out...")
		  // Done here
		  LogToFile(CurrentMethodName + ": <---")
		  // Return to Thread_Finished
		End Sub
	#tag EndMethod


	#tag Note, Name = Icon
		Application Icon use with CC license:
		From http://www.flickr.com/photos/23453447@N02/5107438855/sizes/o/in/photostream/
		By zyrquel
		http://www.flickr.com/photos/23453447@N02/
	#tag EndNote


	#tag Property, Flags = &h0
		pAppVersion As String = "2.0"
	#tag EndProperty

	#tag Property, Flags = &h0
		pComputerIdleAfterNumSeconds As Integer = 900
	#tag EndProperty

	#tag Property, Flags = &h0
		pCountDownTime As Integer
	#tag EndProperty

	#tag Property, Flags = &h0
		pDefaultPrefsFileName As String = "edu.psu.its.clc.IdleLogoutSettings.plist"
	#tag EndProperty

	#tag Property, Flags = &h0
		pDefaultPrefsFSPath As String = "/Library/CLMadmin/Config/"
	#tag EndProperty

	#tag Property, Flags = &h0
		pIdleCountDownThread As WatchForIdleThread
	#tag EndProperty

	#tag Property, Flags = &h0
		pIdleLoopDelaySeconds As Integer = 60
	#tag EndProperty

	#tag Property, Flags = &h0
		pIgnoreGroup As String = "admin"
	#tag EndProperty

	#tag Property, Flags = &h0
		pIgnoreUser As String = "macadmin"
	#tag EndProperty

	#tag Property, Flags = &h0
		pMoreTimeAskedFor As Boolean = true
	#tag EndProperty

	#tag Property, Flags = &h0
		pWaitForUserPromptSeconds As Integer = 90
	#tag EndProperty

	#tag Property, Flags = &h0
		pWatchForIdleThread As WatchForIdleThread
	#tag EndProperty


	#tag Constant, Name = kEditClear, Type = String, Dynamic = False, Default = \"&Delete", Scope = Public
		#Tag Instance, Platform = Windows, Language = Default, Definition  = \"&Delete"
		#Tag Instance, Platform = Linux, Language = Default, Definition  = \"&Delete"
	#tag EndConstant

	#tag Constant, Name = kFileQuit, Type = String, Dynamic = False, Default = \"&Quit", Scope = Public
		#Tag Instance, Platform = Windows, Language = Default, Definition  = \"E&xit"
	#tag EndConstant

	#tag Constant, Name = kFileQuitShortcut, Type = String, Dynamic = False, Default = \"", Scope = Public
		#Tag Instance, Platform = Mac OS, Language = Default, Definition  = \"Cmd+Q"
		#Tag Instance, Platform = Linux, Language = Default, Definition  = \"Ctrl+Q"
	#tag EndConstant


	#tag Enum, Name = eTaskType, Type = Int32, Flags = &h0
		StartIdleWatch
		StartUserCountDown
	#tag EndEnum


	#tag ViewBehavior
		#tag ViewProperty
			Name="pAppVersion"
			Group="Behavior"
			InitialValue="1.1"
			Type="String"
			EditorType="MultiLineEditor"
		#tag EndViewProperty
		#tag ViewProperty
			Name="pComputerIdleAfterNumSeconds"
			Group="Behavior"
			InitialValue="900"
			Type="Integer"
		#tag EndViewProperty
		#tag ViewProperty
			Name="pCountDownTime"
			Group="Behavior"
			Type="Integer"
		#tag EndViewProperty
		#tag ViewProperty
			Name="pDefaultPrefsFileName"
			Group="Behavior"
			InitialValue="edu.psu.its.clc.IdleLogoutSettings.plist"
			Type="String"
			EditorType="MultiLineEditor"
		#tag EndViewProperty
		#tag ViewProperty
			Name="pDefaultPrefsFSPath"
			Group="Behavior"
			InitialValue="/Library/CLMadmin/Config/"
			Type="String"
			EditorType="MultiLineEditor"
		#tag EndViewProperty
		#tag ViewProperty
			Name="pIdleLoopDelaySeconds"
			Group="Behavior"
			InitialValue="60"
			Type="Integer"
		#tag EndViewProperty
		#tag ViewProperty
			Name="pIgnoreGroup"
			Group="Behavior"
			InitialValue="admin"
			Type="String"
			EditorType="MultiLineEditor"
		#tag EndViewProperty
		#tag ViewProperty
			Name="pIgnoreUser"
			Group="Behavior"
			InitialValue="macadmin"
			Type="String"
			EditorType="MultiLineEditor"
		#tag EndViewProperty
		#tag ViewProperty
			Name="pMoreTimeAskedFor"
			Group="Behavior"
			InitialValue="true"
			Type="Boolean"
		#tag EndViewProperty
		#tag ViewProperty
			Name="pWaitForUserPromptSeconds"
			Group="Behavior"
			InitialValue="90"
			Type="Integer"
		#tag EndViewProperty
	#tag EndViewBehavior
End Class
#tag EndClass