Recently, out of curiosity I started writing an applescript code to make an app to login onto my gmail web account. Once I started it I realized there are many things I needed to consider. Here are the different scenarios:
1) One just booted the system. Hence, the app should start Safari, open Gmail login page and then login with credentials stored in a keychain.
2) One has already logged into his gmail account. But, the page has been closed but account hasn't been logged out. In this scenario, app should not enter the username and password as when it tries to open the Gmail login page your Gmail opens up with your inbox with emails since you already logged in before closing the page.
3) You logged out but didn't close the page. Then, after a while you want to log back in. In this case it should not reopen the login page but find the already logged out page that's open and put the credentials.
4) The account is already opened and active in a tab that is not in focus right now. In this situation, app should simply bring this tab to the front.
5) The other case is where Safari is minimized with account is already open but page is closed, is logged out but page is open, etc.
6) Most important thing is to make sure Gmail login page loads before it starts entering username and password.
7) At the same time accessing username and password has to be secure.
8) Multiple windows and tabs are open and the app should cycle through them to find some of the above mentioned cases.
This code covers all these cases. I'm sure there are many more one might come across. For now this is working great for me on my 3 different macs (office iMac, home iMac and Macbook Pro). All these 3 have mountain lion operating system with Safari 6.0.3.
This code uses apple security tool to access the username and password I have stored in a keychain. This makes it very secure unlike storing the password in the script it self (check for one of my previous scripts).
The one important thing one needs to remember is to change the keychain location in the script based on their username. For example, on all my macs my username is manchu and I store the keychain in the default location of /Users/manchu/Library/Keychain. So, if you save it in the default location then you should just change manchu in the above path with your home directory name (the one that has home/house symbol in the finder window on the left pane).
This code ensures that page has successfully loaded by checking whether File --> Save as becomes active in the Safari menu. I'm not sure whether this is the right way but some how it appears to be working ok. I have given 10 seconds for the page to load. If it doesn't happen before that it will let you know that page has failed to load through announcement as well as display dialog. If you don't want your Mac to announce it then hash the lines with "say".
If you'd like to use this script, then make sure you create a keychain with your Gmail login credentials in it and change the path to it in the script below. You can see my post before this one to know how to create a keychain.
Here is the code:
# Applescript to open Gmail account in Safari.
# Written by Sreedhar Manchu.
# Password retrieval from keychain access has been accomplished with the code
# found on www.vickash.com. The page is titled "More Secure Passwords In Applescript"
set IsDefined to true
set theCredentials to getCredentials of ¬
"GMailPersonal" from "/Users/manchu/Library/Keychains/WebLogins.keychain"
set userName to account of theCredentials
set thePW to password of theCredentials
set theTitle to "" & userName & "@gmail.com - Gmail" as string
if appIsRunning("Safari") then
tell application "Safari"
activate
if windows is not {} then
activate
repeat with j from (count of windows) to 1 by -1
repeat with i from (count of tabs of window j) to 1 by -1
set thisTab to tab i of window j
set thePageName to name of thisTab
#display dialog thePageName
if thePageName contains theTitle then
set current tab of window j to thisTab
set windowNum to j
set alreadyLogged to true
end if
end repeat
end repeat
end if
end tell
end if
try
get alreadyLogged
on error
set IsDefined to false
end try
if IsDefined then
tell application "Safari"
activate
set index of window windowNum to 1
end tell
else
set IsDefined to true
set theTitle to "Gmail: Email from Google" as string
tell application "Safari"
activate
if windows is not {} then
activate
repeat with j from (count of windows) to 1 by -1
repeat with i from (count of tabs of window j) to 1 by -1
set thisTab to tab i of window j
set thePageName to name of thisTab
if thePageName is equal to theTitle then
#set tabNum to i
set theWindows to windows
set winOne to item j of theWindows
if (miniaturized of winOne) then
#set miniaturized of winOne to false
activate
#delay 0.5
end if
#set currentTab to item i of tabs of window j
set currentTab to item i of tabs of winOne
#set winOne to item j of windows
set alreadyOpen to true
end if
end repeat
end repeat
end if
end tell
try
get alreadyOpen
on error
set IsDefined to false
end try
if IsDefined then
tell application "Safari"
activate
tell winOne
set index to 1
set visible to false
set visible to true
end tell
#set thisTab to tab tabNum of window 1
#set current tab of window 1 to thisTab
set current tab of window 1 to currentTab
tell application "System Events"
keystroke userName
keystroke tab
keystroke thePW
keystroke return
end tell
end tell
else
tell application "Safari"
activate
if windows is not {} then
tell window 1
if (miniaturized) then
set miniaturized to false
end if
set current tab to (make new tab with properties {URL:"https://mail.google.com"})
#set current tab to (make new tab with properties {URL:"http://www.bsnl.co.in"})
end tell
else
tell application "Safari"
activate
make new document with properties {URL:"https://mail.google.com"}
end tell
end if
end tell
if page_loaded(10) then
set IsDefined to false
tell application "Safari"
activate
#set theURL to URL of current tab of front window as text
#set thePageName to name of current tab of front window as string
set thePageName to name of current tab of window 1 as string
if thePageName as string is equal to "" or thePageName as string is equal to null or thePageName is equal to missing value then
set pageIsNotLoading to true
end if
end tell
try
get pageIsNotLoading
on error
set IsDefined to true
end try
if IsDefined then
#display dialog thePageName
set theTitle to "" & userName & "@gmail.com - Gmail" as string
if thePageName does not contain theTitle then
tell application "Safari"
activate
tell application "System Events"
keystroke userName
keystroke tab
keystroke thePW
keystroke return
say "Gmail login has been successful"
end tell
end tell
end if
else
say "Either page has failed to load or Gmail is taking time"
display dialog "Either page has failed to load or Gmail is taking time!"
end if
end if
end if
end if
on page_loaded(timeout_value) -- in seconds
tell application "System Events"
tell process "Safari"
tell menu 1 of menu bar item "File" of menu bar 1
repeat with i from 1 to timeout_value
if enabled of menu item "Save as..." is false then
delay 0.5
else
return true
end if
if i is timeout_value then
#set timeoutValue to timeout_value * 0.5
beep
display dialog "" & timeout_value * 0.5 & " seconds have elapsed; Either page has failed to load or Gmail is taking time!"
return false
end if
end repeat
end tell
end tell
end tell
end page_loaded
on extractData(theText, theFieldName, theEndDelimiter, spaces)
set theDataStart to the offset of theFieldName in theText
if theDataStart = 0 then
return ""
else
set theDataStart to theDataStart + (length of theFieldName) + spaces
set theData to text theDataStart through end of theText
set theDataEnd to ((offset of theEndDelimiter in theData) - 1)
set theData to text 1 through theDataEnd of theData
end if
end extractData
on getCredentials of theKeychainItem from theKeychain
set theKeychainPath to (POSIX path of theKeychain) as text
try
set theKeychainAlias to (POSIX file theKeychainPath) as alias
set theKeychainPath to (POSIX path of theKeychainAlias)
on error
return "Keychain file not found at specified location: " & ¬
theKeychain as text
end try
try
set theResult to do shell script ¬
"security 2>&1 find-generic-password -gs " & ¬
theKeychainItem & " " & ¬
quoted form of theKeychainPath
set theAccount to extractData(theResult, "\"acct\"<blob>=\"", "\"", 0)
set thePassword to extractData(theResult, "password: \"", "\"", 0)
return {account:theAccount, password:thePassword}
on error
return "Generic password item specifeid does not exist in keychain: " & ¬
theKeychainItem
end try
end getCredentials
on appIsRunning(appName)
tell application "System Events" to (name of processes) contains appName
end appIsRunning