Move fileRef outside of try (where it can be accessed)

-- core configuration --

-- configurable options --

-- limit number of songs to improve efficiency
property resultLimit : 25
-- whether or not to retrieve/display album artwork for each result
property albumArtEnabled : true

-- workflow parameters --

property homeFolder : (path to home folder as text)
property libraryFolder : (path to library folder from user domain as text)
property cacheFolder : (libraryFolder & "Caches:")
property alfredWorkflowDataFolder : (cacheFolder & "com.runningwithcrayons.Alfred-3:Workflow Data:")
property bundleId : "com.calebevans.playsong"
property workflowCacheFolder : (alfredWorkflowDataFolder & bundleId & ":") as text
property artworkCacheFolderName : "Album Artwork"
property artworkCachePath : (workflowCacheFolder & artworkCacheFolderName & ":")
property songArtworkNameSep : " | "
property defaultIconName : "icon-noartwork.png"
-- the name of the playlist used by the workflow for playing songs
property workflowPlaylistName : "Alfred Play Song"
-- the text used to determine if a track is an audio file
property songDescriptor : "audio"
-- list of Alfred results
property resultList : {}

-- replaces substring in string with another substring
on replace(replaceThis, replaceWith, theString)

	set oldDelims to AppleScript's text item delimiters
	set AppleScript's text item delimiters to replaceThis
	set strItems to text items of theString
	set AppleScript's text item delimiters to replaceWith
	set newString to strItems as text
	set AppleScript's text item delimiters to oldDelims
	return newString

end replace

-- retrieve the plural/singular form of a quantity based on the given number
on quantifyNumber(theNumber, quantityName, pluralQuantityName)

	if theNumber is 1 then

		set theString to (theNumber as text) & quantityName

	else

		set theString to (theNumber as text) & pluralQuantityName

	end if

	return theString

end quantifyNumber

-- encodes XML reserved characters in the given string
on encodeXmlChars(theString)

	set theString to replace("&", "&", theString)
	set theString to replace("<", "<", theString)
	set theString to replace(">", ">", theString)
	set theString to replace("\"", """, theString)
	set theString to replace("'", "'", theString)
	return theString

end encodeXmlChars

-- decodes XML reserved characters in the given string
on decodeXmlChars(theString)

	set theString to replace("&", "&", theString)
	set theString to replace("<", "<", theString)
	set theString to replace(">", ">", theString)
	set theString to replace(""", "\"", theString)
	set theString to replace("'", "'", theString)
	return theString

end decodeXmlChars

-- adds Alfred result to result list
on addResult(theResult)

	copy theResult to the end of resultList

end addResult

-- adds item for "No Results" message
on addNoResultsItem(query, queryType)

	addResult({uid:"no-results", valid:"no", title:"No Results Found", subtitle:("No " & queryType & "s matching '" & query & "'"), icon:defaultIconName})

end addNoResultsItem

on resultListIsFull()

	return (length of resultList is resultLimit)

end resultListIsFull

on resultListIsEmpty()

	return (length of resultList is 0)

end resultListIsFull

-- builds Alfred result item as XML
on getResultXml(theResult)

	-- encode reserved XML characters
	set resultUid to encodeXmlChars(uid of theResult)
	set resultValid to (valid of theResult) as text
	set resultTitle to encodeXmlChars(title of theResult)
	set resultSubtitle to encodeXmlChars(subtitle of theResult)

	if (icon of theResult) contains ":" then

		set resultIcon to encodeXmlChars(POSIX path of icon of theResult)

	else

		set resultIcon to icon of theResult

	end if

	set xml to "<item uid='" & resultUid & "' arg='" & resultUid & "' valid='" & resultValid & "'>"
	set xml to xml & "<title>" & resultTitle & "</title>"
	set xml to xml & "<subtitle>" & resultSubtitle & "</subtitle>"
	set xml to xml & "<icon>" & resultIcon & "</icon>"
	set xml to xml & "</item>"
	return xml

end getResultXml

-- retrieves XML document for Alfred results
on getResultListXml()

	set xml to "<?xml version='1.0'?><items>"

	repeat with theResult in resultList

		set xml to xml & getResultXml(theResult)

	end repeat

	set xml to xml & "</items>"
	return xml

end getResultListXml

-- writes the given content to the given file
on fileWrite(theFile, theContent)

	set fileRef to open for access theFile with write permission
	try
		set eof of fileRef to 0
		write theContent to fileRef starting at eof
		close access fileRef
	on error
		close access fileRef
	end try

end fileWrite

-- builds path to album art for the given song
on getSongArtworkPath(theSong)

	if albumArtEnabled is false then return defaultIconName

	tell application "iTunes"

		set songArtist to artist of theSong
		set songAlbum to album of theSong
		-- generate a unique identifier for that album
		set songArtworkName to (songArtist & songArtworkNameSep & songAlbum) as text
		-- remove forbidden path characters
		set songArtworkName to replace(":", "", songArtworkName) of me
		set songArtworkName to replace("/", "", songArtworkName) of me
		set songArtworkName to replace(".", "", songArtworkName) of me
		set songArtworkPath to (artworkCachePath & songArtworkName & ".jpg")

	end tell

	tell application "Finder"

		-- cache artwork if it's not already cached
		if not (songArtworkPath exists) then

			tell application "iTunes"

				set songArtworks to artworks of theSong
				-- only save artwork if artwork exists for this song
				if (length of songArtworks) is 0 then

					-- use default iTunes icon if song has no artwork
					set songArtworkPath to defaultIconName

				else

					-- save artwork to file
					set songArtwork to data of (item 1 of songArtworks)
					fileWrite(songArtworkPath, songArtwork) of me

				end if

			end tell

		end if

	end tell

	return songArtworkPath

end getSongArtworkPath

-- creates album artwork cache
on createWorkflowPlaylist()

	tell application "iTunes"

		if not (user playlist workflowPlaylistName exists) then

			make new user playlist with properties {name:workflowPlaylistName, shuffle:false}

		end if

	end tell

end createWorkflowPlaylist

on clearQueue()

	tell application "iTunes"

		if user playlist workflowPlaylistName exists then

			delete tracks of user playlist workflowPlaylistName

		end if

	end tell

end clearQueue

on queueSongs(theSongs)

	tell application "iTunes"

		repeat with theSong in theSongs

			duplicate theSong to user playlist workflowPlaylistName

		end repeat

	end tell

end queueSongs

on playQueue()

	tell application "iTunes"

		if number of tracks in user playlist workflowPlaylistName is not 0 then

			play user playlist workflowPlaylistName

		end if

	end tell

end playQueue

-- brings queue into view in iTunes window
on focusQueue()

	tell application "iTunes"

		reveal user playlist workflowPlaylistName

	end tell

end focusQueue

on getPlaylistSongs(playlistId)

	tell application "iTunes"

		set thePlaylist to first user playlist whose id is playlistId
		set theSongs to every track of thePlaylist

	end tell

	return theSongs

end getPlaylistSongs

-- retrieves list of artist names for the given genre
on getGenreArtists(genreName)

	tell application "iTunes"

		set theSongs to every track of playlist 2 whose genre is genreName and kind contains songDescriptor
		set artistNames to {}

		repeat with theSong in theSongs

			if (artist of theSong) is not in artistNames then

				set artistNames to artistNames & (artist of theSong)

			end if

		end repeat

	end tell

	return artistNames

end getGenreArtists

-- retrieves list of songs within the given genre, sorted by artist
on getGenreSongs(genreName)

	set artistNames to getGenreArtists(genreName) of me
	set theSongs to {}

	repeat with artistName in artistNames

		set theSongs to theSongs & getArtistSongs(artistName) of me

	end repeat

	return theSongs

end getGenreSongs

-- retrieves list of album names for the given artist
on getArtistAlbums(artistName)

	tell application "iTunes"

		set theSongs to every track of playlist 2 whose artist is artistName and kind contains songDescriptor
		set albumNames to {}

		repeat with theSong in theSongs

			if (album of theSong) is not in albumNames then

				set albumNames to albumNames & (album of theSong)

			end if

		end repeat

	end tell

	return albumNames

end getArtistAlbums

-- retrieves list of songs by the given artist, sorted by album
on getArtistSongs(artistName)

	tell application "iTunes"

		set albumNames to getArtistAlbums(artistName) of me
		set theSongs to {}

		repeat with albumName in albumNames

			set albumSongs to (every track of playlist 2 whose artist is artistName and album is albumName and kind contains songDescriptor)
			set albumSongs to sortSongsByAlbumOrder(albumSongs) of me
			set theSongs to theSongs & albumSongs

		end repeat

	end tell

	return theSongs

end getArtistSongs

-- retrieves list of songs in the given album
on getAlbumSongs(albumName)

	tell application "iTunes"

		set theSongs to every track of playlist 2 whose album is albumName and kind contains songDescriptor
		set theSongs to sortSongsByAlbumOrder(theSongs) of me

	end tell

	return theSongs

end getAlbumSongs

-- sorts songs from the same album by track number
on sortSongsByAlbumOrder(theSongs)

	tell application "iTunes"

		set theSongsSorted to theSongs

		if length of theSongs is greater than 1 then

			set trackCount to track count of (item 1 of theSongs)
			set discCount to disc count of (item 1 of theSongs)

			if trackCount is not 0 and discCount is not 0 then

				set theSongsSorted to {} as list

				repeat with discIndex from 1 to discCount

					repeat with songIndex from 1 to trackCount

						repeat with theSong in theSongs

							if disc number of theSong is discIndex and track number of theSong is songIndex then

								set nextSong to theSong
								copy nextSong to the end of theSongsSorted

							end if

						end repeat

					end repeat

				end repeat

			end if

		end if

	end tell

	return theSongsSorted

end sortSongsByAlbumOrder

-- retrieves the song with the given ID
on getSong(songId)

	tell application "iTunes"

		set theSong to first track of playlist 2 whose database ID is songId and kind contains songDescriptor

	end tell

	return theSong

end getSong

-- retrieves a list of objects or names matching the given query and type
on getResultsFromQuery(query, queryType)

	set evalScript to run script "
	script

		on findResults(query, queryType, resultLimit, songDescriptor)

			tell application \"iTunes\"

				set theSongs to (get every track in playlist 2 whose " & queryType & " starts with query and kind contains songDescriptor)

				if length of theSongs < resultLimit then

					set theSongs to theSongs & (get every track in playlist 2 whose " & queryType & " contains (space & query) and " & queryType & " does not start with query and kind contains songDescriptor)

				end if

				if length of theSongs < resultLimit then

					set theSongs to theSongs & (get every track in playlist 2 whose " & queryType & " contains query and " & queryType & " does not start with query and " & queryType & " does not contain (space & query) and kind contains songDescriptor)

				end if

				if queryType is \"name\" then

					if length of theSongs > resultLimit then

						set theSongs to items 1 thru resultLimit of theSongs

					end if

					set theResults to theSongs

				else

					set theResults to {}

					repeat with theSong in theSongs

						if length of theResults is resultLimit then exit repeat

						set theResult to " & queryType & " of theSong

						if theResult is not in theResults then

							set theResults to theResults & theResult

						end if

					end repeat

				end if

			end tell

			return theResults

		end findResults

	end script
	"

	evalScript's findResults(query, queryType, resultLimit, songDescriptor)

end getResultsFromQuery

-- returns the given string with leading and trailing whitespace removed
on trimWhitespace(theString)

	-- trim leading whitespace
	repeat while theString begins with space

		if length of theString is 1 then return ""
		set theString to text 2 thru end of theString

	end repeat

	-- trim trailing whitespace
	repeat while theString ends with space

		set theString to text 1 thru ((length of theString) - 1) of theString

	end repeat

	return theString

end trimWhitespace

-- queues the song with the given ID
on queueSong(songId)

	set theSong to getSong(songId)
	queueSongs({theSong})

end queueSong

-- queues all songs belonging to the given album
on queueAlbum(albumName)

	set albumName to decodeXmlChars(albumName)
	set theSongs to getAlbumSongs(albumName)
	queueSongs(theSongs)

end queueAlbum

-- queues all songs by the given artist
on queueArtist(artistName)

	set artistName to decodeXmlChars(artistName)
	set theSongs to getArtistSongs(artistName)
	queueSongs(theSongs)

end queueArtist

-- queues all songs within the given genre
on queueGenre(genreName)

	set genreName to decodeXmlChars(genreName)
	set theSongs to getGenreSongs(genreName)
	queueSongs(theSongs)

end queueGenre

-- queues all songs in the given playlist
on queuePlaylist(playlistId)

	set theSongs to getPlaylistSongs(playlistId)
	queueSongs(theSongs)

end queuePlaylist

-- parses the given result query to retrieve type and id of item to queue
on parseResultQuery(query)

	set pos to offset of "-" in query
	set theType to text 1 thru (pos - 1) of query
	set theId to text (pos + 1) thru end of query
	return {type:theType, id:theId}

end parseResultQuery

on queue(query)

	set typeAndId to parseResultQuery(query)
	set theType to type of typeAndId
	set theId to id of typeAndId

	createWorkflowPlaylist()

	if theType is "song" then
		queueSong(theId)
	else if theType is "album" then
		queueAlbum(theId)
	else if theType is "artist" then
		queueArtist(theId)
	else if theType is "genre" then
		queueGenre(theId)
	else if theType is "playlist" then
		queuePlaylist(theId)
	else
		log "Unknown type: " & theType
	end if

	focusQueue()

end queue

on play(query)

	clearQueue()
	queue(query)
	focusQueue()
	playQueue()

end play

-- core configuration --

-- configurable options --

-- limit number of songs to improve efficiency
property resultLimit : 25
-- whether or not to retrieve/display album artwork for each result
property albumArtEnabled : true

-- workflow parameters --

property homeFolder : (path to home folder as text)
property libraryFolder : (path to library folder from user domain as text)
property cacheFolder : (libraryFolder & "Caches:")
property alfredWorkflowDataFolder : (cacheFolder & "com.runningwithcrayons.Alfred-3:Workflow Data:")
property bundleId : "com.calebevans.playsong"
property workflowCacheFolder : (alfredWorkflowDataFolder & bundleId & ":") as text
property artworkCacheFolderName : "Album Artwork"
property artworkCachePath : (workflowCacheFolder & artworkCacheFolderName & ":")
property songArtworkNameSep : " | "
property defaultIconName : "icon-noartwork.png"
-- the name of the playlist used by the workflow for playing songs
property workflowPlaylistName : "Alfred Play Song"
-- the text used to determine if a track is an audio file
property songDescriptor : "audio"
-- list of Alfred results
property resultList : {}

-- replaces substring in string with another substring
on replace(replaceThis, replaceWith, theString)

	set oldDelims to AppleScript's text item delimiters
	set AppleScript's text item delimiters to replaceThis
	set strItems to text items of theString
	set AppleScript's text item delimiters to replaceWith
	set newString to strItems as text
	set AppleScript's text item delimiters to oldDelims
	return newString

end replace

-- retrieve the plural/singular form of a quantity based on the given number
on quantifyNumber(theNumber, quantityName, pluralQuantityName)

	if theNumber is 1 then

		set theString to (theNumber as text) & quantityName

	else

		set theString to (theNumber as text) & pluralQuantityName

	end if

	return theString

end quantifyNumber

-- encodes XML reserved characters in the given string
on encodeXmlChars(theString)

	set theString to replace("&", "&", theString)
	set theString to replace("<", "<", theString)
	set theString to replace(">", ">", theString)
	set theString to replace("\"", """, theString)
	set theString to replace("'", "'", theString)
	return theString

end encodeXmlChars

-- decodes XML reserved characters in the given string
on decodeXmlChars(theString)

	set theString to replace("&", "&", theString)
	set theString to replace("<", "<", theString)
	set theString to replace(">", ">", theString)
	set theString to replace(""", "\"", theString)
	set theString to replace("'", "'", theString)
	return theString

end decodeXmlChars

-- adds Alfred result to result list
on addResult(theResult)

	copy theResult to the end of resultList

end addResult

-- adds item for "No Results" message
on addNoResultsItem(query, queryType)

	addResult({uid:"no-results", valid:"no", title:"No Results Found", subtitle:("No " & queryType & "s matching '" & query & "'"), icon:defaultIconName})

end addNoResultsItem

on resultListIsFull()

	return (length of resultList is resultLimit)

end resultListIsFull

on resultListIsEmpty()

	return (length of resultList is 0)

end resultListIsFull

-- builds Alfred result item as XML
on getResultXml(theResult)

	-- encode reserved XML characters
	set resultUid to encodeXmlChars(uid of theResult)
	set resultValid to (valid of theResult) as text
	set resultTitle to encodeXmlChars(title of theResult)
	set resultSubtitle to encodeXmlChars(subtitle of theResult)

	if (icon of theResult) contains ":" then

		set resultIcon to encodeXmlChars(POSIX path of icon of theResult)

	else

		set resultIcon to icon of theResult

	end if

	set xml to "<item uid='" & resultUid & "' arg='" & resultUid & "' valid='" & resultValid & "'>"
	set xml to xml & "<title>" & resultTitle & "</title>"
	set xml to xml & "<subtitle>" & resultSubtitle & "</subtitle>"
	set xml to xml & "<icon>" & resultIcon & "</icon>"
	set xml to xml & "</item>"
	return xml

end getResultXml

-- retrieves XML document for Alfred results
on getResultListXml()

	set xml to "<?xml version='1.0'?><items>"

	repeat with theResult in resultList

		set xml to xml & getResultXml(theResult)

	end repeat

	set xml to xml & "</items>"
	return xml

end getResultListXml

-- writes the given content to the given file
on fileWrite(theFile, theContent)

	try

		set fileRef to open for access theFile with write permission
		set eof of fileRef to 0
		write theContent to fileRef starting at eof
		close access fileRef

	on error

		close access fileRef

	end try

end fileWrite

-- builds path to album art for the given song
on getSongArtworkPath(theSong)

	if albumArtEnabled is false then return defaultIconName

	tell application "iTunes"

		set songArtist to artist of theSong
		set songAlbum to album of theSong
		-- generate a unique identifier for that album
		set songArtworkName to (songArtist & songArtworkNameSep & songAlbum) as text
		-- remove forbidden path characters
		set songArtworkName to replace(":", "", songArtworkName) of me
		set songArtworkName to replace("/", "", songArtworkName) of me
		set songArtworkName to replace(".", "", songArtworkName) of me
		set songArtworkPath to (artworkCachePath & songArtworkName & ".jpg")

	end tell

	tell application "Finder"

		-- cache artwork if it's not already cached
		if not (songArtworkPath exists) then

			tell application "iTunes"

				set songArtworks to artworks of theSong
				-- only save artwork if artwork exists for this song
				if (length of songArtworks) is 0 then

					-- use default iTunes icon if song has no artwork
					set songArtworkPath to defaultIconName

				else

					-- save artwork to file
					set songArtwork to data of (item 1 of songArtworks)
					fileWrite(songArtworkPath, songArtwork) of me

				end if

			end tell

		end if

	end tell

	return songArtworkPath

end getSongArtworkPath

-- creates album artwork cache
on createWorkflowPlaylist()

	tell application "iTunes"

		if not (user playlist workflowPlaylistName exists) then

			make new user playlist with properties {name:workflowPlaylistName, shuffle:false}

		end if

	end tell

end createWorkflowPlaylist

on clearQueue()

	tell application "iTunes"

		if user playlist workflowPlaylistName exists then

			delete tracks of user playlist workflowPlaylistName

		end if

	end tell

end clearQueue

on queueSongs(theSongs)

	tell application "iTunes"

		repeat with theSong in theSongs

			duplicate theSong to user playlist workflowPlaylistName

		end repeat

	end tell

end queueSongs

on playQueue()

	tell application "iTunes"

		if number of tracks in user playlist workflowPlaylistName is not 0 then

			play user playlist workflowPlaylistName

		end if

	end tell

end playQueue

-- brings queue into view in iTunes window
on focusQueue()

	tell application "iTunes"

		reveal user playlist workflowPlaylistName

	end tell

end focusQueue

on getPlaylistSongs(playlistId)

	tell application "iTunes"

		set thePlaylist to first user playlist whose id is playlistId
		set theSongs to every track of thePlaylist

	end tell

	return theSongs

end getPlaylistSongs

-- retrieves list of artist names for the given genre
on getGenreArtists(genreName)

	tell application "iTunes"

		set theSongs to every track of playlist 2 whose genre is genreName and kind contains songDescriptor
		set artistNames to {}

		repeat with theSong in theSongs

			if (artist of theSong) is not in artistNames then

				set artistNames to artistNames & (artist of theSong)

			end if

		end repeat

	end tell

	return artistNames

end getGenreArtists

-- retrieves list of songs within the given genre, sorted by artist
on getGenreSongs(genreName)

	set artistNames to getGenreArtists(genreName) of me
	set theSongs to {}

	repeat with artistName in artistNames

		set theSongs to theSongs & getArtistSongs(artistName) of me

	end repeat

	return theSongs

end getGenreSongs

-- retrieves list of album names for the given artist
on getArtistAlbums(artistName)

	tell application "iTunes"

		set theSongs to every track of playlist 2 whose artist is artistName and kind contains songDescriptor
		set albumNames to {}

		repeat with theSong in theSongs

			if (album of theSong) is not in albumNames then

				set albumNames to albumNames & (album of theSong)

			end if

		end repeat

	end tell

	return albumNames

end getArtistAlbums

-- retrieves list of songs by the given artist, sorted by album
on getArtistSongs(artistName)

	tell application "iTunes"

		set albumNames to getArtistAlbums(artistName) of me
		set theSongs to {}

		repeat with albumName in albumNames

			set albumSongs to (every track of playlist 2 whose artist is artistName and album is albumName and kind contains songDescriptor)
			set albumSongs to sortSongsByAlbumOrder(albumSongs) of me
			set theSongs to theSongs & albumSongs

		end repeat

	end tell

	return theSongs

end getArtistSongs

-- retrieves list of songs in the given album
on getAlbumSongs(albumName)

	tell application "iTunes"

		set theSongs to every track of playlist 2 whose album is albumName and kind contains songDescriptor
		set theSongs to sortSongsByAlbumOrder(theSongs) of me

	end tell

	return theSongs

end getAlbumSongs

-- sorts songs from the same album by track number
on sortSongsByAlbumOrder(theSongs)

	tell application "iTunes"

		set theSongsSorted to theSongs

		if length of theSongs is greater than 1 then

			set trackCount to track count of (item 1 of theSongs)
			set discCount to disc count of (item 1 of theSongs)

			if trackCount is not 0 and discCount is not 0 then

				set theSongsSorted to {} as list

				repeat with discIndex from 1 to discCount

					repeat with songIndex from 1 to trackCount

						repeat with theSong in theSongs

							if disc number of theSong is discIndex and track number of theSong is songIndex then

								set nextSong to theSong
								copy nextSong to the end of theSongsSorted

							end if

						end repeat

					end repeat

				end repeat

			end if

		end if

	end tell

	return theSongsSorted

end sortSongsByAlbumOrder

-- retrieves the song with the given ID
on getSong(songId)

	tell application "iTunes"

		set theSong to first track of playlist 2 whose database ID is songId and kind contains songDescriptor

	end tell

	return theSong

end getSong

-- retrieves a list of objects or names matching the given query and type
on getResultsFromQuery(query, queryType)

	set evalScript to run script "
	script

		on findResults(query, queryType, resultLimit, songDescriptor)

			tell application \"iTunes\"

				set theSongs to (get every track in playlist 2 whose " & queryType & " starts with query and kind contains songDescriptor)

				if length of theSongs < resultLimit then

					set theSongs to theSongs & (get every track in playlist 2 whose " & queryType & " contains (space & query) and " & queryType & " does not start with query and kind contains songDescriptor)

				end if

				if length of theSongs < resultLimit then

					set theSongs to theSongs & (get every track in playlist 2 whose " & queryType & " contains query and " & queryType & " does not start with query and " & queryType & " does not contain (space & query) and kind contains songDescriptor)

				end if

				if queryType is \"name\" then

					if length of theSongs > resultLimit then

						set theSongs to items 1 thru resultLimit of theSongs

					end if

					set theResults to theSongs

				else

					set theResults to {}

					repeat with theSong in theSongs

						if length of theResults is resultLimit then exit repeat

						set theResult to " & queryType & " of theSong

						if theResult is not in theResults then

							set theResults to theResults & theResult

						end if

					end repeat

				end if

			end tell

			return theResults

		end findResults

	end script
	"

	evalScript's findResults(query, queryType, resultLimit, songDescriptor)

end getResultsFromQuery

-- returns the given string with leading and trailing whitespace removed
on trimWhitespace(theString)

	-- trim leading whitespace
	repeat while theString begins with space

		if length of theString is 1 then return ""
		set theString to text 2 thru end of theString

	end repeat

	-- trim trailing whitespace
	repeat while theString ends with space

		set theString to text 1 thru ((length of theString) - 1) of theString

	end repeat

	return theString

end trimWhitespace

-- queues the song with the given ID
on queueSong(songId)

	set theSong to getSong(songId)
	queueSongs({theSong})

end queueSong

-- queues all songs belonging to the given album
on queueAlbum(albumName)

	set albumName to decodeXmlChars(albumName)
	set theSongs to getAlbumSongs(albumName)
	queueSongs(theSongs)

end queueAlbum

-- queues all songs by the given artist
on queueArtist(artistName)

	set artistName to decodeXmlChars(artistName)
	set theSongs to getArtistSongs(artistName)
	queueSongs(theSongs)

end queueArtist

-- queues all songs within the given genre
on queueGenre(genreName)

	set genreName to decodeXmlChars(genreName)
	set theSongs to getGenreSongs(genreName)
	queueSongs(theSongs)

end queueGenre

-- queues all songs in the given playlist
on queuePlaylist(playlistId)

	set theSongs to getPlaylistSongs(playlistId)
	queueSongs(theSongs)

end queuePlaylist

-- parses the given result query to retrieve type and id of item to queue
on parseResultQuery(query)

	set pos to offset of "-" in query
	set theType to text 1 thru (pos - 1) of query
	set theId to text (pos + 1) thru end of query
	return {type:theType, id:theId}

end parseResultQuery

on queue(query)

	set typeAndId to parseResultQuery(query)
	set theType to type of typeAndId
	set theId to id of typeAndId

	createWorkflowPlaylist()

	if theType is "song" then
		queueSong(theId)
	else if theType is "album" then
		queueAlbum(theId)
	else if theType is "artist" then
		queueArtist(theId)
	else if theType is "genre" then
		queueGenre(theId)
	else if theType is "playlist" then
		queuePlaylist(theId)
	else
		log "Unknown type: " & theType
	end if

	focusQueue()

end queue

on play(query)

	clearQueue()
	queue(query)
	focusQueue()
	playQueue()

end play


Fix #114: GrowlTunes hanging if pictd has died.

As noted in the ticket, the fix here is to ask for the raw data, not the PICT data, for the artwork.

FasdUAS 1.101.10
k
lH
QH

O=
k<

r

mfsck
o0returnvaluereturnValue
r
n
1

pnam
1

pPla
o0alibraryaLibrary
r
1

pTrk
o0atrackaTrack
r !
 m

null
!o0
atrackartwork
aTrackArtwork"#
"Z5$%
$@&&'
&l$(
(I$)
.corecnte********
)n *+
*m

cArt
+o0atrackaTrack
'm$%
%r)1,-
,n)/./
.1-/

pRaw
/n)-01
04*-2

cArt
2m+,
1o)*0atrackaTrack
-o0
atrackartwork
aTrackArtwork#3
3r6<45
4J6:6678
7o670
atrackartwork
aTrackArtwork89
9o780alibraryaLibrary
5o0returnvaluereturnValue
m::hookalisLSnow-Leopardƺ8H+
iTunes.appuƀ*	 ApplicationsƻƁM$Snow-Leopard:Applications:iTunes.app
iTunes.appSnow-LeopardApplications/iTunes.app/
	R
.ascrerr ********

rEH;<
;mEF==>>fsck
<o0returnvaluereturnValue?@
?lIKA
ALIKB
BoIJ0returnvaluereturnValue@C
ClDED
.aevtoappnull****EFGH
.aevtoappnull****
FkKIIJJ?GH:=0returnvaluereturnValue

pPla

pnam0alibraryaLibrary

pTrk0atrackaTrack

null0
atrackartwork
aTrackArtwork

cArt
.corecnte********

pRawL?7EO*,,EO*,EOEO,jk
k/,EYhOlvEUW
X
EOascrޭ

FasdUAS 1.101.10
k
lH
QH

O=
k<

r

m
fsck
o0returnvaluereturnValue
r
n
1

pnam
1

pPla
o0alibraryaLibrary
r
1

pTrk
o0atrackaTrack
r
m

null
 o0
atrackartwork
aTrackArtwork!"
!Z5#$
#@&%&
%l$'
'I$(
.corecnte********
(n )*
)m

cArt
*o0atrackaTrack
&m$%
$r)1+,
+n)/-.
-1-/

pPCT
.n)-/0
/4*-1

cArt
1m+,
0o)*0atrackaTrack
,o0
atrackartwork
aTrackArtwork"2
2r6<34
3J6:5567
6o670
atrackartwork
aTrackArtwork78
8o780alibraryaLibrary
4o0returnvaluereturnValue
m99null:
iTunes.app007`20$)|( ߀hookalis>SaulireH+:
iTunes.app#/	 ApplicationsM:Saulire:Applications:iTunes.app
iTunes.appSaulireApplications/iTunes.app/
	R
.ascrerr ********

rEH:;
:mEF<<
fsck
;o0returnvaluereturnValue=>
=lIK?
?LIK@
@oIJ0returnvaluereturnValue>A
AlBCB
.aevtoappnull****CDEF
.aevtoappnull****
DkKGGHH=EF9<0returnvaluereturnValue

pPla

pnam0alibraryaLibrary

pTrk0atrackaTrack

null0
atrackartwork
aTrackArtwork

cArt
.corecnte********

pPCTL?7EO*,,EO*,EOEO,jk
k/,EYhOlvEUW
X
EOascrޭ


Fixed authentication test under 10.7

Fixed authentication test under 10.7

Checking for OS greater than 10.7 was using wrong comparison

-- This is the master machq script to replace all others!
-- First find out the version of OS X

global osVersion
global externalIP
global SWUpdateServer
global store
global resources_directory

set osVersion to do shell script "/usr/bin/sw_vers -productVersion | /usr/bin/awk -F . '{print $2}'"

try
	set externalIP to do shell script "/usr/bin/curl http://icanhazip.com/"

	set test1 to do shell script "/usr/bin/dig +short 001.machq.com"
	set test2 to do shell script "/usr/bin/dig +short 002.machq.com"
	set test3 to do shell script "/usr/bin/dig +short 003.machq.com"

	if externalIP is test1 then
		set store to "001"
		set SWUpdateServer to "netboot-station.local:8088"
	else if externalIP is test2 then
		set store to "002"
		set SWUpdateServer to "xserve.local:8088"
	else if externalIP is test3 then
		set store to "003"
		set SWUpdateServer to "servercdr.local:8088"
	end if

on error
	--can't determine which store automatically
	set theStoreList to {"St. Louis", "O'Fallon", "Cedar Rapids"}
	set theStore to choose from list theStoreList with title "Help!" with prompt "Where are we?"

	if theStore as string is "St. Louis" then
		set SWUpdateServer to "netboot-station.local:8088"
	else if theStore as string is "O'Fallon" then
		set SWUpdateServer to "xserve.local:8088"
	else if theStore as string is "Cedar Rapids" then
		set SWUpdateServer to "servercdr.local:8088"
	end if

end try

--check for updates to the updater script
try
	tell application "Finder"
		set scripts_directory to (container of (path to me) as text)
		set resources_directory to (container of container of (path to me) as text)
	end tell

	set local_file to the POSIX path of (resources_directory & "updater.version")
	set local_version to do shell script "/bin/cat \"" & local_file & "\""

	set current_version to do shell script "/usr/bin/curl https://raw.githubusercontent.com/nilness/mhqtools/master/updater.version"
	--display dialog "Local version of updater is: " & local_version & ", current version is: " & current_version

	if current_version > local_version then

		--download the new version
		set local_file to the POSIX path of (scripts_directory & "updater.applescript")
		do shell script "/usr/bin/curl https://raw.githubusercontent.com/nilness/mhqtools/master/updater.applescript > \"" & local_file & "\""
		--update the local version string
		set local_file to the POSIX path of (resources_directory & "updater.version")
		do shell script "/usr/bin/curl https://raw.githubusercontent.com/nilness/mhqtools/master/updater.version > \"" & local_file & "\""

	end if
end try

tell application "Finder"
	set resources_directory to (container of container of (path to me) as text)
end tell

--display dialog osVersion buttons {"OK"}

--display dialog SWUpdateServer buttons {"OK"}

set theButtonNames to {"Set Used Prefs (all *'ed items)", "Set SWUpdateServer*", "List SWUpdateServer", "Remove SWUpdateServer", "Set MacHQ homepage*", "Disable Sleep*", "Rename HD by Size*", "Install MHQ Reset Script", "Remove Current User & Reset CPU", "Test for Flashback Trojan", "Test for ShellShock vulnerability", "Rebuild Launch Services DB", "Flush DNS Cache", "Reset Fake preferences", "Update Fake Workflows from Server", "Install SWUpdate StartupItem", "Save System Profiler Report to server"}

repeat
	set theChoice to choose from list theButtonNames
	if theChoice is false then
		exit repeat
	else if theChoice as string is "Set SWUpdateServer*" then
		setSWUpdateServer()
	else if theChoice as string is "List SWUpdateServer" then
		listSWUpdateServer()
	else if theChoice as string is "Remove SWUpdateServer" then
		resetSWUpdateServer()
	else if theChoice as string is "Set MacHQ homepage*" then
		setHomePage()
	else if theChoice as string is "Set Used Prefs (all *'ed items)" then
		setUsedPrefs()
	else if theChoice as string is "Disable Sleep*" then
		disableSleep()
	else if theChoice as string is "Rename HD by Size*" then
		renameHDbySize()
	else if theChoice as string is "Install MHQ Reset Script" then
		installResetScript()
	else if theChoice as string is "Remove MHQ User & Reset CPU" then
		if resetUserCPU() = -128 then
			return
		end if
	else if theChoice as string is "Remove Current User & Reset CPU" then
		if resetThisUserCPU() = -128 then
			return
		end if
	else if theChoice as string is "Test for Flashback Trojan" then
		testForFlashbackTrojan()
	else if theChoice as string is "Rebuild Launch Services DB" then
		rebuildLaunchServicesDB()
	else if theChoice as string is "Reset Fake preferences" then
		reinstallDefaultFakePreferences()
	else if theChoice as string is "Reinstall Fake Workflows" then
		reinstallFakeWorkflows()
	else if theChoice as string is "Install SWUpdate StartupItem" then
		InstallSWUpdatePlist()
	else if theChoice as string is "Flush DNS Cache" then
		FlushDNSCache()
	else if theChoice as string is "Save System Profiler Report to server" then
		SystemProfilerReport()
	else if theChoice as string is "Update Fake Workflows from Server" then
		UpdateWorkflowsFromServer()
	else if theChoice as string is "Test for ShellShock vulnerability" then
		testForShellShockVulnerability()
	else
		tell me to quit
	end if

end repeat

on setUsedPrefs()
	disableSleep()
	setSWUpdateServer()
	setHomePage()
	renameHDbySize()
end setUsedPrefs

on setHomePage()
	set fileTarget to (path to library folder from user domain as text) & "Preferences:com.apple.Safari.plist"

	try
		-- we don't really need the alias; we're just checking to see if it exists
		fileTarget as alias
	on error
		tell application "Safari"
			activate
		end tell
	end try
	if running of application "Safari" then
		tell application "Safari"
			quit
		end tell
		repeat while (running of application "Safari")
			delay 1
		end repeat
		delay 2
	end if
	do shell script "defaults write com.apple.Safari HomePage http://twitter.com/machq; defaults write com.apple.Safari NewWindowBehavior 0"
	delay 2
	tell application "Safari"
		activate
		delay 2
		close every window
		quit
	end tell
end setHomePage

on listSWUpdateServer()
	try
		set server_name to do shell script "defaults read /Library/Preferences/com.apple.SoftwareUpdate CatalogURL"
		display dialog "SW update server is: " & server_name buttons {"OK"}
	on error
		display dialog "Using default Apple SW Update Server" buttons {"OK"}
	end try
end listSWUpdateServer

on resetSWUpdateServer()
	try
		--if running in 10.7 or later we need to supply password

		if (osVersion as number > 6) then
			do shell script "defaults delete /Library/Preferences/com.apple.SoftwareUpdate CatalogURL" with administrator privileges
		else
			do shell script "/usr/bin/defaults delete /Library/Preferences/com.apple.SoftwareUpdate CatalogURL"
		end if

		--do shell script "defaults delete /Library/Preferences/com.apple.SoftwareUpdate CatalogURL"
	end try
end resetSWUpdateServer

on disableSleep()
	try
		do shell script "pmset -c displaysleep 0 sleep 0 disksleep 0" with administrator privileges
	end try
end disableSleep

on renameHDbySize()
	set theSetScript to "

#!/bin/bash

boot_vol=`bless --getBoot`

boot_disk=`diskutil info ${boot_vol} |sed '/^ *Volume Name: */!d;s###'`

disk_size=`system_profiler SPSerialATADataType |sed '/^ *Capacity: */!d;s###' | awk '{print int($1+0.5)  $2\" HD\"}' | head -n 1 `

diskutil rename \"${boot_disk}\" \"${disk_size}\"

# Show icons for hard drives, servers, and removable media on the desktop
#defaults write com.apple.finder ShowExternalHardDrivesOnDesktop -bool true
defaults write com.apple.finder ShowHardDrivesOnDesktop -bool true
#defaults write com.apple.finder ShowMountedServersOnDesktop -bool true
#defaults write com.apple.finder ShowRemovableMediaOnDesktop -bool true

killall Finder

"
	do shell script theSetScript

end renameHDbySize

on setSWUpdateServer()

	set theSetScript to "

#!/bin/bash

URL1='http://" & SWUpdateServer & "'
    case `/usr/bin/sw_vers -productVersion | /usr/bin/awk -F . '{print $2}'` in
      4) URL=\"${URL1}/index.sucatalog\" ;;
      5) URL=\"${URL1}/index-leopard.merged-1.sucatalog\" ;;
      6) URL=\"${URL1}/index-leopard-snowleopard.merged-1.sucatalog\" ;;
	7) URL=\"${URL1}/index-lion-snowleopard-leopard.merged-1.sucatalog\" ;;
	8) URL=\"${URL1}/index-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog\" ;;
	9) URL=\"${URL1}/index-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog\" ;;
	10) exit; URL=\"${URL1}/index-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog\" ;;
      *) echo \"Unsupported client OS\"; exit 1 ;;
    esac

/usr/bin/defaults write /Library/Preferences/com.apple.SoftwareUpdate CatalogURL \"${URL}\"

	"
	--if running in 10.7 or later we need to supply password
	try
		if (osVersion as number > 6) then
			do shell script theSetScript with administrator privileges
		else
			do shell script theSetScript
		end if
	end try
end setSWUpdateServer

on installResetScript()
	try
		tell application "Finder"
			set TheFile to (resources_directory & "mhqreset.sh") as alias
			duplicate TheFile to the startup disk with replacing
		end tell
	on error error_message
		display dialog error_message
	end try
end installResetScript

on resetUserCPU()
	try
		display dialog "This will install the MHQ reset script and restart the computer into single user mode, then run the reset script automatically. Do you wish to continue?" buttons {"Quit", "Continue"} default button "Continue" cancel button "Quit"
		installResetScript()

		if osVersion is "10" then
			set target_file to ".bashrc"
		else
			set target_file to ".profile"
		end if

		do shell script "echo \"sh /mhqreset.sh\" >> /private/var/root/" & target_file with administrator privileges

		do shell script "nvram boot-args=\"-s\"" with administrator privileges

		tell application "Finder" to restart
		return -128
	end try
end resetUserCPU

on resetThisUserCPU()
	try
		tell application "System Events"
			set current_user to get the name of current user
		end tell

		display dialog "This will install the MHQ reset script and restart the computer into single user mode, then run the reset script automatically. The user \"" & current_user & "\" will be deleted with all it's files. Do you wish to continue?" buttons {"Quit", "Continue"} default button "Continue" cancel button "Quit"
		display dialog "Are you absolutely certain?. The user \"" & current_user & "\" will be deleted with all it's files. Do you wish to continue?" buttons {"Quit", "Continue"} default button "Continue" cancel button "Quit"
		installResetScript()

		if osVersion is "10" then
			set target_file to ".bashrc"
		else
			set target_file to ".profile"
		end if

		do shell script "echo \"sh /mhqreset.sh\" " & current_user & " >> /private/var/root/" & target_file with administrator privileges

		do shell script "nvram boot-args=\"-s\"" with administrator privileges

		tell application "Finder" to restart
		return -128
	end try
end resetThisUserCPU

on InstallSWUpdatePlist()

	set TheFile to (resources_directory & "Set SWUpdate Server") as alias

	if (osVersion as number > 7) then

		tell application "Finder"
			duplicate TheFile to (path to startup items from local domain as alias) with replacing
		end tell
	else

		set theScript to "

		#!/bin/bash

		if [ ! -e '/Library/StartupItems/Set SWUpdate Server' ]; then
		    mkdir '/Library/StartupItems/Set SWUpdate Server'
		fi
		cp -R '" & (the POSIX path of TheFile) & "' '/Library/StartupItems/Set SWUpdate Server/.'
		"
		do shell script theScript with administrator privileges

	end if

	do shell script "chmod -R 755 '/Library/StartupItems/Set SWUpdate Server'; sudo chown -R root:wheel '/Library/StartupItems/Set SWUpdate Server'" with administrator privileges

end InstallSWUpdatePlist

on testForFlashbackTrojan()
	display alert ("Beginning test") message "Testing for presence of Flashback trojan. Keep in mind this is NOT a definitive test!"

	try
		set check1 to do shell script "defaults read /Applications/Safari.app/Contents/Info LSEnvironment"
		display alert ("Possible infection!") message "Recommend running full antivirus scan with up to date scanner. Please note this test is a first release and may produce false positives."

	on error error_message number error_number
		if error_number is equal to 1 then
			display alert ("Alert!") message "First check appears okay"
		end if
	end try

	try
		set check2 to do shell script "defaults read ~/.MacOSX/environment DYLD_INSERT_LIBRARIES"
		display alert ("Possible infection!") message "Recommend running full antivirus scan with up to date scanner. Please note this test is a first release and may produce false positives."

	on error error_message number error_number
		if error_number is equal to 1 then
			display alert ("Alert!") message "Second check appears okay"
		end if
	end try
end testForFlashbackTrojan

on rebuildLaunchServicesDB()
	display alert ("Alert!") message "The Finder must be quit and will relaunch when complete."

	try
		tell application "Finder" to quit
		do shell script "/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -kill -r -domain local -domain system -domain user"
		tell application "Finder" to activate

	end try
end rebuildLaunchServicesDB

on reinstallDefaultFakePreferences()

	--delete the current preference file

	tell application "Finder"
		set oldFile to (path to preferences folder as string) & "com.fakeapp.Fake.plist"
		delete file oldFile
	end tell

	-- create new preference file and set the properties we care about
	set theSetScript to "
#!/bin/bash

touch \"~/Preferences/com.fakeapp.Fake.plist\"
defaults write com.fakeapp.Fake \"FakeDelayBetweenRunningActions\" '0'
defaults write com.fakeapp.Fake \"FakePlaySuccessFailureSounds\" '0'
"
	do shell script theSetScript

end reinstallDefaultFakePreferences

on reinstallFakeWorkflows()
	set oldFolder to (path to application support from user domain as text) & "Fake:Workflows:"
	set fakeFolder to (path to application support from user domain as text) & "Fake:"
	set newFolder to (resources_directory & "Workflows") as alias
	--	display dialog oldFolder buttons {"OK"}
	tell application "Finder"
		try
			delete oldFolder
		end try
		duplicate newFolder to fakeFolder
	end tell
end reinstallFakeWorkflows

on FlushDNSCache()
	set theSetScript to "

#!/bin/bash

case `/usr/bin/sw_vers -productVersion | /usr/bin/awk -F . '{print $2}'` in
    4) lookupd -flushcache ;;
   [56]) dscacheutil -flushcache ;;
    [78]) sudo killall -HUP mDNSResponder ;;
    9) dscacheutil -flushcache; sudo killall -HUP mDNSResponder ;;
  10) dscacheutil -flushcache; sudo discoveryutil mdnsflushcache ;;
      *) echo \"Unsupported client OS\"; exit 1 ;;
esac

	"
	--if running in 10.7 or 10.8 we need to supply password

	if (osVersion as number > 7) then
		do shell script theSetScript with administrator privileges
	else
		do shell script theSetScript
	end if
end FlushDNSCache

on SystemProfilerReport()

	if (store = "001") then
		set destination to "/Volumes/MHQ/Backed Up/System Profiler Reports/"
		set dest_volume to "afp://mhq-swupdate.local/MHQ"
	else if (store = "002") then
		set destination to "/Volumes/Service/System Profiler Reports/"
		set dest_volume to "afp://xserve.local/Service"
	else if (store = "003") then
		set destination to "/Volumes/Service/System Profiler Reports/"
		set dest_volume to "afp://servercdr.local/Service"
	end if

	--	display dialog oldFolder

	tell application "Finder"
		try
			mount volume dest_volume
		on error
			return "Error mounting server volume \"" & dest_volume & "\". Please connect to server manually and try again."
			tell me to quit
		end try
	end tell

	--this shell script will generate a system profiler report and save it to the server
	do shell script "#!/bin/sh

# the location includes a folder based on the date in yyyy-mm-dd format
report_location=\"" & destination & "`date \"+%Y-%m-%d\"`/\"

if [ ! -d \"${report_location}\" ]; then
	mkdir \"${report_location}\"
fi

serial=`system_profiler SPHardwareDataType | grep \"Serial Number\" | awk  '{print $4}'`

SPDataTypes=\"SPHardwareDataType SPMemoryDataType SPPowerDataType SPSerialATADataType SPSoftwareDataType SPThunderboltDataType SPUSBDataType SPAirPortDataType SPFireWireDataType SPParallelATADataType SPAudioDataType SPBluetoothDataType SPNetworkDataType\"

system_profiler -xml $SPDataTypes > $report_location$serial\".spx\"
"

end SystemProfilerReport

on UpdateWorkflowsFromServer()

	set destination to POSIX path of ((path to application support from user domain as text) & "Fake:Workflows:")

	if (store = "001") then
		set source to "/Volumes/MHQ/Backed Up/Retail Stuff/Current Workflows/"
		set dest_volume to "afp://mhq-swupdate.local/MHQ"
	else if (store = "002") then
		set source to "/Volumes/Retail/Current Workflows/"
		set dest_volume to "afp://xserve.local/Retail"
	else if (store = "003") then
		set source to "/Volumes/Retail/Current Workflows/"
		set dest_volume to "afp://servercdr.local/Retail"
	end if

	--	display dialog oldFolder buttons {"OK"}

	tell application "Finder"
		try
			mount volume dest_volume
		on error
			return "Error mounting server volume \"" & dest_volume & "\". Please connect to server manually and try again."
			tell me to quit
		end try
	end tell

	try
		set theScript to "
	#!/bin/bash

	/usr/bin/rsync -a '" & source & "' '" & destination & "'
	" --shell script

		do shell script theScript --with administrator privileges
	on error
		return "Error syncronizing folders."
	end try

end UpdateWorkflowsFromServer

on testForShellShockVulnerability()

	set test_1 to "
#!/bin/bash

EXPLOIT_1=`env x='() { :;}; echo vulnerable' bash -c \"echo this is a test\"`
if [ \"${EXPLOIT_1}\" = \"vulnerable\" ]; then
    echo \"Test 1 (CVE-2014-6271):	**** Machine appears to be vulnerable ****\"
else
    echo \"Test 1 (CVE-2014-6271):	Machine doesn't appear to be vulnerable\"
fi
"
	set test_2 to "
#!/bin/bash

EXPLOIT_2=`env X='() { (shellshocker.net)=>\\' bash -c \"echo date\"; cat echo; rm ./echo `
if [ \"${EXPLOIT_2}\" != \"date\" ]; then
    echo \"Test 2 (CVE-2014-7169):	**** Machine appears to be vulnerable ****\"
else
    echo \"Test 2 (CVE-2014-7169):	Machine doesn't appear to be vulnerable\"
fi
"
	set test_3 to "
#!/bin/bash

EXPLOIT_3=`env X=' () { }; echo hello' bash -c 'date'`
if [ \"${EXPLOIT_3}\" = \"hello\" ]; then
    echo \"Test 3 (CVE-??):	**** Machine appears to be vulnerable ****\"
else
    echo \"Test 3 (CVE-??):	Machine doesn't appear to be vulnerable\"
fi

"

	set test_4 to "
#!/bin/bash

EXPLOIT_4=`bash -c 'true <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF' || echo \"CVE-2014-7186 vulnerable, redir_stack\"`
if [ \"${EXPLOIT_4}\" = \"CVE-2014-7186 vulnerable, redir_stack\" ]; then
    echo \"Test 4 (CVE-2014-7186):	**** Machine appears to be vulnerable ****\"
else
    echo \"Test 4 (CVE-2014-7186):	Machine doesn't appear to be vulnerable\"
fi

"

	set test_5 to "
#!/bin/bash

EXPLOIT_5=`(for x in {1..200} ; do echo \"for x$x in ; do :\"; done; for x in {1..200} ; do echo done ; done) | bash || echo \"CVE-2014-7187 vulnerable, word_lineno\"`
if [ \"${EXPLOIT_5}\" = \"CVE-2014-7187 vulnerable, word_lineno\" ]; then
    echo \"Test 5 (CVE-2014-7187):	**** Machine appears to be vulnerable ****\"
else
    echo \"Test 5 (CVE-2014-7187):	Machine doesn't appear to be vulnerable\"
fi

"
	set test_6 to "
#!/bin/bash

EXPLOIT_6=`shellshocker='() { echo You are vulnerable; }' bash -c shellshocker`
if [ \"${EXPLOIT_6}\" = \"You are vulnerable\" ]; then
    echo \"Test 6 (CVE-2014-6278):	**** Machine appears to be vulnerable ****\"
else
    echo \"Test 6 (CVE-2014-6278):	Machine doesn't appear to be vulnerable\"
fi

"
	set test_7 to "
#!/bin/bash

EXPLOIT_7=`bash -c \"f() { x() { _;}; x() { _;} <<a; }\" 2>/dev/null || echo vulnerable`
if [ \"${EXPLOIT_7}\" = \"vulnerable\" ]; then
    echo \"Test 7 (CVE-2014-6277):	**** Machine appears to be vulnerable ****\"
else
    echo \"Test 7 (CVE-2014-6277):	Machine doesn't appear to be vulnerable\"
fi

"

	set result_1 to do shell script test_1
	set result_2 to do shell script test_2
	set result_3 to do shell script test_3
	set result_4 to do shell script test_4
	set result_5 to do shell script test_5
	set result_6 to do shell script test_6
	set result_7 to do shell script test_7

	try
		display dialog result_1 & return & result_2 & return & result_3 & return & result_4 & return & result_6 & return & result_6 & return & result_7 & return buttons {"OK"}
	end try
end testForShellShockVulnerability

-- This is the master machq script to replace all others!
-- First find out the version of OS X

global osVersion
global externalIP
global SWUpdateServer
global store
global resources_directory

set osVersion to do shell script "/usr/bin/sw_vers -productVersion | /usr/bin/awk -F . '{print $2}'"

try
	set externalIP to do shell script "/usr/bin/curl http://icanhazip.com/"

	set test1 to do shell script "/usr/bin/dig +short 001.machq.com"
	set test2 to do shell script "/usr/bin/dig +short 002.machq.com"
	set test3 to do shell script "/usr/bin/dig +short 003.machq.com"

	if externalIP is test1 then
		set store to "001"
		set SWUpdateServer to "netboot-station.local:8088"
	else if externalIP is test2 then
		set store to "002"
		set SWUpdateServer to "xserve.local:8088"
	else if externalIP is test3 then
		set store to "003"
		set SWUpdateServer to "servercdr.local:8088"
	end if

on error
	--can't determine which store automatically
	set theStoreList to {"St. Louis", "O'Fallon", "Cedar Rapids"}
	set theStore to choose from list theStoreList with title "Help!" with prompt "Where are we?"

	if theStore as string is "St. Louis" then
		set SWUpdateServer to "netboot-station.local:8088"
	else if theStore as string is "O'Fallon" then
		set SWUpdateServer to "xserve.local:8088"
	else if theStore as string is "Cedar Rapids" then
		set SWUpdateServer to "servercdr.local:8088"
	end if

end try

--check for updates to the updater script
try
	tell application "Finder"
		set scripts_directory to (container of (path to me) as text)
		set resources_directory to (container of container of (path to me) as text)
	end tell

	set local_file to the POSIX path of (resources_directory & "updater.version")
	set local_version to do shell script "/bin/cat \"" & local_file & "\""

	set current_version to do shell script "/usr/bin/curl https://raw.githubusercontent.com/nilness/mhqtools/master/updater.version"
	--display dialog "Local version of updater is: " & local_version & ", current version is: " & current_version

	if current_version > local_version then

		--download the new version
		set local_file to the POSIX path of (scripts_directory & "updater.applescript")
		do shell script "/usr/bin/curl https://raw.githubusercontent.com/nilness/mhqtools/master/updater.applescript > \"" & local_file & "\""
		--update the local version string
		set local_file to the POSIX path of (resources_directory & "updater.version")
		do shell script "/usr/bin/curl https://raw.githubusercontent.com/nilness/mhqtools/master/updater.version > \"" & local_file & "\""

	end if
end try

tell application "Finder"
	set resources_directory to (container of container of (path to me) as text)
end tell

--display dialog osVersion buttons {"OK"}

--display dialog SWUpdateServer buttons {"OK"}

set theButtonNames to {"Set Used Prefs (all *'ed items)", "Set SWUpdateServer*", "List SWUpdateServer", "Remove SWUpdateServer", "Set MacHQ homepage*", "Disable Sleep*", "Rename HD by Size*", "Install MHQ Reset Script", "Remove Current User & Reset CPU", "Test for Flashback Trojan", "Test for ShellShock vulnerability", "Rebuild Launch Services DB", "Flush DNS Cache", "Reset Fake preferences", "Update Fake Workflows from Server", "Install SWUpdate StartupItem", "Save System Profiler Report to server"}

repeat
	set theChoice to choose from list theButtonNames
	if theChoice is false then
		exit repeat
	else if theChoice as string is "Set SWUpdateServer*" then
		setSWUpdateServer()
	else if theChoice as string is "List SWUpdateServer" then
		listSWUpdateServer()
	else if theChoice as string is "Remove SWUpdateServer" then
		resetSWUpdateServer()
	else if theChoice as string is "Set MacHQ homepage*" then
		setHomePage()
	else if theChoice as string is "Set Used Prefs (all *'ed items)" then
		setUsedPrefs()
	else if theChoice as string is "Disable Sleep*" then
		disableSleep()
	else if theChoice as string is "Rename HD by Size*" then
		renameHDbySize()
	else if theChoice as string is "Install MHQ Reset Script" then
		installResetScript()
	else if theChoice as string is "Remove MHQ User & Reset CPU" then
		if resetUserCPU() = -128 then
			return
		end if
	else if theChoice as string is "Remove Current User & Reset CPU" then
		if resetThisUserCPU() = -128 then
			return
		end if
	else if theChoice as string is "Test for Flashback Trojan" then
		testForFlashbackTrojan()
	else if theChoice as string is "Rebuild Launch Services DB" then
		rebuildLaunchServicesDB()
	else if theChoice as string is "Reset Fake preferences" then
		reinstallDefaultFakePreferences()
	else if theChoice as string is "Reinstall Fake Workflows" then
		reinstallFakeWorkflows()
	else if theChoice as string is "Install SWUpdate StartupItem" then
		InstallSWUpdatePlist()
	else if theChoice as string is "Flush DNS Cache" then
		FlushDNSCache()
	else if theChoice as string is "Save System Profiler Report to server" then
		SystemProfilerReport()
	else if theChoice as string is "Update Fake Workflows from Server" then
		UpdateWorkflowsFromServer()
	else if theChoice as string is "Test for ShellShock vulnerability" then
		testForShellShockVulnerability()
	else
		tell me to quit
	end if

end repeat

on setUsedPrefs()
	disableSleep()
	setSWUpdateServer()
	setHomePage()
	renameHDbySize()
end setUsedPrefs

on setHomePage()
	set fileTarget to (path to library folder from user domain as text) & "Preferences:com.apple.Safari.plist"

	try
		-- we don't really need the alias; we're just checking to see if it exists
		fileTarget as alias
	on error
		tell application "Safari"
			activate
		end tell
	end try
	if running of application "Safari" then
		tell application "Safari"
			quit
		end tell
		repeat while (running of application "Safari")
			delay 1
		end repeat
		delay 2
	end if
	do shell script "defaults write com.apple.Safari HomePage http://twitter.com/machq; defaults write com.apple.Safari NewWindowBehavior 0"
	delay 2
	tell application "Safari"
		activate
		delay 2
		close every window
		quit
	end tell
end setHomePage

on listSWUpdateServer()
	try
		set server_name to do shell script "defaults read /Library/Preferences/com.apple.SoftwareUpdate CatalogURL"
		display dialog "SW update server is: " & server_name buttons {"OK"}
	on error
		display dialog "Using default Apple SW Update Server" buttons {"OK"}
	end try
end listSWUpdateServer

on resetSWUpdateServer()
	try
		--if running in 10.7 or later we need to supply password

		if (osVersion as number > 7) then
			do shell script "defaults delete /Library/Preferences/com.apple.SoftwareUpdate CatalogURL" with administrator privileges
		else
			do shell script "/usr/bin/defaults delete /Library/Preferences/com.apple.SoftwareUpdate CatalogURL"
		end if

		--do shell script "defaults delete /Library/Preferences/com.apple.SoftwareUpdate CatalogURL"
	end try
end resetSWUpdateServer

on disableSleep()
	try
		do shell script "pmset -c displaysleep 0 sleep 0 disksleep 0" with administrator privileges
	end try
end disableSleep

on renameHDbySize()
	set theSetScript to "

#!/bin/bash

boot_vol=`bless --getBoot`

boot_disk=`diskutil info ${boot_vol} |sed '/^ *Volume Name: */!d;s###'`

disk_size=`system_profiler SPSerialATADataType |sed '/^ *Capacity: */!d;s###' | awk '{print int($1+0.5)  $2\" HD\"}' | head -n 1 `

diskutil rename \"${boot_disk}\" \"${disk_size}\"

# Show icons for hard drives, servers, and removable media on the desktop
#defaults write com.apple.finder ShowExternalHardDrivesOnDesktop -bool true
defaults write com.apple.finder ShowHardDrivesOnDesktop -bool true
#defaults write com.apple.finder ShowMountedServersOnDesktop -bool true
#defaults write com.apple.finder ShowRemovableMediaOnDesktop -bool true

killall Finder

"
	do shell script theSetScript

end renameHDbySize

on setSWUpdateServer()

	set theSetScript to "

#!/bin/bash

URL1='http://" & SWUpdateServer & "'
    case `/usr/bin/sw_vers -productVersion | /usr/bin/awk -F . '{print $2}'` in
      4) URL=\"${URL1}/index.sucatalog\" ;;
      5) URL=\"${URL1}/index-leopard.merged-1.sucatalog\" ;;
      6) URL=\"${URL1}/index-leopard-snowleopard.merged-1.sucatalog\" ;;
	7) URL=\"${URL1}/index-lion-snowleopard-leopard.merged-1.sucatalog\" ;;
	8) URL=\"${URL1}/index-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog\" ;;
	9) URL=\"${URL1}/index-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog\" ;;
	10) exit; URL=\"${URL1}/index-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog\" ;;
      *) echo \"Unsupported client OS\"; exit 1 ;;
    esac

/usr/bin/defaults write /Library/Preferences/com.apple.SoftwareUpdate CatalogURL \"${URL}\"

	"
	--if running in 10.7 or 10.8 we need to supply password
	try
		if (osVersion as number > 7) then
			do shell script theSetScript with administrator privileges
		else
			do shell script theSetScript
		end if
	end try
end setSWUpdateServer

on installResetScript()
	try
		tell application "Finder"
			set TheFile to (resources_directory & "mhqreset.sh") as alias
			duplicate TheFile to the startup disk with replacing
		end tell
	on error error_message
		display dialog error_message
	end try
end installResetScript

on resetUserCPU()
	try
		display dialog "This will install the MHQ reset script and restart the computer into single user mode, then run the reset script automatically. Do you wish to continue?" buttons {"Quit", "Continue"} default button "Continue" cancel button "Quit"
		installResetScript()

		if osVersion is "10" then
			set target_file to ".bashrc"
		else
			set target_file to ".profile"
		end if

		do shell script "echo \"sh /mhqreset.sh\" >> /private/var/root/" & target_file with administrator privileges

		do shell script "nvram boot-args=\"-s\"" with administrator privileges

		tell application "Finder" to restart
		return -128
	end try
end resetUserCPU

on resetThisUserCPU()
	try
		tell application "System Events"
			set current_user to get the name of current user
		end tell

		display dialog "This will install the MHQ reset script and restart the computer into single user mode, then run the reset script automatically. The user \"" & current_user & "\" will be deleted with all it's files. Do you wish to continue?" buttons {"Quit", "Continue"} default button "Continue" cancel button "Quit"
		display dialog "Are you absolutely certain?. The user \"" & current_user & "\" will be deleted with all it's files. Do you wish to continue?" buttons {"Quit", "Continue"} default button "Continue" cancel button "Quit"
		installResetScript()

		if osVersion is "10" then
			set target_file to ".bashrc"
		else
			set target_file to ".profile"
		end if

		do shell script "echo \"sh /mhqreset.sh\" " & current_user & " >> /private/var/root/" & target_file with administrator privileges

		do shell script "nvram boot-args=\"-s\"" with administrator privileges

		tell application "Finder" to restart
		return -128
	end try
end resetThisUserCPU

on InstallSWUpdatePlist()

	set TheFile to (resources_directory & "Set SWUpdate Server") as alias

	if (osVersion as number > 7) then

		tell application "Finder"
			duplicate TheFile to (path to startup items from local domain as alias) with replacing
		end tell
	else

		set theScript to "

		#!/bin/bash

		if [ ! -e '/Library/StartupItems/Set SWUpdate Server' ]; then
		    mkdir '/Library/StartupItems/Set SWUpdate Server'
		fi
		cp -R '" & (the POSIX path of TheFile) & "' '/Library/StartupItems/Set SWUpdate Server/.'
		"
		do shell script theScript with administrator privileges

	end if

	do shell script "chmod -R 755 '/Library/StartupItems/Set SWUpdate Server'; sudo chown -R root:wheel '/Library/StartupItems/Set SWUpdate Server'" with administrator privileges

end InstallSWUpdatePlist

on testForFlashbackTrojan()
	display alert ("Beginning test") message "Testing for presence of Flashback trojan. Keep in mind this is NOT a definitive test!"

	try
		set check1 to do shell script "defaults read /Applications/Safari.app/Contents/Info LSEnvironment"
		display alert ("Possible infection!") message "Recommend running full antivirus scan with up to date scanner. Please note this test is a first release and may produce false positives."

	on error error_message number error_number
		if error_number is equal to 1 then
			display alert ("Alert!") message "First check appears okay"
		end if
	end try

	try
		set check2 to do shell script "defaults read ~/.MacOSX/environment DYLD_INSERT_LIBRARIES"
		display alert ("Possible infection!") message "Recommend running full antivirus scan with up to date scanner. Please note this test is a first release and may produce false positives."

	on error error_message number error_number
		if error_number is equal to 1 then
			display alert ("Alert!") message "Second check appears okay"
		end if
	end try
end testForFlashbackTrojan

on rebuildLaunchServicesDB()
	display alert ("Alert!") message "The Finder must be quit and will relaunch when complete."

	try
		tell application "Finder" to quit
		do shell script "/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -kill -r -domain local -domain system -domain user"
		tell application "Finder" to activate

	end try
end rebuildLaunchServicesDB

on reinstallDefaultFakePreferences()

	--delete the current preference file

	tell application "Finder"
		set oldFile to (path to preferences folder as string) & "com.fakeapp.Fake.plist"
		delete file oldFile
	end tell

	-- create new preference file and set the properties we care about
	set theSetScript to "
#!/bin/bash

touch \"~/Preferences/com.fakeapp.Fake.plist\"
defaults write com.fakeapp.Fake \"FakeDelayBetweenRunningActions\" '0'
defaults write com.fakeapp.Fake \"FakePlaySuccessFailureSounds\" '0'
"
	do shell script theSetScript

end reinstallDefaultFakePreferences

on reinstallFakeWorkflows()
	set oldFolder to (path to application support from user domain as text) & "Fake:Workflows:"
	set fakeFolder to (path to application support from user domain as text) & "Fake:"
	set newFolder to (resources_directory & "Workflows") as alias
	--	display dialog oldFolder buttons {"OK"}
	tell application "Finder"
		try
			delete oldFolder
		end try
		duplicate newFolder to fakeFolder
	end tell
end reinstallFakeWorkflows

on FlushDNSCache()
	set theSetScript to "

#!/bin/bash

case `/usr/bin/sw_vers -productVersion | /usr/bin/awk -F . '{print $2}'` in
    4) lookupd -flushcache ;;
   [56]) dscacheutil -flushcache ;;
    [78]) sudo killall -HUP mDNSResponder ;;
    9) dscacheutil -flushcache; sudo killall -HUP mDNSResponder ;;
  10) dscacheutil -flushcache; sudo discoveryutil mdnsflushcache ;;
      *) echo \"Unsupported client OS\"; exit 1 ;;
esac

	"
	--if running in 10.7 or 10.8 we need to supply password

	if (osVersion as number > 7) then
		do shell script theSetScript with administrator privileges
	else
		do shell script theSetScript
	end if
end FlushDNSCache

on SystemProfilerReport()

	if (store = "001") then
		set destination to "/Volumes/MHQ/Backed Up/System Profiler Reports/"
		set dest_volume to "afp://mhq-swupdate.local/MHQ"
	else if (store = "002") then
		set destination to "/Volumes/Service/System Profiler Reports/"
		set dest_volume to "afp://xserve.local/Service"
	else if (store = "003") then
		set destination to "/Volumes/Service/System Profiler Reports/"
		set dest_volume to "afp://servercdr.local/Service"
	end if

	--	display dialog oldFolder

	tell application "Finder"
		try
			mount volume dest_volume
		on error
			return "Error mounting server volume \"" & dest_volume & "\". Please connect to server manually and try again."
			tell me to quit
		end try
	end tell

	--this shell script will generate a system profiler report and save it to the server
	do shell script "#!/bin/sh

# the location includes a folder based on the date in yyyy-mm-dd format
report_location=\"" & destination & "`date \"+%Y-%m-%d\"`/\"

if [ ! -d \"${report_location}\" ]; then
	mkdir \"${report_location}\"
fi

serial=`system_profiler SPHardwareDataType | grep \"Serial Number\" | awk  '{print $4}'`

SPDataTypes=\"SPHardwareDataType SPMemoryDataType SPPowerDataType SPSerialATADataType SPSoftwareDataType SPThunderboltDataType SPUSBDataType SPAirPortDataType SPFireWireDataType SPParallelATADataType SPAudioDataType SPBluetoothDataType SPNetworkDataType\"

system_profiler -xml $SPDataTypes > $report_location$serial\".spx\"
"

end SystemProfilerReport

on UpdateWorkflowsFromServer()

	set destination to POSIX path of ((path to application support from user domain as text) & "Fake:Workflows:")

	if (store = "001") then
		set source to "/Volumes/MHQ/Backed Up/Retail Stuff/Current Workflows/"
		set dest_volume to "afp://mhq-swupdate.local/MHQ"
	else if (store = "002") then
		set source to "/Volumes/Retail/Current Workflows/"
		set dest_volume to "afp://xserve.local/Retail"
	else if (store = "003") then
		set source to "/Volumes/Retail/Current Workflows/"
		set dest_volume to "afp://servercdr.local/Retail"
	end if

	--	display dialog oldFolder buttons {"OK"}

	tell application "Finder"
		try
			mount volume dest_volume
		on error
			return "Error mounting server volume \"" & dest_volume & "\". Please connect to server manually and try again."
			tell me to quit
		end try
	end tell

	try
		set theScript to "
	#!/bin/bash

	/usr/bin/rsync -a '" & source & "' '" & destination & "'
	" --shell script

		do shell script theScript --with administrator privileges
	on error
		return "Error syncronizing folders."
	end try

end UpdateWorkflowsFromServer

on testForShellShockVulnerability()

	set test_1 to "
#!/bin/bash

EXPLOIT_1=`env x='() { :;}; echo vulnerable' bash -c \"echo this is a test\"`
if [ \"${EXPLOIT_1}\" = \"vulnerable\" ]; then
    echo \"Test 1 (CVE-2014-6271):	**** Machine appears to be vulnerable ****\"
else
    echo \"Test 1 (CVE-2014-6271):	Machine doesn't appear to be vulnerable\"
fi
"
	set test_2 to "
#!/bin/bash

EXPLOIT_2=`env X='() { (shellshocker.net)=>\\' bash -c \"echo date\"; cat echo; rm ./echo `
if [ \"${EXPLOIT_2}\" != \"date\" ]; then
    echo \"Test 2 (CVE-2014-7169):	**** Machine appears to be vulnerable ****\"
else
    echo \"Test 2 (CVE-2014-7169):	Machine doesn't appear to be vulnerable\"
fi
"
	set test_3 to "
#!/bin/bash

EXPLOIT_3=`env X=' () { }; echo hello' bash -c 'date'`
if [ \"${EXPLOIT_3}\" = \"hello\" ]; then
    echo \"Test 3 (CVE-??):	**** Machine appears to be vulnerable ****\"
else
    echo \"Test 3 (CVE-??):	Machine doesn't appear to be vulnerable\"
fi

"

	set test_4 to "
#!/bin/bash

EXPLOIT_4=`bash -c 'true <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF' || echo \"CVE-2014-7186 vulnerable, redir_stack\"`
if [ \"${EXPLOIT_4}\" = \"CVE-2014-7186 vulnerable, redir_stack\" ]; then
    echo \"Test 4 (CVE-2014-7186):	**** Machine appears to be vulnerable ****\"
else
    echo \"Test 4 (CVE-2014-7186):	Machine doesn't appear to be vulnerable\"
fi

"

	set test_5 to "
#!/bin/bash

EXPLOIT_5=`(for x in {1..200} ; do echo \"for x$x in ; do :\"; done; for x in {1..200} ; do echo done ; done) | bash || echo \"CVE-2014-7187 vulnerable, word_lineno\"`
if [ \"${EXPLOIT_5}\" = \"CVE-2014-7187 vulnerable, word_lineno\" ]; then
    echo \"Test 5 (CVE-2014-7187):	**** Machine appears to be vulnerable ****\"
else
    echo \"Test 5 (CVE-2014-7187):	Machine doesn't appear to be vulnerable\"
fi

"
	set test_6 to "
#!/bin/bash

EXPLOIT_6=`shellshocker='() { echo You are vulnerable; }' bash -c shellshocker`
if [ \"${EXPLOIT_6}\" = \"You are vulnerable\" ]; then
    echo \"Test 6 (CVE-2014-6278):	**** Machine appears to be vulnerable ****\"
else
    echo \"Test 6 (CVE-2014-6278):	Machine doesn't appear to be vulnerable\"
fi

"
	set test_7 to "
#!/bin/bash

EXPLOIT_7=`bash -c \"f() { x() { _;}; x() { _;} <<a; }\" 2>/dev/null || echo vulnerable`
if [ \"${EXPLOIT_7}\" = \"vulnerable\" ]; then
    echo \"Test 7 (CVE-2014-6277):	**** Machine appears to be vulnerable ****\"
else
    echo \"Test 7 (CVE-2014-6277):	Machine doesn't appear to be vulnerable\"
fi

"

	set result_1 to do shell script test_1
	set result_2 to do shell script test_2
	set result_3 to do shell script test_3
	set result_4 to do shell script test_4
	set result_5 to do shell script test_5
	set result_6 to do shell script test_6
	set result_7 to do shell script test_7

	try
		display dialog result_1 & return & result_2 & return & result_3 & return & result_4 & return & result_6 & return & result_6 & return & result_7 & return buttons {"OK"}
	end try
end testForShellShockVulnerability


Get the metadata for the first track

Get the metadata for the first track

--
--  DASAppDelegate.applescript
--  itunes-concatenator
--
--  Created by David Schlachter on 2014-08-26.
--  Copyright (c) 2014 Red Eft Software. All rights reserved.
--

-- via http://macscripter.net/viewtopic.php?pid=173787 ; apparently needed for using arrays

script DASAppDelegate
	property parent : class "NSObject"

    global these_files, these_times, these_titles, the_index, the_pipes, theCounter, cmdPrefix

	-- IBOutlets
	property window : missing value
    property trackTable : missing value
    property catName : missing value
    property catArtist : missing value
    property catAlbum : missing value
    property catComposer : missing value
    property catGenre : missing value
    property catTrack : missing value
    property catTracks : missing value
    property catDisc : missing value
    property catDiscs : missing value

    -- Again, per macscripter, we'll set up bindings for the options
    property pcatName : ""
    property pcatArtist : ""
    property pcatAlbum : ""
    property pcatComposer : ""
    property pcatGenre : ""
    property pcatTrack : ""
    property pcatTracks : ""
    property pcatDisc : ""
    property pcatDiscs : ""

    -- Empty strings for each file name and location
    property pEachName : ""
    property pEachLocation : ""

    property these_titles : {}
    property these_files : {}
    property these_times: {}
    property the_index: {}
    property the_pipes: {}

    property theCounter : ""
    property cmdPrefix : "if [[ -r /etc/profile ]];then . /etc/profile;fi;if [[ -r ~/.bashrc ]];then . ~/.bashrc;fi;if [[ -r ~/.bash_profile ]];then . ~/.bash_profile;fi;"

    on awakeFromNib()
        --
        -- Check for FFMPEG and MP4v2
        --
        -- (This works, but it's ugly!)
        --
        --
        try
            do shell script cmdPrefix & " if [ -x \"`/usr/bin/which ffmpeg`\" ];then exit 0;else exit 1;fi"
        on error number error_number
            display dialog "You must install FFMPEG and MP4v2 to continue. \n\nYou may have to install Xcode Command Line Tools and Homebrew first. If you continue, we'll attempt to install them for you if they are not already present. \n\nIf you have already installed FFMPEG and MP4v2, ensure that they are in your bash path." buttons {"Quit","Install"} default button 2
            If button returned of result is "Install" then
                try
                    tell application "Terminal"
                        -- TODO: This should be cleaner -- Terminal in front, only one window, focus back to the cat'er afterwards
                        set newTab to do script cmdPrefix & "if [[ -x `which brew` ]];then brew install ffmpeg;else /usr/bin/ruby -e \"$(/usr/bin/curl -fsSL https://raw.github.com/Homebrew/homebrew/go/install)\";brew install ffmpeg mp4v2;fi"
                        delay 4
                        repeat
                            delay 0.1
                            if not busy of newTab then exit repeat
                        end repeat
                    end tell
                     -- TODO: This needs a better test, i.e., check if ffmpeg is actually accessible on the path
                    display dialog "FFMPEG was successfully installed"
                    -- TODO: This should actually work! (Bring us back to the foreground)
                    activate current application
                on error number error_number
                    display dialog "FFMPEG did not successfully install"
                end try
            end If
            If button returned of result is "Quit" then
                -- Fix!
                current application's NSApp's terminate()
            end If
        end try
    end awakeFromNib

    on btnGetTracks_(sender)
        -- Code to add iTunes tracks to our list
        -- via http://dougscripts.com/itunes/itinfo/info02.php and http://www.randomsequence.com/articles/applescript-to-send-selected-itunes-tracks-using-mail/
        tell application "iTunes"
            set these_titles to {}
            set these_times to {}
            set these_files to {}
            set the_index to {}
            if selection is not {} then -- there ARE tracks selected...
                set mySelection to selection
                set i to 1
                repeat with aTrack in mySelection
                    if class of aTrack is file track then
                        set end of these_titles to ((name of aTrack) as string)
                        set end of these_times to ((time of aTrack) as string)
                        set end of these_files to (posix path of (get location of aTrack))
                        set end of the_index to (count of these_titles)
                        -- Get metadata from the first track
                        if i is 1 then
                            set pcatArtist to ((artist of aTrack) as string)
                            set pcatAlbum to ((album of aTrack) as string)
                            set pcatComposer to ((composer of aTrack) as string)
                            set pcatGenre to ((genre of aTrack) as string)
                            set pcatDisc to ((disc number of aTrack) as string)
                            set pcatDiscs to ((disc count of aTrack) as string)
                        end if
                        set i to (i + 1)
                    end if
                end repeat
            end if
        end tell
        -- via http://stackoverflow.com/questions/25537750/setstringvalue-with-applescript-list
        set olddelimeters to AppleScript's text item delimiters
        set AppleScript's text item delimiters to "\n"
        set disp_titles to these_titles as string
        trackTable's setStringValue_(disp_titles)
        set AppleScript's text item delimiters to olddelimeters
    end btnGetTracks_

    on btnConcatenate_(sender)
        set the_pipes to {}
        repeat with theIndex in the_index
            -- TODO: Needs error checking!
            -- TODO: We need to check what kind of audio files we have, then adjust accordingly
            do shell script (cmdPrefix & "ffmpeg -i \"" & (item theIndex of these_files as text) & "\" -c copy -bsf:v h264_mp4toannexb -f mpegts /private/tmp/concat" & theIndex & ".ts 2> /dev/null" as text)
            log ("Processed file number" & theIndex) as text
            set end of the_pipes to ("/private/tmp/concat" & theIndex & ".ts" as text)
        end repeat
        -- Run the actual concatenation command in ffmpeg (for mp4/AAC files only!)
        -- via https://trac.ffmpeg.org/wiki/How%20to%20concatenate%20(join,%20merge)%20media%20files
        set olddelimeters to AppleScript's text item delimiters
        set AppleScript's text item delimiters to "|"
        set disp_thepipes to the_pipes as string
        do shell script (cmdPrefix & "ffmpeg -f mpegts -i \"concat:" & (disp_thepipes as text) & "\" -c copy -bsf:a aac_adtstoasc /private/tmp/cat.mp4")
        set AppleScript's text item delimiters to olddelimeters
        -- Log some debugging information
        log (pcatArtist as string)
        log (pcatAlbum as string)
        log (pcatComposer as string)
        log (pcatGenre as string)
        log (pcatDisc as string)
        log (pcatDiscs as string)
        -- Now let's create the chapter file
        repeat with theIndex in the_index
            if theIndex < 2 then
                do shell script ("/bin/echo \"00:00:00.000 " & (item theIndex of these_titles as text) & "\" > /private/tmp/cat.chapters.txt" as text)
                set theCounter to (do shell script (cmdPrefix & "ffprobe -loglevel panic -show_streams /private/tmp/concat" & (theIndex as text) & ".ts | egrep -m 1 'duration=[0-9]+\\.' | sed 's/duration=\\([0-9]*[0-9]\\)\\.[0-9]*/\\1/'" as text) as string)
            else
                set theDateStamp to do shell script (cmdPrefix & "t="& (theCounter as text) & ";((sec=t%60, t/=60, min=t%60, hrs=t/60));timestamp=$(printf \"%02d:%02d:%02d\" $hrs $min $sec);/bin/echo $timestamp" as text)
                do shell script ("/bin/echo \"" & (theDateStamp as text) & ".000 " & (item theIndex of these_titles as text) & "\" >> /private/tmp/cat.chapters.txt" as text)
                log ("Before the addition, theCounter is " & theCounter) as text
                set theCounter to do shell script (cmdPrefix & "t=" & theCounter & ";t=`echo $t\"+\\`ffprobe -loglevel panic -show_streams /private/tmp/concat" & (theIndex as text) & ".ts | egrep -m 1 'duration=[0-9]+\\.' | sed 's/duration=\\([0-9]*[0-9]\\)\\.[0-9]*/\\1/'\\`\" | bc`;echo $t" as text)
                log ("After the addition, theCounter is " & theCounter) as text
            end if
        end repeat
        -- Chapterize cat.mp4
        do shell script (cmdPrefix & "mp4chaps -i /private/tmp/cat.mp4" as text)
        -- Clean up
        do shell script "/bin/rm /private/tmp/concat* /private/tmp/cat.chapters.txt /private/tmp/cat.mp4"
        set theCounter to ""
    end btnConcatenate_

	on applicationWillFinishLaunching_(aNotification)
		-- Insert code here to initialize your application before any files are opened
	end applicationWillFinishLaunching_

	on applicationShouldTerminate_(sender)
		-- Insert code here to do any housekeeping before your application quits
		return current application's NSTerminateNow
	end applicationShouldTerminate_

end script

--
--  DASAppDelegate.applescript
--  itunes-concatenator
--
--  Created by David Schlachter on 2014-08-26.
--  Copyright (c) 2014 Red Eft Software. All rights reserved.
--

-- via http://macscripter.net/viewtopic.php?pid=173787 ; apparently needed for using arrays

script DASAppDelegate
	property parent : class "NSObject"

    global these_files, these_times, these_titles, the_index, the_pipes, theCounter, cmdPrefix

	-- IBOutlets
	property window : missing value
    property trackTable : missing value
    property catName : missing value
    property catArtist : missing value
    property catAlbum : missing value
    property catComposer : missing value
    property catGenre : missing value
    property catTrack : missing value
    property catTracks : missing value
    property catDisc : missing value
    property catDiscs : missing value

    -- Again, per macscripter, we'll set up bindings for the options
    property pcatName : ""
    property pcatArtist : ""
    property pcatAlbum : ""
    property pcatComposer : ""
    property pcatGenre : ""
    property pcatTrack : ""
    property pcatTracks : ""
    property pcatDisc : ""
    property pcatDiscs : ""

    -- Empty strings for each file name and location
    property pEachName : ""
    property pEachLocation : ""

    property these_titles : {}
    property these_files : {}
    property these_times: {}
    property the_index: {}
    property the_pipes: {}

    property theCounter : ""
    property cmdPrefix : "if [[ -r /etc/profile ]];then . /etc/profile;fi;if [[ -r ~/.bashrc ]];then . ~/.bashrc;fi;if [[ -r ~/.bash_profile ]];then . ~/.bash_profile;fi;"

    on awakeFromNib()
        --
        -- Check for FFMPEG
        --
        -- (This works, but it's ugly!)
        --
        --
        try
            do shell script cmdPrefix & " if [ -x \"`/usr/bin/which ffmpeg`\" ];then exit 0;else exit 1;fi"
        on error number error_number
            display dialog "You must install FFMPEG and MP4v2 to continue. \n\nYou may have to install Xcode Command Line Tools and Homebrew first. If you continue, we'll attempt to install them for you if they are not already present. \n\nIf you have already installed FFMPEG and MP4v2, ensure that they are in your bash path." buttons {"Quit","Install"} default button 2
            If button returned of result is "Install" then
                try
                    tell application "Terminal"
                        -- TODO: This should be cleaner -- Terminal in front, only one window, focus back to the cat'er afterwards
                        set newTab to do script cmdPrefix & "if [[ -x `which brew` ]];then brew install ffmpeg;else /usr/bin/ruby -e \"$(/usr/bin/curl -fsSL https://raw.github.com/Homebrew/homebrew/go/install)\";brew install ffmpeg mp4v2;fi"
                        delay 4
                        repeat
                            delay 0.1
                            if not busy of newTab then exit repeat
                        end repeat
                    end tell
                     -- TODO: This needs a better test, i.e., check if ffmpeg is actually accessible on the path
                    display dialog "FFMPEG was successfully installed"
                    -- TODO: This should actually work! (Bring us back to the foreground)
                    activate current application
                on error number error_number
                    display dialog "FFMPEG did not successfully install"
                end try
            end If
            If button returned of result is "Quit" then
                -- Fix!
                current application's NSApp's terminate()
            end If
        end try
    end awakeFromNib

    on btnGetTracks_(sender)
        -- Code to add iTunes tracks to our list
        -- via http://dougscripts.com/itunes/itinfo/info02.php and http://www.randomsequence.com/articles/applescript-to-send-selected-itunes-tracks-using-mail/
        tell application "iTunes"
            set these_titles to {}
            set these_times to {}
            set these_files to {}
            set the_index to {}
            if selection is not {} then -- there ARE tracks selected...
                set mySelection to selection
                set i to 1
                repeat with aTrack in mySelection
                    if class of aTrack is file track then
                        set end of these_titles to ((name of aTrack) as string)
                        set end of these_times to ((time of aTrack) as string)
                        set end of these_files to (posix path of (get location of aTrack))
                        set end of the_index to (count of these_titles)
                    end if
                end repeat
            end if
        end tell
        -- via http://stackoverflow.com/questions/25537750/setstringvalue-with-applescript-list
        set olddelimeters to AppleScript's text item delimiters
        set AppleScript's text item delimiters to "\n"
        set disp_titles to these_titles as string
        trackTable's setStringValue_(disp_titles)
        set AppleScript's text item delimiters to olddelimeters
    end btnGetTracks_

    on btnConcatenate_(sender)
        set the_pipes to {}
        repeat with theIndex in the_index
            -- TODO: Needs error checking!
            -- TODO: We need to check what kind of audio files we have, then adjust accordingly
            do shell script (cmdPrefix & "ffmpeg -i \"" & (item theIndex of these_files as text) & "\" -c copy -bsf:v h264_mp4toannexb -f mpegts /private/tmp/concat" & theIndex & ".ts 2> /dev/null" as text)
            log ("Processed file number" & theIndex) as text
            set end of the_pipes to ("/private/tmp/concat" & theIndex & ".ts" as text)
        end repeat
        -- Run the actual concatenation command in ffmpeg (for mp4/AAC files only!)
        -- via https://trac.ffmpeg.org/wiki/How%20to%20concatenate%20(join,%20merge)%20media%20files
        set olddelimeters to AppleScript's text item delimiters
        set AppleScript's text item delimiters to "|"
        set disp_thepipes to the_pipes as string
        do shell script (cmdPrefix & "ffmpeg -f mpegts -i \"concat:" & (disp_thepipes as text) & "\" -c copy -bsf:a aac_adtstoasc /private/tmp/cat.mp4")
        set AppleScript's text item delimiters to olddelimeters
        -- Now let's create the chapter file
        repeat with theIndex in the_index
            if theIndex < 2 then
                do shell script ("/bin/echo \"00:00:00.000 " & (item theIndex of these_titles as text) & "\" > /private/tmp/cat.chapters.txt" as text)
                set theCounter to (do shell script (cmdPrefix & "ffprobe -loglevel panic -show_streams /private/tmp/concat" & (theIndex as text) & ".ts | egrep -m 1 'duration=[0-9]+\\.' | sed 's/duration=\\([0-9]*[0-9]\\)\\.[0-9]*/\\1/'" as text) as string)
            else
                set theDateStamp to do shell script (cmdPrefix & "t="& (theCounter as text) & ";((sec=t%60, t/=60, min=t%60, hrs=t/60));timestamp=$(printf \"%02d:%02d:%02d\" $hrs $min $sec);/bin/echo $timestamp" as text)
                do shell script ("/bin/echo \"" & (theDateStamp as text) & ".000 " & (item theIndex of these_titles as text) & "\" >> /private/tmp/cat.chapters.txt" as text)
                log ("Before the addition, theCounter is " & theCounter) as text
                set theCounter to do shell script (cmdPrefix & "t=" & theCounter & ";t=`echo $t\"+\\`ffprobe -loglevel panic -show_streams /private/tmp/concat" & (theIndex as text) & ".ts | egrep -m 1 'duration=[0-9]+\\.' | sed 's/duration=\\([0-9]*[0-9]\\)\\.[0-9]*/\\1/'\\`\" | bc`;echo $t" as text)
                log ("After the addition, theCounter is " & theCounter) as text
            end if
        end repeat
        -- Chapterize cat.mp4
        do shell script (cmdPrefix & "mp4chaps -i /private/tmp/cat.mp4" as text)
        -- Clean up
        do shell script "/bin/rm /private/tmp/concat* /private/tmp/cat.chapters.txt /private/tmp/cat.mp4"
        set theCounter to ""
    end btnConcatenate_

	on applicationWillFinishLaunching_(aNotification)
		-- Insert code here to initialize your application before any files are opened
	end applicationWillFinishLaunching_

	on applicationShouldTerminate_(sender)
		-- Insert code here to do any housekeeping before your application quits
		return current application's NSTerminateNow
	end applicationShouldTerminate_

end script


Delete DirectJarvis.applescript

Delete DirectJarvis.applescript

(*Instead of using the spotlight app, this opens a new terminal directly*)

tell application "Terminal"
	activate
	(*sets up the script for dictation*)
	tell application "System Events" to keystroke "./jarvis.sh \"\""
	tell application "System Events" to key code 123

	(*opens up speech-to-text*)
	tell application "System Events" to key code 63
	tell application "System Events" to key code 63
	delay 4

	(*presses "enter" on DictationIM*)
	tell application "System Events" to tell process "DictationIM"
		click button 1
	end tell
	delay 1.2

	(*presses "enter" in the terminal*)
	tell application "System Events" to key code 36

	(*closes the terminal when process is completed*)
	set mainID to id of front window
	set toggle to 1
	delay 0.1
	set thetimeout to 100
	repeat until toggle ≥ thetimeout
		if busy of currentTab is false then
			close (every window whose id = mainID)
			set toggle to thetimeout
		end if
		set toggle to (toggle + 1)
		delay 0.1
	end repeat

end tell


We're going to use this applescript, after I rename it.

tell application "Finder"
	tell disk "MacCode"
		open
		tell container window
			set current view to icon view
			set toolbar visible to false
			set statusbar visible to false
			set the bounds to {30, 50, 490, 360}
		end tell
		close
		set opts to icon view options of container window
		tell opts
			set icon size to 60
			set arrangement to not arranged
		end tell
		set background picture of opts to file ".background:maccodeDMGBackground.png"
		--set position of item "Bindings" to {263, 76}
		set position of item "Source" to {370, 76}
		set position of item "MacCode.webloc" to {263, 196}
		-- set position of item "Growl version history for developers.webloc" to {370, 183}
		update without registering applications
		tell container window
			set the bounds to {31, 50, 490, 360}
			set the bounds to {30, 50, 490, 360}
		end tell
		update without registering applications
	end tell
	--give the finder some time to write the .DS_Store file
	delay 5
end tell

tell application "Finder"
	tell disk "Growl-SDK"
		open
		tell container window
			set current view to icon view
			set toolbar visible to false
			set statusbar visible to false
			set the bounds to {30, 50, 490, 360}
		end tell
		close
		set opts to icon view options of container window
		tell opts
			set icon size to 60
			set arrangement to not arranged
		end tell
		set background picture of opts to file ".background:growlSDK.png"
		set position of item "Bindings" to {263, 76}
		set position of item "Frameworks" to {370, 76}
		set position of item "Growl Developer Documentation.webloc" to {263, 196}
		set position of item "Growl version history for developers.webloc" to {370, 183}
		update without registering applications
		tell container window
			set the bounds to {31, 50, 490, 360}
			set the bounds to {30, 50, 490, 360}
		end tell
		update without registering applications
	end tell
	--give the finder some time to write the .DS_Store file
	delay 5
end tell


Issue #247: Fix bug in which loading of Kalabox icon fails on OS x < 10.7.

set kalaicon to 2
tell application "System Events" to set itExists to ¬
  exists of alias ((path to me as text) & ":..:..:..:..:app.icns")
if itExists
  set kalaicon to alias ((path to me as text) & ":..:..:..:..:app.icns")
end if
tell application "SystemUIServer"
  activate
  set my_password to display dialog ¬
    "Please enter your administrator password:" with title ¬
    "Password" with icon kalaicon ¬
    default answer ¬
    "" buttons {"Cancel", "OK"} default button 2 ¬
    giving up after 295 ¬
    with hidden answer
end tell

set kalaicon to alias ((path to me as text) & ":..:..:..:..:app.icns")
tell application "SystemUIServer"
  activate
  set my_password to display dialog ¬
    "Please enter your administrator password:" with title ¬
    "Password" with icon kalaicon ¬
    default answer ¬
    "" buttons {"Cancel", "OK"} default button 2 ¬
    giving up after 295 ¬
    with hidden answer
end tell