-- define globals
global completeMailboxList
global emailClient
global importFolderName
global logFile
global logInterval
global pathToErrorFile
global pathToHome
global pathToMail
global pathToMailboxes
global warming



-- define variables
set completeMailboxList to {}
set AppleScript's text item delimiters to ""
set warming to 0
tell application "Finder" to set pathToHome to (home as string)
set logInterval to 10



-- set some defaults based on client name
set emailClient to "Outlook Express"
set importFolderName to "Outlook-Import"



-- determine the version of Mac OS that is running
try
	get path to extensions
	activate
	display dialog "This script will only run on Mac OS X." buttons {"Quit"} default button 1
	return
on error
	set pathToMail to pathToHome & ":Library:Mail:"
	set pathToMailboxes to pathToMail & "Mailboxes:"
end try


try
	-- main procedure
	tell application "Outlook Express"
		my WaitUntilReady()
		set savedSchedules to {}
		set allSchedules to every «class cSch»
		set numberOfSchedules to length of allSchedules
		repeat with eachSchedule in allSchedules
			set savedSchedules to savedSchedules & «class enbl» of eachSchedule
			set «class enbl» of eachSchedule to false
		end repeat
		set topLevelfolderList to every «class cFld»
		repeat with eachFolder in topLevelfolderList
			set completeMailboxList to my traverseOutlookFolders(eachFolder)
		end repeat
		set userMailboxList to my selectMailboxesToImport()
		if (warming is equal to 0) then
			set keepAttachments to my selectWantAttachments()
			if (userMailboxList is not equal to {}) then
				my createImportFolder()
				
				-- create log file
				tell application "TextEdit"
					activate
					close every document saving ask
					make new document at the beginning of documents
					set the name of window 1 to importFolderName
					set text of document 1 to ¬
						"--- " & my displayTime() & ": Starting import from " & emailClient & " ---" & return & return & "Preparing mailboxes..." & return & return & ¬
						return & "--- Import in progress ---" & return
				end tell
				
				repeat with eachMailbox in userMailboxList
					set mailboxIsFolder to 0
					set mailboxHasMessages to 0
					set pathToOutlookMailbox to my findPositionOfOutlookMailboxInHierarchy(eachMailbox)
					try
						if (number of items in every «class cFld» of eachMailbox) is greater than 0 then
							set mailboxIsFolder to 1
						end if
					end try
					try
						if (number of every «class msg » in eachMailbox) is greater than 0 then
							set mailboxHasMessages to 1
						end if
					end try
					set mboxFile to my createMailboxInImportFolder(eachMailbox, mailboxIsFolder, mailboxHasMessages, pathToOutlookMailbox)
					if (mailboxHasMessages is equal to 1) then
						my processOutlookMailbox(eachMailbox, mboxFile, keepAttachments)
					end if
				end repeat
				my writeToLog("", 3)
				my writeToLog("", 4)
				my writeToLog("--- " & my displayTime() & ": Import finished! ---", 6)
				set allSchedules to every «class cSch»
				try
					repeat with counter from 1 to numberOfSchedules
						set «class enbl» of «class cSch» counter to item counter of savedSchedules
					end repeat
				end try
			end if
		end if
	end tell
	activate
	if (warming is equal to 0) then
		activate
		display dialog "Importing done! If Mail is already running, you need to quit and relaunch Mail." buttons {"OK"} default button 1
	end if
on error errText
	activate
	display dialog errText buttons {"Quit"} default button 1
end try
set AppleScript's text item delimiters to ""



-- routines
on createImportFolder()
	tell application "Finder"
		if (alias pathToMail exists) then
		else
			make new folder at alias (pathToHome & ":Library:") with properties {name:"Mail"}
		end if
		if (alias pathToMailboxes exists) then
		else
			make new folder at alias (pathToMail) with properties {name:"Mailboxes"}
		end if
	end tell
	set safeImportFolderName to my makeUniqueName(importFolderName, pathToMailboxes)
	set importFolderName to safeImportFolderName
	tell application "Finder"
		make new folder at alias pathToMailboxes with properties {name:importFolderName}
		set pathToImportFolder to (pathToMailboxes & importFolderName)
	end tell
end createImportFolder



on createMailboxInImportFolder(eachMailbox, mailboxIsFolder, mailboxHasMessages, pathToOutlookMailbox)
	tell application "Finder"
		set nameOfEachMailbox to name of eachMailbox
		set pathToNewFolder to pathToMailboxes & importFolderName & ":" & pathToOutlookMailbox
		if (mailboxIsFolder is equal to 1) then
			make new folder at alias pathToNewFolder with properties {name:nameOfEachMailbox}
			if (mailboxHasMessages is equal to 1) then
				set mboxName to " " & name of eachMailbox & ".temporary"
			end if
			set createLocation to pathToNewFolder & nameOfEachMailbox & ":"
		else
			set mboxName to name of eachMailbox & ".temporary"
			set createLocation to pathToNewFolder
		end if
		if (mailboxHasMessages is equal to 1) then
			try
				set existsLocation to alias createLocation
			on error
				set createLocation to pathToMailboxes & importFolderName & ":"
			end try
			set safeMboxName to my makeUniqueName(mboxName, createLocation)
			set mboxName to safeMboxName
			make new folder at alias createLocation with properties {name:mboxName}
			set tempMboxFile to createLocation & mboxName & ":mboxTemp"
			set mboxFile to createLocation & mboxName & ":mbox"
			set mboxCreateLocation to createLocation & mboxName & ":"
			make new file at alias (mboxCreateLocation) with properties {name:"mbox"}
		end if
	end tell
	try
		return mboxFile
	on error
		return {}
	end try
end createMailboxInImportFolder



on displayTime()
	set currentDate to current date
	get time string of currentDate
	return the result
end displayTime



on findPositionOfOutlookMailboxInHierarchy(eachMailbox)
	tell application "Outlook Express"
		set pathToOutlookMailbox to ""
		set parentMailboxes to {}
		set success to 0
		set currentMailbox to name of eachMailbox
		set currentParent to (name of parent of eachMailbox)
		set currentParentID to eachMailbox
		repeat until success is equal to 1
			if (currentParent is equal to "Outlook Express") then
				set success to 1
			else
				set parentMailboxes to parentMailboxes & currentParent as list
				set currentParentID to (parent of currentParentID)
				set currentParent to (name of parent of currentParentID)
			end if
		end repeat
	end tell
	set lengthOfPathToMailbox to length of parentMailboxes
	if (lengthOfPathToMailbox is greater than 0) then
		set reversedList to reverse of parentMailboxes
		repeat with counter from 1 to lengthOfPathToMailbox
			set pathToOutlookMailbox to pathToOutlookMailbox & item counter of reversedList & ":"
		end repeat
	end if
	return pathToOutlookMailbox
end findPositionOfOutlookMailboxInHierarchy



on makeUniqueName(proposedFile, proposedLocation)
	tell application "Finder"
		set mboxExtension to 0
		set dotPosition to (offset of ".mbox" in proposedFile) as number
		if (dotPosition is not equal to 0) then
			set mboxExtension to 1
			set namePrefix to ((characters 1 thru (dotPosition - 1) of proposedFile) as string)
		end if
		set success to 0
		set counter to 1
		set proposedName to proposedFile
		repeat until success is equal to 1
			set allNames to {}
			try
				set allNames to allNames & name of every file of alias proposedLocation
			end try
			try
				set allNames to allNames & name of every folder of alias proposedLocation
			end try
			if allNames contains proposedName then
				if (mboxExtension is equal to 1) then
					set proposedName to (namePrefix & " " & counter as string) & ".mbox"
				else
					set proposedName to proposedFile & " " & counter as string
				end if
				set counter to counter + 1
			else
				set success to 1
				return proposedName
			end if
		end repeat
	end tell
end makeUniqueName



on processOutlookMailbox(eachMailbox, mboxFile, keepAttachments)
	tell application "Finder"
		set AppleScript's text item delimiters to return
		set theOriginalMbox to (open for access file mboxFile with write permission)
		set messageCounter to 0
		try
			tell application "Outlook Express"
				set savedMboxName to name of eachMailbox
				my startingNewMailbox(savedMboxName)
				repeat
					set messageCounter to messageCounter + 1
					set currentMessage to «class msg » messageCounter of eachMailbox
					if (messageCounter mod logInterval is equal to 0) then
						my writeToLog(("Message " & messageCounter as string) & ": " & «class subj» of currentMessage, 4)
					end if
					set hasAttachments to every «class cAtc» of currentMessage
					
					if (hasAttachments is equal to {}) then
						try
							set unprocessedContent to «class mSrc» of currentMessage
						on error
							set unprocessedContent to «class pMHd» of currentMessage & return & "<< Outlook Express was unable to provide the content for this message >>" & return
						end try
					else
						if (keepAttachments is equal to "Yes") then
							try
								set unprocessedContent to «class mSrc» of currentMessage
							on error
								try
									set unprocessedContent to «class pMHd» of currentMessage & return & return & ¬
										"<< Attachment(s) were too large and were not imported from Outlook Express >>" & return & return & «class ctnt» of currentMessage
								on error
									set unprocessedContent to «class pMHd» of currentMessage & return & "<< Message was too large and was not imported from Outlook Express >>" & return
								end try
							end try
						else
							set unprocessedContent to «class pMHd» of currentMessage & return & «class ctnt» of currentMessage
						end if
					end if
					
					-- if the message doesn't begin with a From\020 header, then construct one
					if (text item 1 of unprocessedContent begins with "From ") then
						set fromHeader to ""
					else
						set fromHeader to "From " & «class addr» of «class sndr» of currentMessage & " " & «class tims» of currentMessage & return
					end if
					set unprocessedContent to fromHeader & unprocessedContent
					set messageContent to ""
					set hasFromSpace to offset of (return & "From ") in unprocessedContent
					set hasContentType to offset of (return & "Content-type: multi") in unprocessedContent
					
					if (hasFromSpace is equal to 0 and hasContentType is equal to 0) then
						set messageContent to unprocessedContent
					else
						set AppleScript's text item delimiters to return
						set previousLine to "Nil"
						set lineCounter to 1
						try
							repeat
								set eachLine to text item lineCounter of unprocessedContent
								if (eachLine begins with "From " and lineCounter is greater than 1) then
									if (previousLine is equal to "") then
										set messageContent to messageContent & ">" & eachLine & return
									end if
								else if (keepAttachments is equal to "No" and eachLine begins with "Content-type: multi") then
									set lineCounter to lineCounter + 1
									set nextLine to text item lineCounter of unprocessedContent
									if (nextLine contains "boundary") then
									else
										set messageContent to messageContent & text item lineCounter of unprocessedContent & return
									end if
								else
									set messageContent to messageContent & eachLine & return
								end if
								set lineCounter to lineCounter + 1
								set previousLine to eachLine
							end repeat
						end try
						
					end if
					
					set intervalCounter to 0
					try
						repeat
							set startCounter to (intervalCounter * 100) + 1
							set endCounter to (intervalCounter * 100) + 100
							try
								set subContent to text items startCounter thru endCounter of messageContent
							on error
								set subContent to text items startCounter thru -1 of messageContent
							end try
							set AppleScript's text item delimiters to ASCII character 10
							tell application "Finder" to write (subContent as string) & (ASCII character 10) to theOriginalMbox starting at eof
							set AppleScript's text item delimiters to return
							set intervalCounter to intervalCounter + 1
						end repeat
					end try
				end repeat
			end tell
		end try
		close access theOriginalMbox
		set AppleScript's text item delimiters to ""
	end tell
	tell application "Finder"
		set temporaryFolder to (container of alias mboxFile)
		set temporaryFolderName to name of temporaryFolder
		set temporaryOffset to (offset of ".temporary" in temporaryFolderName)
		set newFolderName to ((characters 1 thru (temporaryOffset - 1) of temporaryFolderName) as string) & ".mbox"
		set name of temporaryFolder to newFolderName
	end tell
end processOutlookMailbox



on selectMailboxesToImport()
	set completeMailboxNames to {}
	set userMailboxList to {}
	repeat with eachItem in completeMailboxList
		set completeMailboxNames to completeMailboxNames & {name of eachItem}
	end repeat
	with timeout of 600 seconds
		activate
		set theResult to display dialog ¬
			"You can choose to either import all mailboxes from " & emailClient & " or choose a subset of mailboxes to import." buttons {"Import all mailboxes", "Choose mailboxes to import"} default button 2
		if (theResult is equal to false) then
			set warming to 1
			return
		else
			set chosenPath to button returned of theResult
		end if
	end timeout
	if (chosenPath is equal to "Import all mailboxes") then
		set userMailboxList to completeMailboxList
	else
		with timeout of 600 seconds
			activate
			set selectionList to choose from list (completeMailboxNames) ¬
				with prompt "Select which folder(s) you would like to import. Select multiple items by holding down the command key." with multiple selections allowed
			if (selectionList is equal to false) then
				set warming to 1
			end if
		end timeout
		if (warming is equal to 0) then
			try
				set userMailboxList to {}
				set namesCounter to 1
				set mailboxesCounter to 1
				try
					repeat
						set name1 to item namesCounter of selectionList
						set name2 to name of item mailboxesCounter of completeMailboxList
						if (name1 is equal to name2) then
							set userMailboxList to userMailboxList & {item mailboxesCounter of completeMailboxList}
							set namesCounter to namesCounter + 1
							set mailboxesCounter to mailboxesCounter + 1
						else
							set mailboxesCounter to mailboxesCounter + 1
						end if
					end repeat
				end try
			on error
				return {}
			end try
		end if
	end if
	return userMailboxList
end selectMailboxesToImport



on selectWantAttachments()
	activate
	set selectedItem to display dialog "Include attachments?" buttons {"No", "Yes"} default button 2 giving up after 60
	return (button returned of selectedItem) as string
end selectWantAttachments



on startingNewMailbox(savedMboxName)
	my writeToLog("", 4)
	set displayString to "Importing '" & savedMboxName & "'"
	my writeToLog(displayString, 3)
end startingNewMailbox



on traverseOutlookFolders(currentFolder)
	tell application "Outlook Express"
		set completeMailboxList to completeMailboxList & (currentFolder as list)
		set subFolders to every «class cFld» of currentFolder
		repeat with eachFolder in subFolders
			my traverseOutlookFolders(eachFolder, completeMailboxList)
		end repeat
	end tell
	return completeMailboxList
end traverseOutlookFolders



on WaitUntilReady()
	with timeout of 5 seconds
		repeat
			try
				tell application "Outlook Express" to get version
				return
			end try
		end repeat
	end timeout
end WaitUntilReady



on writeToLog(passedString, paragraphNumber)
	tell application "TextEdit"
		set paragraph paragraphNumber of text of document 1 to passedString & return
	end tell
end writeToLog