
--
--  Firewolf Website Browser
--  Made by GravityScore and 1lann
--  License found here: https://raw.github.com/1lann/Firewolf/master/LICENSE
--
--  Original Concept From RednetExplorer 2.4.1
--  RednetExplorer Made by ComputerCraftFan11
--


--  -------- Variables

-- Version
local version = "2.5"
local build = 36
local browserAgent = "Firewolf " .. version
local tArgs = {...}

-- Server Identification
local serverID = "other"
local serverList = {experimental = "Experimental", other = "Other"}

-- Updating
local autoupdate = "false"
local noInternet = false

-- Resources
local graphics = {}
local files = {}
local w, h = term.getSize()

-- Debugging
local debugFile = nil

-- Environment
local oldEnv = {}
local env = {}
local api = {}
local override = {}
local antivirus = {}

-- Themes
local theme = {}

-- Databases
local blacklist = {}
local whitelist = {}
local dnsDatabase = {{}, {}}

-- Website Loading
local pages = {}
local errorPages = {}

local website = ""
local homepage = ""
local timeout = 0.2
local loadingRate, loadingClock = 0, nil
local openAddressBar, clickableAddressBar = true, true
local menuBarOpen = false
local offsetclick = true
local websiteerror = nil

-- Tabs
local tabs = {}
local filters = {}
local currentTab = 1

-- Protocols
local curProtocol = {}
local protocols = {}

-- History
local addressBarHistory = {}

-- Events
local event_load = "firewolf_websiteLoadEvent"
local event_redirect = "firewolf_redirectEvent"
local event_error = "firewolf_siteErrorEvent"
local event_exit = "firewolf_exitEvent"

-- Download URLs
local firewolfURL = "https://raw.github.com/1lann/Firewolf/master/entities/other.lua"
local serverURL = "https://raw.github.com/1lann/Firewolf/master/server/server-release.lua"
local buildURL = "https://raw.github.com/1lann/Firewolf/master/build"

-- Data Locations
local rootFolder = "./.Firewolf_Data"
local cacheFolder = rootFolder .. "/cache"
local serverFolder = rootFolder .. "/servers"
local websiteDataFolder = rootFolder .. "/website_data"
local themeLocation = rootFolder .. "/theme"
local serverLocation = rootFolder .. "/server_software"
local settingsLocation = rootFolder .. "/settings"
local debugLogLocation = "./firewolf-log"
local firewolfLocation = "/" .. shell.getRunningProgram()

local userBlacklist = rootFolder .. "/user_blacklist"
local userWhitelist = rootFolder .. "/user_whitelist"

local function isAdvanced() return term.isColor and term.isColor() end

local function modemPresent()
	if curProtocol == protocols.rdnt then
		for _, v in pairs(rs.getSides()) do
			if peripheral.getType(v) == "modem" then rednet.open(v) return true end
		end
	else return true end
	return false
end


--  -------- Utilities

local function debugLog(n, ...)
	local lArgs = {...}
	if debugFile then
		if not n then return end
		debugFile:write("\n" .. tostring(n) .. " : ")
		for k, v in pairs(lArgs) do
			if type(v) == "string" or type(v) == "number" or not type(v) or type(v) == "boolean" then
				debugFile:write(tostring(v) .. ", ")
			else debugFile:write("type-" .. type(v) .. ", ") end
		end
	end
end

local function clear(site, background, drawmenu)
	local titles = {
		["firewolf"] = "Firewolf",
		["server/rdnt"] = "Server Management",
		["server/http"] = "Server Management",
		["help"] = "Help",
		["settings"] = "Settings",
		["credits"] = "Credits",
		["crash"] = "Crashed!",
		["overspeed"] = "Too Fast!"
	}

	local title = titles[site] or ""
	term.setBackgroundColor(background or colors.black)
	term.setTextColor(colors[theme["address-bar-text"]])
	if drawmenu ~= true then term.clear() end

	if not menuBarOpen then
		term.setBackgroundColor(colors[theme["address-bar-background"]])
		term.setCursorPos(2, 1)
		term.clearLine()
		if site:len() > w - 10 then site = site:sub(1, 38) .. "..." end
		if curProtocol == protocols.http then write("http://" .. site)
		else write("rdnt://" .. site) end
		term.setCursorPos(w - title:len() - 1, 1)
		write(title)

		if isAdvanced() then
			term.setCursorPos(w, 1)
			term.setBackgroundColor(colors[theme["top-box"]])
			term.setTextColor(colors[theme["text-color"]])
			write("<")
		end

		term.setBackgroundColor(background or colors.black)
		term.setTextColor(colors.white)
	else
		term.setCursorPos(1, 1)
		term.setBackgroundColor(colors[theme["top-box"]])
		term.setTextColor(colors[theme["text-color"]])
		term.clearLine()
		write("> [- Exit Firewolf -]                              ")
	end

	print("")
end

local function modRead(properties)
	local w, h = term.getSize()
	local defaults = {replaceChar = nil, history = nil, visibleLength = nil, textLength = nil,
		liveUpdates = nil, exitOnKey = nil}
	if not properties then properties = {} end
	for k, v in pairs(defaults) do if not properties[k] then properties[k] = v end end
	if properties.replaceChar then properties.replaceChar = properties.replaceChar:sub(1, 1) end
	if not properties.visibleLength then properties.visibleLength = w end

	local sx, sy = term.getCursorPos()
	local line = ""
	local pos = 0
	local historyPos = nil

	local function redraw(repl)
		local scroll = 0
		if properties.visibleLength and sx + pos > properties.visibleLength + 1 then
			scroll = (sx + pos) - (properties.visibleLength + 1)
		end

		term.setCursorPos(sx, sy)
		local a = repl or properties.replaceChar
		if a then term.write(string.rep(a, line:len() - scroll))
		else term.write(line:sub(scroll + 1, -1)) end
		term.setCursorPos(sx + pos - scroll, sy)
	end

	local function sendLiveUpdates(event, ...)
		if type(properties.liveUpdates) == "function" then
			local ox, oy = term.getCursorPos()
			local a, data = properties.liveUpdates(line, event, ...)
			if a == true and not data then
				term.setCursorBlink(false)
				return line
			elseif a == true and data then
				term.setCursorBlink(false)
				return data
			end
			term.setCursorPos(ox, oy)
		end
	end

	local a = sendLiveUpdates("delete")
	if a then return a end
	term.setCursorBlink(true)
	while true do
		local e, but, x, y, p4, p5 = os.pullEvent()
		if e == "char" then
			local s = false
			if properties.textLength and line:len() < properties.textLength then s = true
			elseif not properties.textLength then s = true end

			local canType = true
			if not properties.grantPrint and properties.refusePrint then
				local canTypeKeys = {}
				if type(properties.refusePrint) == "table" then
					for _, v in pairs(properties.refusePrint) do
						table.insert(canTypeKeys, tostring(v):sub(1, 1))
					end
				elseif type(properties.refusePrint) == "string" then
					for char in properties.refusePrint:gmatch(".") do
						table.insert(canTypeKeys, char)
					end
				end
				for _, v in pairs(canTypeKeys) do if but == v then canType = false end end
			elseif properties.grantPrint then
				canType = false
				local canTypeKeys = {}
				if type(properties.grantPrint) == "table" then
					for _, v in pairs(properties.grantPrint) do
						table.insert(canTypeKeys, tostring(v):sub(1, 1))
					end
				elseif type(properties.grantPrint) == "string" then
					for char in properties.grantPrint:gmatch(".") do table.insert(canTypeKeys, char) end
				end
				for _, v in pairs(canTypeKeys) do if but == v then canType = true end end
			end

			if s and canType then
				line = line:sub(1, pos) .. but .. line:sub(pos + 1, -1)
				pos = pos + 1
				redraw()
			end
		elseif e == "key" then
			if but == keys.enter then break
			elseif but == keys.left then if pos > 0 then pos = pos - 1 redraw() end
			elseif but == keys.right then if pos < line:len() then pos = pos + 1 redraw() end
			elseif (but == keys.up or but == keys.down) and properties.history and
					#properties.history > 0 then
				redraw(" ")
				if but == keys.up then
					if not historyPos and #properties.history > 0 then
						historyPos = #properties.history
					elseif historyPos > 1 then historyPos = historyPos - 1 end
				elseif but == keys.down then
					if historyPos == #properties.history then historyPos = nil
					elseif historyPos then historyPos = historyPos + 1 end
				end

				if properties.history and historyPos then
					line = properties.history[historyPos]
					pos = line:len()
				else
					line = ""
					pos = 0
				end

				redraw()
				local a = sendLiveUpdates("history")
				if a then return a end
			elseif but == keys.backspace and pos > 0 then
				redraw(" ")
				line = line:sub(1, pos - 1) .. line:sub(pos + 1, -1)
				pos = pos - 1
				redraw()
				local a = sendLiveUpdates("delete")
				if a then return a end
			elseif but == keys.home then
				pos = 0
				redraw()
			elseif but == keys.delete and pos < line:len() then
				redraw(" ")
				line = line:sub(1, pos) .. line:sub(pos + 2, -1)
				redraw()
				local a = sendLiveUpdates("delete")
				if a then return a end
			elseif but == keys["end"] then
				pos = line:len()
				redraw()
			elseif properties.exitOnKey then
				if but == properties.exitOnKey or (properties.exitOnKey == "control" and
						(but == 29 or but == 157)) then
					term.setCursorBlink(false)
					return nil
				end
			end
		end

		local a = sendLiveUpdates(e, but, x, y, p4, p5)
		if a then return a end
	end

	term.setCursorBlink(false)
	if line then line = line:gsub("^%s*(.-)%s*$", "%1") end
	return line
end


--  -------- API

--  Drawing

local function printWithType(t, func)
	if type(t) == "table" then
		for _, v in pairs(t) do env.pcall(function() printWithType(v, func) end) end
	else func(tostring(t)) end
end

api.centerWrite = function(text)
	printWithType(text, function(t)
		local x, y = term.getCursorPos()
		term.setCursorPos(math.ceil((w + 1)/2 - t:len()/2), y)
		write(t)
	end)
end

api.centerPrint = function(text)
	printWithType(text, function(t)
		local x, y = term.getCursorPos()
		term.setCursorPos(math.ceil((w + 2)/2 - t:len()/2), y)
		print(t)
	end)
end

api.leftWrite = function(text)
	printWithType(text, function(t)
		local x, y = term.getCursorPos()
		term.setCursorPos(1, y)
		write(t)
	end)
end

api.leftPrint = function(text)
	printWithType(text, function(t)
		local x, y = term.getCursorPos()
		term.setCursorPos(1, y)
		print(t)
	end)
end

api.rightWrite = function(text)
	printWithType(text, function(t)
		local x, y = term.getCursorPos()
		term.setCursorPos(w - t:len() + 1, y)
		write(t)
	end)
end

api.rightPrint = function(text)
	printWithType(text, function(t)
		local x, y = term.getCursorPos()
		term.setCursorPos(w - t:len() + 1, y)
		print(t)
	end)
end

api.clearArea = function() term.clear() term.setCursorPos(1, 1) end
api.cPrint = function(text) api.centerPrint(text) end
api.cWrite = function(text) api.centerWrite(text) end
api.lPrint = function(text) api.leftPrint(text) end
api.lWrite = function(text) api.leftWrite(text) end
api.rPrint = function(text) api.rightPrint(text) end
api.rWrite = function(text) api.rightWrite(text) end


--  Server Interation

api.loadFileFromServer = function(path)
	if type(path) ~= "string" then error("expected string", 2) end
	sleep(0.05)
	if path:sub(1, 1) == "/" then path = path:sub(2, -1) end
	local id, content = curProtocol.getWebsite(website .. "/" .. path)
	if id then return content end
	return nil
end

api.ioReadFileFromServer = function(path)
	local content = api.loadFileFromServer(path)
	if content then
		local f = env.io.open(rootFolder .. "/temp_file", "w")
		f:write(content)
		f:close()
		return env.io.open(rootFolder .. "/temp_file", "r")
	end
	return nil
end

api.loadImageFromServer = function(path)
	local content = api.loadFileFromServer(path)
	if content then
		local f = env.io.open(rootFolder .. "/temp_file", "w")
		f:write(content)
		f:close()

		local image = paintutils.loadImage(rootFolder .. "/temp_file")
		env.fs.delete("/temp_file")
		return image
	end
	return nil
end

api.writeDataFile = function(path, content)
	if type(path) ~= "string" or type(content) ~= "string" then
		error("expected string, string", 2) end
	if path:sub(1, 1) == "/" then path = path:sub(2, -1) end
	local dataPath = websiteDataFolder .. "/" .. path:gsub("/", "$slazh$")

	if env.fs.isReadOnly(dataPath) then return false end
	if env.fs.exists(dataPath) then env.fs.delete(dataPath) end
	local f = env.io.open(dataPath, "w")
	if not f then return false end
	f:write(content)
	f:close()
	return true
end

api.readDataFile = function(path)
	if type(path) ~= "string" then error("readDataFile: expected string") end
	if path:sub(1, 1) == "/" then path = path:sub(2, -1) end
	local dataPath = websiteDataFolder .. "/" .. path:gsub("/", "$slazh$")

	if env.fs.isDir(dataPath) then env.fs.delete(dataPath) end
	if env.fs.exists(dataPath) then
		local f = env.io.open(dataPath, "r")
		local cont = f:read("*a")
		f:close()
		return cont
	end
	return nil
end

api.saveFileToUserComputer = function(content)
	if type(content) ~= "string" then error("expected string", 2) end
	local oldback, oldtext = override.term.getBackgroundColor(), override.term.getTextColor()
	local ox, oy = term.getCursorPos()

	term.setTextColor(colors[theme["text-color"]])
	term.setBackgroundColor(colors[theme["background"]])
	term.clear()
	term.setCursorPos(1, 1)
	term.setBackgroundColor(colors[theme["top-box"]])
	print("")
	leftPrint(string.rep(" ", 20))
	leftPrint(" Save File Request  ")
	leftPrint(string.rep(" ", 20))
	print("")

	term.setBackgroundColor(colors[theme["bottom-box"]])
	for i = 1, 11 do rightPrint(string.rep(" ", 36)) end
	term.setCursorPos(1, 7)
	rightPrint("The website: ")
	rightPrint(website .. "  ")
	rightPrint("Is requesting to save a file ")
	rightPrint("to your computer. ")

	local ret = nil
	local opt = prompt({{"Save File", w - 16, 12}, {"Cancel", w - 13, 13}}, "vertical")
	if opt == "Save File" then
		while not ret do
			term.setCursorPos(1, 15)
			rightWrite(string.rep(" ", 36))
			term.setCursorPos(w - 34, 15)
			write("Path: /")
			local p = read()

			term.setCursorPos(1, 15)
			rightWrite(string.rep(" ", 36))
			if p == "" then
				rightWrite("Cancelled ")
				break
			elseif fs.exists("/" .. p) then
				rightWrite("File Already Exists! ")
			else
				rightWrite("File Saved! ")
				ret = "/" .. p
				local f = env.io.open(ret, "w")
				f:write(content)
				f:close()
			end

			openAddressBar = false
			sleep(1.3)
			openAddressBar = true
		end
	elseif opt == "Cancel" then
		term.setCursorPos(1, 15)
		rightWrite("Saving Cancelled! ")
	end

	openAddressBar = false
	sleep(1.3)
	openAddressBar = true

	term.setBackgroundColor(oldback or colors.black)
	term.setTextColor(oldtext or colors.white)
	term.clear()
	term.setCursorPos(ox, oy)
	return ret
end

api.urlDownload = function(url)
	if type(url) ~= "string" then error("expected string", 2) end
	local source = nil
	http.request(url)
	local a = os.startTimer(10)
	while true do
		local e, surl, handle = os.pullEvent()
		if e == "http_success" then
			source = handle.readAll()
			break
		elseif e == "http_failure" or (e == "timer" and surl == a) then
			break
		end
	end

	if type(source) == "string" then
		return api.saveFileToUserComputer(source)
	else return nil end
end

api.pastebinDownload = function(code)
	return api.urlDownload("http://pastebin.com/raw.php?i=" .. tostring(code))
end


--  Redirection

api.redirect = function(url)
	if type(url) ~= "string" then url = "home" end
	os.queueEvent(event_redirect, url:gsub("rdnt://"):gsub("http://"))
	error()
end


--  Theme

api.themeColor = function(tag)
	if type(tag) ~= "string" then error("themeColor: expected string") end
	return colors[theme[tag] or defaultTheme[tag] or "white"]
end

api.themeColour = function(tag) return api.themeColor(tag) end


--  Prompt

api.prompt = function(list, dir)
	if isAdvanced() then
		for _, v in pairs(list) do
			if v.bg then term.setBackgroundColor(v.bg) end
			if v.tc then term.setTextColor(v.tc) end
			if v[2] == -1 then v[2] = math.ceil((w + 1)/2 - (v[1]:len() + 6)/2) end

			term.setCursorPos(v[2], v[3])
			write("[- " .. v[1])
			term.setCursorPos(v[2] + v[1]:len() + 3, v[3])
			write(" -]")
		end

		while true do
			local e, but, x, y = os.pullEvent()
			if e == "mouse_click" then
				for _, v in pairs(list) do
					if x >= v[2] and x <= v[2] + v[1]:len() + 5 and y + (v.coffset or 0) == v[3] then
						return v[1]
					end
				end
			end
		end
	else
		for _, v in pairs(list) do
			term.setBackgroundColor(colors.black)
			term.setTextColor(colors.white)
			if v[2] == -1 then v[2] = math.ceil((w + 1)/2 - (v[1]:len() + 4)/2) end

			term.setCursorPos(v[2], v[3])
			write("  " .. v[1])
			term.setCursorPos(v[2] + v[1]:len() + 2, v[3])
			write("  ")
		end

		local key1 = dir == "horizontal" and 203 or 200
		local key2 = dir == "horizontal" and 205 or 208

		local curSel = 1
		term.setCursorPos(list[curSel][2], list[curSel][3])
		write("[")
		term.setCursorPos(list[curSel][2] + list[curSel][1]:len() + 3, list[curSel][3])
		write("]")
		while true do
			local e, key = os.pullEvent("key")
			term.setCursorPos(list[curSel][2], list[curSel][3])
			write(" ")
			term.setCursorPos(list[curSel][2] + list[curSel][1]:len() + 3, list[curSel][3])
			write(" ")
			if key == key1 and curSel > 1 then curSel = curSel - 1
			elseif key == key2 and curSel < #list then curSel = curSel + 1
			elseif key == 28 then return list[curSel][1] end
			term.setCursorPos(list[curSel][2], list[curSel][3])
			write("[")
			term.setCursorPos(list[curSel][2] + list[curSel][1]:len() + 3, list[curSel][3])
			write("]")
		end
	end
end

api.scrollingPrompt = function(list, x, y, len, width, coffset)
	local wid = width
	if not wid then wid = w - 3 end

	local function updateDisplayList(items, loc, len)
		local ret = {}
		for i = 1, len do
			local item = items[i + loc - 1]
			if item then table.insert(ret, item) end
		end
		return ret
	end

	if isAdvanced() then
		local function draw(a)
			for i, v in ipairs(a) do
				term.setCursorPos(x, y + i - 1)
				write(string.rep(" ", wid))
				term.setCursorPos(x, y + i - 1)
				write("[ " .. v:sub(1, wid - 5))
				term.setCursorPos(wid + x - 2, y + i - 1)
				write("  ]")
			end
		end

		local loc = 1
		local disList = updateDisplayList(list, loc, len)
		draw(disList)
		
		while true do
			local e, but, clx, cly = os.pullEvent()
			if e == "key" and but == 200 and loc > 1 then
				loc = loc - 1
				disList = updateDisplayList(list, loc, len)
				draw(disList)
			elseif e == "key" and but == 208 and loc + len - 1 < #list then
				loc = loc + 1
				disList = updateDisplayList(list, loc, len)
				draw(disList)
			elseif e == "mouse_scroll" and but > 0 and loc + len - 1 < #list then
				loc = loc + but
				disList = updateDisplayList(list, loc, len)
				draw(disList)
			elseif e == "mouse_scroll" and but < 0 and loc > 1 then
				loc = loc + but
				disList = updateDisplayList(list, loc, len)
				draw(disList)
			elseif e == "mouse_click" then
				for i, v in ipairs(disList) do
					if clx >= x and clx <= x + wid and cly + coffset == i + y - 1 then return v end
				end
			end
		end
	else
		local function draw(a)
			for i, v in ipairs(a) do
				term.setCursorPos(x, y + i - 1)
				write(string.rep(" ", wid))
				term.setCursorPos(x, y + i - 1)
				write("[ ] " .. v:sub(1, wid - 5))
			end
		end

		local loc = 1
		local curSel = 1
		local disList = updateDisplayList(list, loc, len)
		draw(disList)
		term.setCursorPos(x + 1, y + curSel - 1)
		write("x")

		while true do
			local e, key = os.pullEvent()
			term.setCursorPos(x + 1, y + curSel - 1)
			write(" ")
			if e == "key" and key == 200 then
				if curSel > 1 then curSel = curSel - 1
				elseif loc > 1 then
					loc = loc - 1
					disList = updateDisplayList(list, loc, len)
					draw(disList)
				end
			elseif e == "key" and key == 208 then
				if curSel < #disList then curSel = curSel + 1
				elseif loc + len - 1 < #list then
					loc = loc + 1
					disList = updateDisplayList(list, loc, len)
					draw(disList)
				end
			elseif e == "key" and key == 28 then return list[curSel + loc - 1] end
			term.setCursorPos(x + 1, y + curSel - 1)
			write("x")
		end
	end
end

-- Set Environment
for k, v in pairs(getfenv(0)) do env[k] = v end
for k, v in pairs(getfenv(1)) do env[k] = v end
for k, v in pairs(env) do oldEnv[k] = v end
for k, v in pairs(api) do env[k] = v end


--  -------- Override

for k, v in pairs(env) do override[k] = v end
local curtext, curbackground = colors.white, colors.black
override.term = {}
for k, v in pairs(env.term) do override.term[k] = v end
override.os = {}
for k, v in pairs(env.os) do override.os[k] = v end

override.term.getSize = function()
	local a, b = env.term.getSize()
	return a, b - 2
end

override.term.setCursorPos = function(x, y)
	if y < 1 then return env.term.setCursorPos(x, 2)
	else return env.term.setCursorPos(x, y + 1) end
end

override.term.getCursorPos = function()
	local x, y = env.term.getCursorPos()
	return x, y - 1
end

override.term.getBackgroundColor = function() return curbackground end
override.term.getBackgroundColour = function() return override.term.getBackgroundColor() end

override.term.setBackgroundColor = function(col)
	curbackground = col
	return env.term.setBackgroundColor(col)
end

override.term.setBackgroundColour = function(col) return override.term.setBackgroundColor(col) end

override.term.getTextColor = function() return curtext end
override.term.getTextColour = function() return override.term.getTextColor() end

override.term.setTextColor = function(col)
	curtext = col
	return env.term.setTextColor(col)
end

override.term.setTextColour = function(col) return override.term.setTextColor(col) end

override.term.clear = function()
	local x, y = term.getCursorPos()
	local oldbackground = override.term.getBackgroundColor()
	local oldtext = override.term.getTextColor()
	clear(website, curbackground)

	term.setBackgroundColor(oldbackground)
	term.setTextColor(oldtext)
	term.setCursorPos(x, y)
end

override.term.scroll = function(n)
	local x, y = term.getCursorPos()
	local oldbackground = override.term.getBackgroundColor()
	local oldtext = override.term.getTextColor()

	env.term.scroll(n)
	clear(website, curbackground, true)
	term.setBackgroundColor(oldbackground)
	term.setTextColor(oldtext)
	term.setCursorPos(x, y)
end

override.term.isColor = function() return isAdvanced() end
override.term.isColour = function() return override.term.isColor() end

override.os.queueEvent = function(event, ...)
	if event == "terminate" or event == event_exit then return end
	if event == event_error and not websiteerror then return end
	env.os.queueEvent(event, ...)
end

override.prompt = function(list, dir)
	local a = {}
	for k, v in pairs(list) do
		table.insert(a, {v[1], v[2], v[3] + 1, tc = v.tc or curtext,
			bg = v.bg or curbackground, coffset = 1})
	end
	return env.prompt(a, dir)
end

override.scrollingPrompt = function(list, x, y, len, width)
	return env.scrollingPrompt(list, x, y + 1, len, width, 1)
end

local barTerm = {}
for k, v in pairs(override.term) do barTerm[k] = v end
barTerm.clear = override.term.clear
barTerm.scroll = override.term.scroll

local safeTerm = {}
for k, v in pairs(term) do safeTerm[k] = v end

override.showBar = function()
	offsetclick, clickableAddressBar = true, true
	return os.pullEvent, barTerm
end

override.hideBar = function()
	offsetclick, clickableAddressBar = false, false
	return os.pullEvent, safeTerm
end

setfenv(api.saveFileToUserComputer, override)


--  -------- Antivirus

local antivirusOverrides = {
	["Run Files"] = {"shell.run", "os.run"},
	["Modify System"] = {"shell.setAlias", "shell.clearAlias", "os.setComputerLabel", 
		"shell.setDir", "shell.setPath"},
	["Modify Files"] = {"fs.makeDir", "fs.move", "fs.copy", "fs.delete", "fs.open",
		"io.open", "io.write", "io.read", "io.close"},
	["Shutdown Computer"] = {"os.shutdown", "os.reboot", "shell.exit"}
}

local antivirusDestroy = {
	"rawset", "rawget", "setfenv", "loadfile", "loadstring", "dofile", "getfenv"
}

local function triggerAntivirus(offence, onlyCancel)
	local oldback, oldtext = curbackground, curtext
	local ox, oy = term.getCursorPos()
	openAddressBar = false
	term.setBackgroundColor(colors[theme["address-bar-background"]])
	term.setTextColor(colors[theme["address-bar-text"]])
	term.setCursorPos(2, 1)
	term.clearLine()
	write("Request: " .. offence)
	term.setCursorPos(w - 8, 1)
	write("[C]ancel")
	if not onlyCancel then
		term.setCursorPos(w - 16, 1)
		write("[A]llow")
	end

	offsetclick = false
	local stat = ""
	while true do
		local e, but, x, y = env.os.pullEvent()
		if e == "mouse_click" and y == 1 then
			if x >= w - 8 and x <= w - 1 then stat = "cancel"
			elseif not onlyCancel and x >= w - 16 and x <= w - 9 then stat = "allow" end
		elseif e == "char" and but == "c" then stat = "cancel"
		elseif e == "char" and not onlyCancel and but == "a" then stat = "allow" end
		if stat ~= "" then break end
	end
	offsetclick = true

	clear(website, nil, true)
	term.setTextColor(colors.white)
	term.setBackgroundColor(colors.black)
	term.setCursorPos(ox, oy)
	term.setBackgroundColor(oldback)
	term.setTextColor(oldtext)
	if not onlyCancel and stat == "allow" then
		-- To prevent the menu bar from opening
		os.queueEvent("firewolf_requiredEvent")
		os.pullEvent()

		openAddressBar = true
		return true
	elseif stat == "cancel" then
		openAddressBar = true
		redirect("home")
	end
end

for k, v in pairs(override) do antivirus[k] = v end

antivirus.shell = {}
for k, v in pairs(override.shell) do antivirus.shell[k] = v end
antivirus.os = {}
for k, v in pairs(override.os) do antivirus.os[k] = v end
antivirus.fs = {}
for k, v in pairs(override.fs) do antivirus.fs[k] = v end
antivirus.io = {}
for k, v in pairs(override.io) do antivirus.io[k] = v end

for warning, v in pairs(antivirusOverrides) do
	for k, func in pairs(v) do
		if func:find(".", 1, true) then
			-- Functions in another table
			local table = func:sub(1, func:find(".", 1, true) - 1)
			local funcname = func:sub(func:find(".", 1, true) + 1, -1)

			antivirus[table][funcname] = function(...)
				env.setfenv(triggerAntivirus, env)
				if triggerAntivirus(warning) then
					return override[table][funcname](...)
				end
			end
		else
			-- Plain functions
			antivirus[func] = function(...)
				env.setfenv(triggerAntivirus, env)
				if triggerAntivirus(warning) then
					return override[func](...)
				end
			end
		end
	end
end

for k, v in pairs(antivirusDestroy) do
	antivirus[v] = function(...)
		env.setfenv(triggerAntivirus, env)
		triggerAntivirus("Destory your System! D:", true)
		return nil
	end
end

antivirus.pcall = function(...)
	local suc, err = env.pcall(...)
	if err:lower():find("terminate") then error("terminate") end
	return suc, err
end


--  -------- Themes

local defaultTheme = {
	["address-bar-text"] = "white",
	["address-bar-background"] = "gray",
	["address-bar-base"] = "lightGray",
	["top-box"] = "red",
	["bottom-box"] = "orange",
	["text-color"] = "white",
	["background"] = "gray"
} local originalTheme = {
	["address-bar-text"] = "white",
	["address-bar-background"] = "black",
	["address-bar-base"] = "black",
	["top-box"] = "black",
	["bottom-box"] = "black",
	["text-color"] = "white",
	["background"] = "black"
}

local function loadTheme(path)
	if fs.exists(path) and not fs.isDir(path) then
		local a = {}
		local f = io.open(path, "r")
		local l = f:read("*l")
		while l do
			l = l:gsub("^%s*(.-)%s*$", "%1")
			if l and l ~= "" and l ~= "\n" and l:sub(1, 2) ~= "--" then
				local k, v = string.match(l, "^(%a+)=(%a+)")
				if k and v then a[k] = v
				else return nil end
			end
			l = f:read("*l")
		end
		f:close()
		return a
	end
	return nil
end


--  -------- Filesystem

local function download(url, path)
	for i = 1, 3 do
		local response = http.get(url)
		if response then
			local data = response.readAll()
			response.close()
			if path then
				local f = io.open(path, "w")
				f:write(data)
				f:close()
			end
			return true
		end
	end

	return false
end

local function updateClient()
	local skipNormal = false
	if serverID ~= "experimental" then
		http.request(buildURL)
		local a = os.startTimer(10)
		while true do
			local e, url, handle = os.pullEvent()
			if e == "http_success" then
				local b = handle.readAll():gsub("^%s*(.-)%s*$", "%1")
				if not tonumber(b) or tonumber(b) > build then break
				else return false end
			elseif e == "http_failure" or (e == "timer" and url == a) then
				skipNormal = true
				break
			end
		end
	end

	local source = nil
	if not skipNormal then
		local _, y = term.getCursorPos()
		term.setCursorPos(1, y - 2)
		rightWrite(string.rep(" ", 32))
		rightWrite("Updating Firewolf... ")

		http.request(firewolfURL)
		local a = os.startTimer(10)
		while true do
			local e, url, handle = os.pullEvent()
			if e == "http_success" then
				source = handle
				break
			elseif e == "http_failure" or (e == "timer" and url == a) then
				break
			end
		end
	end

	if not source then
		if isAdvanced() then
			term.setTextColor(colors[theme["text-color"]])
			term.setBackgroundColor(colors[theme["background"]])
			term.clear()
			if not fs.exists(rootFolder) then fs.makeDir(rootFolder) end
			local f = io.open(rootFolder .. "/temp_file", "w")
			f:write(graphics.githubImage)
			f:close()
			local a = paintutils.loadImage(rootFolder .. "/temp_file")
			paintutils.drawImage(a, 5, 5)
			sleep(0.2)
			fs.delete(rootFolder .. "/temp_file")

			term.setCursorPos(19, 4)
			term.setBackgroundColor(colors[theme["top-box"]])
			write(string.rep(" ", 32))
			term.setCursorPos(19, 5)
			write("  Could Not Connect to GitHub!  ")
			term.setCursorPos(19, 6)
			write(string.rep(" ", 32))
			term.setBackgroundColor(colors[theme["bottom-box"]])
			term.setCursorPos(19, 8)
			write(string.rep(" ", 32))
			term.setCursorPos(19, 9)
			write("    Sorry, Firewolf could not   ")
			term.setCursorPos(19, 10)
			write(" connect to GitHub to download  ")
			term.setCursorPos(19, 11)
			write(" necessary files. Please check: ")
			term.setCursorPos(19, 12)
			write("    http://status.github.com    ")
			term.setCursorPos(19, 13)
			write(string.rep(" ", 32))
			term.setCursorPos(19, 14)
			write("        Click to exit...        ")
			term.setCursorPos(19, 15)
			write(string.rep(" ", 32))
		else
			term.clear()
			term.setCursorPos(1, 1)
			term.setBackgroundColor(colors.black)
			term.setTextColor(colors.white)
			print("\n")
			centerPrint("Could not connect to GitHub!")
			print("")
			centerPrint("Sorry, Firewolf could not connect to")
			centerPrint("GitHub to download necessary files.")
			centerPrint("Please check:")
			centerPrint("http://status.github.com")
			print("")
			centerPrint("Press any key to exit...")
		end

		while true do
			local e = os.pullEvent()
			if e == "mouse_click" or e == "key" then break end
		end

		return false
	elseif source and autoupdate == "true" then
		local b = io.open(firewolfLocation, "r")
		local new = source.readAll()
		local cur = b:read("*a")
		source.close()
		b:close()

		if cur ~= new then
			fs.delete(firewolfLocation)
			local f = io.open(firewolfLocation, "w")
			f:write(new)
			f:close()
			return true
		else
			return false
		end
	end
end

local function resetFilesystem()
	-- Migrate
	fs.delete(rootFolder .. "/available_themes")
	fs.delete(rootFolder .. "/default_theme")

	-- Reset
	if not fs.exists(rootFolder) then fs.makeDir(rootFolder)
	elseif not fs.isDir(rootFolder) then fs.move(rootFolder, "/Firewolf_Data.old") end

	for _, v in pairs({serverFolder, cacheFolder, websiteDataFolder}) do
		if not fs.isDir(v) then fs.delete(v) end
		if not fs.exists(v) then fs.makeDir(v) end
	end

	if fs.isDir(settingsLocation) then fs.delete(settingsLocation) end
	if fs.isDir(serverLocation) then fs.delete(serverLocation) end

	if not fs.exists(settingsLocation) then
		local f = io.open(settingsLocation, "w")
		f:write(textutils.serialize({auto = "true", incog = "false", home = "firewolf"}))
		f:close()
	end

	if not fs.exists(serverLocation) then download(serverURL, serverLocation) end
	fs.delete(rootFolder .. "/temp_file")

	for _, v in pairs({userWhitelist, userBlacklist}) do
		if fs.isDir(v) then fs.delete(v) end
		if not fs.exists(v) then
			local f = io.open(v, "w")
			f:write("")
			f:close()
		end
	end
end

local function checkForModem(display)
	while true do
		local present = false
		for _, v in pairs(rs.getSides()) do
			if peripheral.getType(v) == "modem" then rednet.open(v) present = true break end
		end

		if not present and type(display) == "function" then display() os.pullEvent("peripheral")
		else return true end
	end
end


--  -------- Databases

local function loadDatabases()
	if fs.exists(userBlacklist) and not fs.isDir(userBlacklist) then
		local bf = io.open(userBlacklist, "r")
		local l = bf:read("*l")
		while l do
			if l and l ~= "" and l ~= "\n" then
				l = l:gsub("^%s*(.-)%s*$", "%1")
				table.insert(blacklist, l)
			end
			l = bf:read("*l")
		end
		bf:close()
	end

	if fs.exists(userWhitelist) and not fs.isDir(userWhitelist) then
		local wf = io.open(userWhitelist, "r")
		local l = wf:read("*l")
		while l do
			if l and l ~= "" and l ~= "\n" then
				l = l:gsub("^%s*(.-)%s*$", "%1")
				local a, b = l:find("| |")
				table.insert(whitelist, {l:sub(1, a - 1), l:sub(b + 1, -1)})
			end
			l = wf:read("*l")
		end
		wf:close()
	end
end

local function verifyBlacklist(id)
	for _, v in pairs(blacklist) do if tostring(id) == v then return true end end
	return false
end

local function verifyWhitelist(id, url)
	for _, v in pairs(whitelist) do
		if v[2] == tostring(id) and v[1] == tostring(url) then return true end
	end
	return false
end


--  -------- Graphics and Files

graphics.githubImage = [[
f       f
fffffffff
fffffffff
f4244424f
f4444444f
fffffefffe
   fffe e
 fffff e
ff f fe e
     e   e
]]

graphics.nomodem = function()
	term.setTextColor(colors[theme["text-color"]])
	term.setBackgroundColor(colors[theme["background"]])
	term.clear()
	term.setCursorPos(1, 1)
	print("\n")
	term.setBackgroundColor(colors[theme["top-box"]])
	api.leftPrint(string.rep(" ", 24))
	api.leftPrint(" No Modem Attached! D:  ")
	api.leftPrint(string.rep(" ", 24))
	print("\n")

	term.setBackgroundColor(colors[theme["bottom-box"]])
	api.rightPrint(string.rep(" ", 40))
	api.rightPrint("    No wireless modem was found on this ")
	api.rightPrint("  computer, and Firewolf cannot use the ")
	api.rightPrint("             RDNT protocol without one! ")
	api.rightPrint(string.rep(" ", 40))
	api.rightPrint("  Waiting for a modem to be attached... ")
	api.rightPrint(string.rep(" ", 40))
end

graphics.nonexistantwebpage = function()
	term.setBackgroundColor(colors[theme["background"]])
	term.clear()
	term.setCursorPos(1, 2)
	term.setTextColor(colors[theme["text-color"]])
	term.setBackgroundColor(colors[theme["top-box"]])
	leftPrint(string.rep(" ", 11))
	leftPrint(" Oh Noes!  ")
	leftPrint(string.rep(" ", 11))
	print("\n")

	term.setBackgroundColor(colors[theme["bottom-box"]])
	rightPrint(string.rep(" ", 43))
	rightPrint([[       ______                          __  ]])
	rightPrint([[      / ____/_____ _____ ____   _____ / /  ]])
	rightPrint([[     / __/  / ___// ___// __ \ / ___// /   ]])
	rightPrint([[    / /___ / /   / /   / /_/ // /   /_/    ]])
	rightPrint([[   /_____//_/   /_/    \____//_/   (_)     ]])
	rightPrint(string.rep(" ", 43))
	rightPrint("  Could not connect to the website! It may ")
	rightPrint("  be down, or not exist!                   ")
	rightPrint(string.rep(" ", 43))
end

graphics.nosearchresults = function()
	term.setBackgroundColor(colors[theme["background"]])
	term.clear()
	term.setCursorPos(1, 5)
	term.setTextColor(colors[theme["text-color"]])
	term.setBackgroundColor(colors[theme["top-box"]])
	centerPrint(string.rep(" ", 40))
	centerPrint("  No Websites are Currently Online! D:  ")
	centerPrint(string.rep(" ", 40))
	centerPrint("       Why not make one yourself?       ")
	centerPrint("          Visit rdnt://server!          ")
	centerPrint(string.rep(" ", 40))
end

files.availableThemes = [[
https://raw.github.com/1lann/firewolf/master/themes/default.txt| |Fire (default)
https://raw.github.com/1lann/firewolf/master/themes/ice.txt| |Ice
https://raw.github.com/1lann/firewolf/master/themes/carbon.txt| |Carbon
https://raw.github.com/1lann/firewolf/master/themes/christmas.txt| |Christmas
https://raw.github.com/1lann/firewolf/master/themes/original.txt| |Original
https://raw.github.com/1lann/firewolf/master/themes/ocean.txt| |Ocean
https://raw.github.com/1lann/firewolf/master/themes/forest.txt| |Forest
https://raw.github.com/1lann/firewolf/master/themes/pinky.txt| |Pinky
https://raw.github.com/1lann/firewolf/master/themes/azhftech.txt| |AzhfTech
]]

files.newTheme = [[
-- Address bar text color
address-bar-text=

-- Address bar background color
address-bar-background=

-- Color of separator bar on live search dropdown
address-bar-base=

-- Top box background color
top-box=

-- Bottom box background color
bottom-box=

-- Main background color
background=

-- Main text color
text-color=

]]


--  -------- Protocols

protocols.rdnt = {}
protocols.http = {}

protocols.rdnt.getSearchResults = function()
	dnsDatabase = {[1] = {}, [2] = {}}
	local resultIDs = {}
	local conflict = {}

	rednet.broadcast("firewolf.broadcast.dns.list")
	local startClock = os.clock()
	while os.clock() - startClock < timeout do
		local id, i = rednet.receive(timeout)
		if id then
			if i:sub(1, 14) == "firewolf-site:" then
				i = i:sub(15, -1)
				local bl, wl = verifyBlacklist(id), verifyWhitelist(id, i)
				if not i:find(" ") and i:len() < 40 and (not bl or (bl and wl)) then
					if not resultIDs[tostring(id)] then resultIDs[tostring(id)] = 1
					else resultIDs[tostring(id)] = resultIDs[tostring(id)] + 1 end
					
					if not i:find("rdnt://") then i = ("rdnt://" .. i) end
					local x = false
					if conflict[i] then
						x = true
						table.insert(conflict[i], id)
					else
						for m, n in pairs(dnsDatabase[1]) do
							if n:lower() == i:lower() then
								x = true
								table.remove(dnsDatabase[1], m)
								table.remove(dnsDatabase[2], m)
								if conflict[i] then table.insert(conflict[i], id)
								else conflict[i] = {} table.insert(conflict[i], id) end
								break
							end
						end
					end

					if not x and resultIDs[tostring(id)] <= 3 then
						table.insert(dnsDatabase[1], i)
						table.insert(dnsDatabase[2], id)
					end
				end
			end
		else break end
	end

	for k, v in pairs(conflict) do
		table.sort(v)
		table.insert(dnsDatabase[1], k)
		table.insert(dnsDatabase[2], v[1])
	end
end

protocols.rdnt.getWebsite = function(site)
	local id, content, status = nil, nil, nil
	local clock = os.clock()
	local websiteID = nil
	for k, v in pairs(dnsDatabase[1]) do
		local web = site:gsub("rdnt://", "")
		if web:find("/") then web = web:sub(1, web:find("/") - 1) end
		if web == v:gsub("rdnt://", "") then websiteID = dnsDatabase[2][k] break end
	end
	if not websiteID then return nil, nil, nil end

	sleep(timeout)
	rednet.send(websiteID, site)
	clock = os.clock()
	while os.clock() - clock < timeout do
		id, content = rednet.receive(timeout)
		if id and id == websiteID then
			local bl, wl = verifyBlacklist(id), verifyWhitelist(id, site)
			status = nil
			if (bl and not wl) or site == "" or site == "." or site == ".." then
				-- Ignore
			elseif wl then status = "whitelist" break
			else status = "safe" break end
		end
	end

	return id, content, status
end

protocols.http.getSearchResults = function()
	dnsDatabase = {[1] = {}, [2] = {}}
end

protocols.http.getWebsite = function(site)
	return nil, nil, nil
end


--  -------- Homepage

pages["firewolf"] = function()
	term.setBackgroundColor(colors[theme["background"]])
	term.clear()
	term.setTextColor(colors[theme["text-color"]])
	term.setBackgroundColor(colors[theme["top-box"]])
	print("")
	leftPrint(string.rep(" ", 42))
	leftPrint([[        _,-='"-.__               /\_/\    ]])
	leftPrint([[         -.}        =._,.-==-._.,  @ @._, ]])
	leftPrint([[            -.__  __,-.   )       _,.-'   ]])
	leftPrint([[ Firewolf ]] .. version .. string.rep(" ", 8 - version:len()) ..
		[["    G..m-"^m m'        ]])
	leftPrint(string.rep(" ", 42))
	print("\n")

	term.setBackgroundColor(colors[theme["bottom-box"]])
	rightPrint(string.rep(" ", 42))
	if isAdvanced() then rightPrint("  News:                       [- Sites -] ")
	else rightPrint("  News:                                   ") end
	rightPrint("     Firewolf 2.5 is out! It rewrites the ")
	rightPrint("    whole Firewolf's internals (again :P) ")
	rightPrint(string.rep(" ", 42))
	rightPrint("   Firewolf 3.0 will be out soon! It will ")
	rightPrint("     bring the long awaited HTTP support! ")
	rightPrint(string.rep(" ", 42))

	while true do
		local e, but, x, y = os.pullEvent()
		if e == "mouse_click" and x >= 40 and x <= 50 and y == 11 then redirect("sites") end
	end
end

pages["sites"] = function()
	term.setBackgroundColor(colors[theme["background"]])
	term.clear()
	term.setTextColor(colors[theme["text-color"]])
	term.setBackgroundColor(colors[theme["top-box"]])
	print("\n")
	leftPrint(string.rep(" ", 17))
	leftPrint(" Built-In Sites  ")
	leftPrint(string.rep(" ", 17))

	local sx = 8
	term.setBackgroundColor(colors[theme["bottom-box"]])
	term.setCursorPos(1, sx)
	rightPrint(string.rep(" ", 40))
	rightPrint("  rdnt://firewolf              Homepage ")
	rightPrint("  rdnt://sites                    Sites ")
	rightPrint("  rdnt://server       Server Management ")
	rightPrint("  rdnt://help                 Help Page ")
	rightPrint("  rdnt://settings              Settings ")
	rightPrint("  rdnt://credits                Credits ")
	rightPrint("  rdnt://exit                      Exit ")
	rightPrint(string.rep(" ", 40))

	local a = {"firewolf", "sites", "server", "help", "settings", "credits", "exit"}
	while true do
		local e, but, x, y = os.pullEvent()
		if e == "mouse_click" and x >= 14 and x <= 50 then
			for i, v in ipairs(a) do if y == sx + i and v ~= "exit" then redirect(v) end end
		end
	end
end


--  -------- Server Management

local function manageServers(site, protocol, functionList, startServerName)
	local servers = functionList["reload servers"]()
	local sy = 7

	if not startServerName then startServerName = "Start" end
	if isAdvanced() then
		local function draw(l, sel)
			term.setBackgroundColor(colors[theme["bottom-box"]])
			term.setCursorPos(4, sy)
			write("[- New Server -]")
			for i, v in ipairs(l) do
				term.setCursorPos(3, i + sy)
				write(string.rep(" ", 24))
				term.setCursorPos(4, i + sy)
				local nv = v
				if nv:len() > 18 then nv = nv:sub(1, 15) .. "..." end
				if i == sel then write("[ " .. nv .. " ]")
				else write("  " .. nv) end
			end

			if #l < 1 then
				term.setCursorPos(4, sy + 2)
				write("A website is literally")
				term.setCursorPos(4, sy + 3)
				write("just a lua script!")
				term.setCursorPos(4, sy + 4)
				write("Go ahead and make one!")
				term.setCursorPos(4, sy + 6)
				write("Also, be sure to check")
				term.setCursorPos(4, sy + 7)
				write("out Firewolf's APIs to")
				term.setCursorPos(4, sy + 8)
				write("help you make your")
				term.setCursorPos(4, sy + 9)
				write("site, at rdnt://help")
			end

			term.setCursorPos(30, sy)
			write(string.rep(" ", 19))
			term.setCursorPos(30, sy)
			if l[sel] then
				local nl = l[sel]
				if nl:len() > 19 then nl = nl:sub(1, 16) .. "..." end
				write(nl)
			else write("No Server Selected!") end
			term.setCursorPos(30, sy + 2)
			write("[- " .. startServerName .. " -]")
			term.setCursorPos(30, sy + 4)
			write("[- Edit -]")
			term.setCursorPos(30, sy + 6)
			if functionList["run on boot"] then write("[- Run on Boot -]") end
			term.setCursorPos(30, sy + 8)
			write("[- Delete -]")
		end

		local function updateDisplayList(items, loc, len)
			local ret = {}
			for i = 1, len do
				if items[i + loc - 1] then table.insert(ret, items[i + loc - 1]) end
			end
			return ret
		end

		while true do
			term.setBackgroundColor(colors[theme["background"]])
			term.clear()
			term.setCursorPos(1, 1)
			term.setTextColor(colors[theme["text-color"]])
			term.setBackgroundColor(colors[theme["top-box"]])
			print("")
			leftPrint(string.rep(" ", 27))
			leftPrint(" Server Management - " .. protocol:upper() .. "  ")
			leftPrint(string.rep(" ", 27))
			print("")

			term.setBackgroundColor(colors[theme["bottom-box"]])
			for i = 1, 12 do
				term.setCursorPos(3, i + sy - 2)
				write(string.rep(" ", 24))
				term.setCursorPos(29, i + sy - 2)
				write(string.rep(" ", 21))
			end

			local sel, loc, len = 1, 1, 10
			local disList = updateDisplayList(servers, loc, len)
			draw(disList, sel)

			while true do
				local e, but, x, y = os.pullEvent()
				if e == "key" and but == 200 and #servers > 0 and loc > 1 then
					loc = loc - 1
					disList = updateDisplayList(servers, loc, len)
					draw(disList, sel)
				elseif e == "key" and but == 208 and #servers > 0 and loc + len - 1 < #servers then
					loc = loc + 1
					disList = updateDisplayList(servers, loc, len)
					draw(disList, sel)
				elseif e == "mouse_click" then
					if x >= 4 and x <= 25 then
						if y == 7 then
							functionList["new server"]()
							servers = functionList["reload servers"]()
							break
						elseif #servers > 0 then
							for i, v in ipairs(disList) do
								if y == i + 7 then
									sel = i
									draw(disList, sel)
								end
							end
						end
					elseif x >= 30 and x <= 40 and y == 9 and #servers > 0 then
						functionList["start"](disList[sel])
						servers = functionList["reload servers"]()
						break
					elseif x >= 30 and x <= 39 and y == 11 and #servers > 0 then
						functionList["edit"](disList[sel])
						servers = functionList["reload servers"]()
						break
					elseif x >= 30 and x <= 46 and y == 13 and #servers > 0 and
							functionList["run on boot"] then
						functionList["run on boot"](disList[sel])
						term.setBackgroundColor(colors[theme["bottom-box"]])
						term.setCursorPos(32, 15)
						write("Will Run on Boot!")
						openAddressBar = false
						sleep(1.3)
						openAddressBar = true
						term.setCursorPos(32, 15)
						write(string.rep(" ", 18))
						break
					elseif x >= 30 and x <= 41 and y == 15 and #servers > 0 then
						functionList["delete"](disList[sel])
						servers = functionList["reload servers"]()
						break
					end
				end
			end
		end
	else
		while true do
			term.setBackgroundColor(colors[theme["background"]])
			term.clear()
			term.setCursorPos(1, 1)
			term.setTextColor(colors[theme["text-color"]])
			term.setBackgroundColor(colors[theme["top-box"]])
			print("")
			centerPrint(string.rep(" ", 27))
			centerPrint(" Server Management - " .. protocol:upper() .. "  ")
			centerPrint(string.rep(" ", 27))
			print("")

			local a = {"New Server"}
			for _, v in pairs(servers) do table.insert(a, v) end
			local server = scrollingPrompt(a, 4, 7, 10)
			if server == "New Server" then
				functionList["new server"]()
				servers = functionList["reload servers"]()
			else
				term.setCursorPos(30, 8)
				write(server)
				local a = {{"Start", 30, 9}, {"Edit", 30, 11}, {"Run on Boot", 30, 12},
					{"Delete", 30, 13}, {"Back", 30, 15}}
				if not functionList["run on boot"] then
					a = {{"Start", 30, 9}, {"Edit", 30, 11}, {"Delete", 30, 13}, {"Back", 30, 15}}
				end
				local opt = prompt(a, "vertical")
				if opt == "Start" then
					functionList["start"](server)
					servers = functionList["reload servers"]()
				elseif opt == "Edit" then
					functionList["edit"](server)
					servers = functionList["reload servers"](server)
				elseif opt == "Run on Boot" and functionList["run on boot"] then
					functionList["run on boot"](server)
					term.setCursorPos(32, 16)
					write("Will Run on Boot!")
					openAddressBar = false
					sleep(1.3)
					openAddressBar = true
				elseif opt == "Delete" then
					functionList["delete"](server)
					servers = functionList["reload servers"]()
				end
			end
		end
	end
end

local function editPages(dir)
	local oldLoc = shell.dir()
	local commandHis = {}
	term.setBackgroundColor(colors.black)
	term.setTextColor(colors.white)
	term.clear()
	term.setCursorPos(1, 1)
	print("")
	print(" Server Shell Editing")
	print(" Type 'exit' to return to Firewolf.")
	print(" The 'home' file is the index of your site.")
	print("")

	local allowed = {"move", "mv", "cp", "copy", "drive", "delete", "rm", "edit",
		"eject", "exit", "help", "id", "monitor", "rename", "alias", "clear",
		"paint", "lua", "redstone", "rs", "redprobe", "redpulse", "programs",
		"redset", "reboot", "hello", "label", "list", "ls", "easter", "pastebin", "dir"}
	
	while true do
		shell.setDir(dir)
		term.setBackgroundColor(colors.black)
		if isAdvanced() then term.setTextColor(colors.yellow)
		else term.setTextColor(colors.white) end
		write("> ")
		term.setTextColor(colors.white)
		local line = read(nil, commandHis)
		table.insert(commandHis, line)

		local words = {}
		for m in string.gmatch(line, "[^ \t]+") do
			local a = m:gsub("^%s*(.-)%s*$", "%1")
			table.insert(words, a)
		end

		local com = words[1]
		if com == "exit" then break
		elseif com then
			local a = false
			for _, v in pairs(allowed) do if com == v then a = true break end end

			if a then
				term.setBackgroundColor(colors.black)
				term.setTextColor(colors.white)
				shell.run(com, unpack(words, 2))
			else
				term.setTextColor(colors.red)
				print("Program Not Allowed!")
			end
		end
	end
	shell.setDir(oldLoc)
end

local function newServer(onCreate)
	term.setBackgroundColor(colors[theme["background"]])
	for i = 1, 12 do
		term.setCursorPos(3, i + 5)
		term.clearLine()
	end

	term.setBackgroundColor(colors[theme["bottom-box"]])
	term.setCursorPos(1, 7)
	for i = 1, 8 do centerPrint(string.rep(" ", 47)) end
	term.setCursorPos(5, 8)
	write("Name: ")
	local name = modRead({refusePrint = "`", visibleLength = w - 4, textLength = 200})
	term.setCursorPos(5, 10)
	write("URL:")
	term.setCursorPos(8, 11)
	write("rdnt://")
	local url = modRead({grantPrint = "abcdefghijklmnopqrstuvwxyz1234567890-_.+",
		visibleLength = w - 4, textLength = 200})
	url = url:gsub(" ", "")
	if name == "" or url == "" then
		term.setCursorPos(5, 13)
		write("URL or Name is Empty!")
		openAddressBar = false
		sleep(1.3)
		openAddressBar = true
	else
		local c = onCreate(name, url)

		term.setCursorPos(5, 13)
		if c and c == "true" then write("Successfully Created Server!")
		elseif c == "false" or not c then write("Server Creation Failed!")
		else write(c) end
		openAddressBar = false
		sleep(1.3)
		openAddressBar = true
	end
end

pages["server/rdnt"] = function(site)
	manageServers(site, "rdnt", {["reload servers"] = function()
		local servers = {}
		for _, v in pairs(fs.list(serverFolder)) do
			if fs.isDir(serverFolder .. "/" .. v) then table.insert(servers, v) end
		end
		return servers
	end, ["new server"] = function()
		newServer(function(name, url)
			if fs.exists(serverFolder .. "/" .. url) then return "Server Already Exists!" end
			fs.makeDir(serverFolder .. "/" .. url)
			local f = io.open(serverFolder .. "/" .. url .. "/home", "w")
			f:write("print(\"\")\ncenterPrint(\"Welcome To " .. name .. "!\")\n\n")
			f:close()
			return "true"
		end)
	end, ["start"] = function(server)
		term.clear()
		term.setCursorPos(1, 1)
		term.setBackgroundColor(colors.black)
		term.setTextColor(colors.white)
		openAddressBar, offsetclick = false, false
		setfenv(1, oldEnv)
		shell.run(serverLocation, server, serverFolder .. "/" .. server)
		setfenv(1, override)
		openAddressBar, offsetclick = true, true
		checkForModem()
	end, ["edit"] = function(server)
		openAddressBar, offsetclick = false, false
		editPages(serverFolder .. "/" .. server)
		openAddressBar, offsetclick = true, true
		if not fs.exists(serverFolder .. "/" .. server .. "/home") then
			local f = io.open(serverFolder .. "/" .. server .. "/home", "w")
			f:write("print(\"\")\ncenterPrint(\"Welcome To " .. server .. "!\")\n\n")
			f:close()
		end
	end, ["run on boot"] = function(server)
		fs.delete("/old-startup")
		if fs.exists("/startup") then fs.move("/startup", "/old-startup") end
		local f = io.open("/startup", "w")
		f:write("shell.run(\"" .. serverLocation .. "\", \"" .. server .. "\", \"" ..
			serverFolder .. "/" .. server .. "\")")
		f:close()
	end, ["delete"] = function(server)
		fs.delete(serverFolder .. "/" .. server)
	end})
end

pages["server/http"] = function()
	term.setTextColor(colors[theme["text-color"]])
	term.setBackgroundColor(colors[theme["background"]])
	term.clear()
	print("\n\n")
	term.setBackgroundColor(colors[theme["top-box"]])
	centerPrint(string.rep(" ", 17))
	centerPrint("  Comming Soon!  ")
	centerPrint(string.rep(" ", 17))
end

pages["server"] = function()
	setfenv(manageServers, override)
	setfenv(newServer, override)
	setfenv(editPages, env)
	if curProtocol == protocols.rdnt then redirect("server/rdnt")
	elseif curProtocol == protocols.http then redirect("server/http") end
end


--  -------- Help

pages["help"] = function()
	term.setBackgroundColor(colors[theme["background"]])
	term.clear()
	term.setTextColor(colors[theme["text-color"]])
	term.setBackgroundColor(colors[theme["top-box"]])
	print("")
	leftPrint(string.rep(" ", 16))
	leftPrint(" Firewolf Help  ")
	leftPrint(string.rep(" ", 16))
	print("\n")

	term.setBackgroundColor(colors[theme["bottom-box"]])
	for i = 1, 11 do rightPrint(string.rep(" ", 41)) end
	term.setCursorPos(1, 15)
	rightPrint("         View the full documentation at: ")
	rightPrint("  https://github.com/1lann/Firewolf/wiki ")

	local opt = prompt({{"Getting Started", w - 21, 8}, {"Making a Theme", w - 20, 10},
		{"API Documentation", w - 23, 12}}, "vertical")
	local pages = {}
	if opt == "Getting Started" then
		pages[1] = {title = "Getting Started - Intoduction", content = {
			"Hey there!",
			"",
			"Firewolf is an app that allows you to create",
			"and visit websites! Each site has an address",
			"(the URL) which you can type into the address",
			"bar above, and then visit the site.",
			"",
			"You can open the address bar by clicking on",
			"it, or by pressing control."
		}} pages[2] = {title = "Getting Started - Searching", content = {
			"The address bar can be also be used to",
			"search for sites, by simply typing in the",
			"search term.",
			"",
			"To view all sites, just open it and hit",
			"enter (leave the field blank)."
		}} pages[3] = {title = "Getting Started - Built-In Websites", content = {
			"Firewolf has a set of built-in websites",
			"available for use:",
			"",
			"rdnt://firewolf   Normal hompage",
			"rdnt://sites      Built-In Site",
			"rdnt://server     Create websites",
			"rdnt://help       Help and documentation"
		}} pages[4] = {title = "Getting Started - Built-In Websites", content = {
			"More built-in websites:",
			"",
			"rdnt://settings   Firewolf settings",
			"rdnt://credits    View the credits",
			"rdnt://exit       Exit the app"
		}}
	elseif opt == "Making a Theme" then
		pages[1] = {title = "Making a Theme - Introduction", content = {
			"Firewolf themes are files that tell Firewolf",
			"to color which things what.",
			"Several themes can already be downloaded for",
			"Firewolf from rdnt://settings/themes.",
			"",
			"You can also make your own theme, use it in",
			"your copy of Firewolf.Your theme can also be",
			"submitted it to the themes list!"
		}} pages[2] = {title = "Making a Theme - Example", content = {
			"A theme file consists of several lines of",
			"text. Here is the default theme file:",
			"address-bar-text=white",
			"address-bar-background=gray",
			"address-bar-base=lightGray",
			"top-box=red",
			"bottom-box=orange",
			"background=gray",
			"text-color=white"
		}} pages[3] = {title = "Making a Theme - Explanation", content = {
			"On each line of the example, something is",
			"given a color, like on the last line, the",
			"text of the page is told to be white.",
			"",
			"The color specified after the = is the same",
			"as when you call colors.[color name].",
			"For example, specifying 'red' after the =",
			"colors that object red."
		}} pages[4] = {title = "Making a Theme - Have a Go", content = {
			"Themes can be made at rdnt://settings/themes,",
			"click on 'Change Theme' button, and click on",
			"'New Theme'.",
			"",
			"Enter a theme name, then exit Firewolf and",
			"edit the newly created file",
			"Specify the colors for each of the keys,",
			"and return to the themes section of the",
			"downloads center. Click 'Load Theme'."
		}} pages[5] = {title = "Making a Theme - Submitting", content = {
			"To submit a theme to the theme list,",
			"send GravityScore a message on the CCForums",
			"that contains your theme file and name.",
			"",
			"He will message you back saying whether your",
			"theme has been added, or if anything needs to",
			"be changed before it is added."
		}}
	elseif opt == "API Documentation" then
		pages[1] = {title = "API Documentation - 1", content = {
			"The Firewolf API is a bunch of global",
			"functions that aim to simplify your life when",
			"designing and coding websites.",
			"",
			"For a full documentation on these functions,",
			"visit the Firewolf Wiki Page here:",
			"https://github.com/1lann/Firewolf/wiki"
		}} pages[2] = {title = "API Documentation - 2", content = {
			"centerPrint(string text)",
			"cPrint(string text)",
			"centerWrite(string text)",
			"cWrite(string text)",
			"",
			"leftPrint(string text)",
			"lPrint(string text)",
		}} pages[3] = {title = "API Documentation - 3", content = {
			"leftWrite(string text)",
			"lWrite(string text)",
			"",
			"rightPrint(string text)",
			"rPrint(string text)",
			"rightWrite(string text)",
			"rWrite(string text)"
		}} pages[4] = {title = "API Documentation - 4", content = {
			"prompt(table list, string direction)",
			"scrollingPrompt(table list, integer x,",
			"   integer y, integer length[,",
			"   integer width])",
			"",
			"urlDownload(string url)",
			"pastebinDownload(string code)",
			"redirect(string site)",
		}} pages[5] = {title = "API Documentation - 5", content = {
			"loadImageFromServer(string imagePath)",
			"ioReadFileFromServer(string filePath)",
			"fileFileFromServer(string filePath)",
			"saveFileToUserComputer(string content)",
			"",
			"writeDataFile(string path, string contents)",
			"readDataFile(string path)"
		}} pages[6] = {title = "API Documentation - 6", content = {
			"themeColor(string tag)",
			"themeColour(string tag)"
		}}
	end

	local function drawPage(page)
		term.setBackgroundColor(colors[theme["background"]])
		term.clear()
		term.setCursorPos(1, 1)
		term.setTextColor(colors[theme["text-color"]])
		term.setBackgroundColor(colors[theme["top-box"]])
		print("")
		leftPrint(string.rep(" ", page.title:len() + 3))
		leftPrint(" " .. page.title .. "  ")
		leftPrint(string.rep(" ", page.title:len() + 3))
		print("")

		term.setBackgroundColor(colors[theme["bottom-box"]])
		for i = 1, 12 do print(string.rep(" ", w)) end
		for i, v in ipairs(page.content) do
			term.setCursorPos(4, i + 6)
			write(v)
		end
	end

	local curPage = 1
	local a = {{"Prev", 26, 17}, {"Next", 38, 17}, {"Back",  14, 17}}
	drawPage(pages[curPage])
	while true do
		local b = {a[3]}
		if curPage == 1 then table.insert(b, a[2])
		elseif curPage == #pages then table.insert(b, a[1])
		else table.insert(b, a[1]) table.insert(b, a[2]) end

		local opt = prompt(b, "horizontal")
		if opt == "Prev" then curPage = curPage - 1
		elseif opt == "Next" then curPage = curPage + 1
		elseif opt == "Back" then break end
		drawPage(pages[curPage])
	end

	redirect("help")
end


--  -------- Settings

pages["settings/themes"] = function()
	term.setBackgroundColor(colors[theme["background"]])
	term.clear()
	term.setTextColor(colors[theme["text-color"]])
	term.setBackgroundColor(colors[theme["top-box"]])
	print("")
	leftPrint(string.rep(" ", 17))
	leftPrint(" Theme Settings  ")
	leftPrint(string.rep(" ", 17))
	print("")

	if isAdvanced() then
		term.setBackgroundColor(colors[theme["bottom-box"]])
		for i = 1, 12 do rightPrint(string.rep(" ", 36)) end

		local themes = {}
		local themenames = {"Back", "New Theme", "Load Theme"}
		local f = io.open(rootFolder .. "/temp_file", "w")
		f:write(files.availableThemes)
		f:close()
		local f = io.open(rootFolder .. "/temp_file", "r")
		local l = f:read("*l")
		while l do
			l = l:gsub("^%s*(.-)%s*$", "%1")
			local a, b = l:find("| |")
			table.insert(themenames, l:sub(b + 1, -1))
			table.insert(themes, {name = l:sub(b + 1, -1), url = l:sub(1, a - 1)})
			l = f:read("*l")
		end
		f:close()
		fs.delete(rootFolder .. "/temp_file")

		local t = scrollingPrompt(themenames, w - 33, 7, 10, 32)
		if t == "Back" then redirect("settings")
		elseif t == "New Theme" then
			term.setCursorPos(w - 33, 17)
			write("Path: /")
			local n = modRead({visibleLength = w - 2, textLength = 100})
			if n ~= "" and n then
				n = "/" .. n
				local f = io.open(n, "w")
				f:write(files.newTheme)
				f:close()

				term.setCursorPos(1, 17)
				rightWrite(string.rep(" ", 36))
				term.setCursorPos(1, 17)
				rightWrite("File Created! ")
				openAddressBar = false
				sleep(1.1)
				openAddressBar = true
				redirect("settings/themes")
			end
		elseif t == "Load Theme" then
			term.setCursorPos(w - 33, 17)
			write("Path: /")
			local n = modRead({visibleLength = w - 2, textLength = 100})
			if n ~= "" and n then
				n = "/" .. n
				term.setCursorPos(1, 17)
				rightWrite(string.rep(" ", 36))
				
				term.setCursorPos(1, 17)
				if fs.exists(n) and not fs.isDir(n) then
					local a = loadTheme(n)
					if a then
						fs.delete(themeLocation)
						fs.copy(n, themeLocation)
						theme = a
						rightWrite("Theme File Loaded! :D ")
					else rightWrite("Theme File is Corrupt! D: ") end
				elseif not fs.exists(n) then rightWrite("File does not exist! ")
				elseif fs.isDir(n) then rightWrite("File is a directory! ") end

				openAddressBar = false
				sleep(1.1)
				openAddressBar = true
				redirect("settings/themes")
			end
		else
			local url = ""
			for _, v in pairs(themes) do if v.name == t then url = v.url break end end
			term.setBackgroundColor(colors[theme["top-box"]])
			term.setCursorPos(1, 3)
			leftWrite(string.rep(" ", 17))
			leftWrite(" Downloading...  ")

			fs.delete(rootFolder .. "/temp_file")
			download(url, rootFolder .. "/temp_file")
			local th = loadTheme(rootFolder .. "/temp_file")
			if not th then
				term.setCursorPos(1, 3)
				leftWrite(string.rep(" ", 17))
				leftWrite(" Theme Corrupt!  ")
				openAddressBar = false
				sleep(1.3)
				openAddressBar = true

				fs.delete(rootFolder .. "/temp_file")
				redirect("settings/themes")
			else
				term.setCursorPos(1, 3)
				leftWrite(string.rep(" ", 17))
				leftWrite(" Theme Loaded!   ")
				openAddressBar = false
				sleep(1.3)
				openAddressBar = true

				fs.delete(themeLocation)
				fs.move(rootFolder .. "/temp_file", themeLocation)
				theme = th
				redirect("home")
			end
		end
	else
		print("")
		rightPrint(string.rep(" ", 30))
		rightPrint("  Themes are not available on ")
		rightPrint("         normal computers! :( ")
		rightPrint(string.rep(" ", 30))
	end
end

pages["settings"] = function()
	term.setBackgroundColor(colors[theme["background"]])
	term.clear()
	term.setTextColor(colors[theme["text-color"]])
	term.setBackgroundColor(colors[theme["top-box"]])
	print("")
	leftPrint(string.rep(" ", 17 + serverList[serverID]:len()))
	leftPrint(" Firewolf Settings  " .. string.rep(" ", serverList[serverID]:len() - 3))
	leftPrint(" Designed For: " .. serverList[serverID] .. "  ")
	leftPrint(string.rep(" ", 17 + serverList[serverID]:len()))
	print("\n")

	local a = "Automatic Updating - On"
	if autoupdate == "false" then a = "Automatic Updating - Off" end
	local b = "Home - rdnt://" .. homepage
	if b:len() >= 28 then b = b:sub(1, 24) .. "..." end

	term.setBackgroundColor(colors[theme["bottom-box"]])
	for i = 1, 9 do rightPrint(string.rep(" ", 36)) end
	local c = {{a, w - a:len() - 6, 9}, {"Change Theme", w - 18, 11}, {b, w - b:len() - 6, 13},
		{"Reset Firewolf", w - 20, 15}}
	if not isAdvanced() then
		c = {{a, w - a:len(), 9}, {b, w - b:len(), 11}, {"Reset Firewolf", w - 14, 13}}
	end

	local opt = prompt(c, "vertical")
	if opt == a then
		if autoupdate == "true" then autoupdate = "false"
		elseif autoupdate == "false" then autoupdate = "true" end
	elseif opt == "Change Theme" and isAdvanced() then
		redirect("settings/themes")
	elseif opt == b then
		if isAdvanced() then term.setCursorPos(w - 30, 14)
		else term.setCursorPos(w - 30, 12) end
		write("rdnt://")
		local a = read()
		if a ~= "" then homepage = a end
	elseif opt == "Reset Firewolf" then
		term.setBackgroundColor(colors[theme["background"]])
		term.clear()
		term.setCursorPos(1, 1)
		term.setTextColor(colors[theme["text-color"]])
		term.setBackgroundColor(colors[theme["top-box"]])
		print("")
		leftPrint(string.rep(" ", 17))
		leftPrint(" Reset Firewolf  ")
		leftPrint(string.rep(" ", 17))
		print("\n")
		term.setBackgroundColor(colors[theme["bottom-box"]])
		for i = 1, 11 do rightPrint(string.rep(" ", 26)) end
		local opt = prompt({{"Reset History", w - 19, 8}, {"Reset Servers", w - 19, 9},
			{"Reset Theme", w - 17, 10}, {"Reset Cache", w - 17, 11}, 
			{"Reset Databases", w - 21, 12},
			{"Reset Settings", w - 20, 13}, {"Back", w - 10, 14}, {"Reset All", w - 15, 16}},
			"vertical")

		openAddressBar = false
		if opt == "Reset All" then
			fs.delete(rootFolder)
		elseif opt == "Reset History" then
			fs.delete(historyLocation)
		elseif opt == "Reset Servers" then
			fs.delete(serverFolder)
			fs.delete(serverLocation)
		elseif opt == "Reset Cache" then
			fs.delete(cacheFolder)
		elseif opt == "Reset Databases" then
			fs.delete(userWhitelist)
			fs.delete(userBlacklist)
		elseif opt == "Reset Settings" then
			fs.delete(settingsLocation)
		elseif opt == "Reset Theme" then
			fs.delete(themeLocation)
		elseif opt == "Back" then
			openAddressBar = true
			redirect("settings")
		end

		term.setBackgroundColor(colors[theme["background"]])
		term.clear()
		term.setCursorPos(1, 1)
		term.setBackgroundColor(colors[theme["top-box"]])
		print("\n\n")
		leftPrint(string.rep(" ", 17))
		leftPrint(" Reset Firewolf  ")
		leftPrint(string.rep(" ", 17))
		print("")

		term.setCursorPos(1, 10)
		term.setBackgroundColor(colors[theme["bottom-box"]])
		rightPrint(string.rep(" ", 27))
		rightPrint("  Firewolf has been reset! ")
		rightWrite(string.rep(" ", 27))
		if isAdvanced() then rightPrint("          Click to exit... ")
		else rightPrint("  Press any key to exit... ") end
		rightPrint(string.rep(" ", 27))

		while true do
			local e = os.pullEvent()
			if e == "mouse_click" or e == "key" then return true end
		end
	end

	-- Save
	local f = io.open(settingsLocation, "w")
	f:write(textutils.serialize({auto = autoupdate, incog = incognito, home = homepage}))
	f:close()

	redirect("settings")
end


--  -------- Credits

pages["credits"] = function()
	term.setTextColor(colors[theme["text-color"]])
	term.setBackgroundColor(colors[theme["background"]])
	term.clear()
	print("\n")
	term.setBackgroundColor(colors[theme["top-box"]])
	leftPrint(string.rep(" ", 19))
	leftPrint(" Firewolf Credits  ")
	leftPrint(string.rep(" ", 19))
	print("\n")

	term.setBackgroundColor(colors[theme["bottom-box"]])
	rightPrint(string.rep(" ", 38))
	rightPrint("  Coded by:              GravityScore ")
	rightPrint("                            and 1lann ")
	rightPrint(string.rep(" ", 38))
	rightPrint("  Based off:     RednetExplorer 2.4.1 ")
	rightPrint("           Made by ComputerCraftFan11 ")
	rightPrint(string.rep(" ", 38))
end


--  -------- Error Pages

errorPages["overspeed"] = function()
	clear("overspeed", colors[theme["background"]])
	print("")
	term.setTextColor(colors[theme["text-color"]])
	term.setBackgroundColor(colors[theme["top-box"]])
	leftPrint(string.rep(" ", 14))
	leftPrint(" Warning! D:  ")
	leftPrint(string.rep(" ", 14))
	print("")

	term.setBackgroundColor(colors[theme["bottom-box"]])
	rightPrint(string.rep(" ", 40))
	rightPrint("  Website browsing sleep limit reached! ")
	rightPrint(string.rep(" ", 40))
	rightPrint("      To prevent Firewolf from spamming ")
	rightPrint("   rednet, Firewolf has stopped loading ")
	rightPrint("                              the page. ")
	for i = 1, 3 do rightPrint(string.rep(" ", 40)) end

	openAddressBar = false
	for i = 1, 5 do
		term.setCursorPos(1, 14)
		rightWrite(string.rep(" ", 43))
		if 6 - i == 1 then rightWrite("                Please wait 1 second... ")
		else rightWrite("                Please wait " .. tostring(6 - i) .. " seconds... ") end
		sleep(1)
	end
	openAddressBar = true

	term.setCursorPos(1, 14)
	rightWrite(string.rep(" ", 43))
	rightWrite("            You may now browse normally ")
end

errorPages["crash"] = function(error)
	clear("crash", colors[theme["background"]])
	print("")
	term.setTextColor(colors[theme["text-color"]])
	term.setBackgroundColor(colors[theme["top-box"]])
	leftPrint(string.rep(" ", 30))
	leftPrint(" The Website Has Crashed! D:  ")
	leftPrint(string.rep(" ", 30))
	print("")

	term.setBackgroundColor(colors[theme["bottom-box"]])
	rightPrint(string.rep(" ", 31))
	rightPrint("      The website has crashed! ")
	rightPrint("  Report this to the operator: ")
	rightPrint(string.rep(" ", 31))
	term.setBackgroundColor(colors[theme["background"]])
	print("")
	print(" " .. tostring(error))
	print("")

	term.setBackgroundColor(colors[theme["bottom-box"]])
	rightPrint(string.rep(" ", 31))
	rightPrint("   You may now browse normally ")
	rightPrint(string.rep(" ", 31))
end


--  -------- External

local function validateexternal(site)
	-- Modem
	if not modemPresent() then return "modem" end

	-- Website
	local id, content, status = curProtocol.getWebsite(site)
	if id and status then return "exists", id, content, status end

	-- Cache
	local cacheLoc = cacheFolder .. "/" .. site:gsub("/", "$slazh$")
	if fs.exists(cacheLoc) and not fs.isDir(cacheLoc) then return "cache" end

	-- Search results
	local res = {}
	if site ~= "" then
		for _, v in pairs(dnsDatabase[1]) do
			if v:find(site:lower(), 1, true) then table.insert(res, v) end
		end
	else for _, v in pairs(dnsDatabase[1]) do table.insert(res, v) end end
	table.sort(res)
	table.sort(res, function(a, b)
		local _, ac = a:gsub("rdnt://", ""):gsub("http://", ""):gsub(site:lower(), "")
		local _, bc = b:gsub("rdnt://", ""):gsub("http://", ""):gsub(site:lower(), "")
		return ac > bc
	end)

	if #res > 0 then return "search", res
	elseif site == "" then return "search", res end

	-- Doesn't exist
	return "false"
end

local function loadexternal(site)
	local a, res, content, status = validateexternal(site)
	local func = nil

	if a == "exists" then
		local cacheLoc = cacheFolder .. "/" .. site:gsub("/", "$slazh$")
		local f = io.open(cacheLoc, "w")
		f:write(content)
		f:close()

		local fn, err = loadfile(cacheLoc)
		if not err then
			setfenv(fn, antivirus)
			if status == "whitelist" then setfenv(fn, override) end

			func = function()
				local _, err = pcall(fn)
				if err then
					websiteerror = err
					os.queueEvent(event_error)
				end
			end
		else
			func = function()
				local errf = errorPages["crash"]
				setfenv(errf, override)
				pcall(function() errf(err) end)
			end
		end
	elseif a == "modem" then
		func = function() checkForModem(graphics.nomodem) end
	elseif a == "search" then
		if #res > 0 then
			func = function()
				term.setBackgroundColor(colors[theme["background"]])
				term.clear()
				term.setCursorPos(1, 2)
				term.setTextColor(colors[theme["text-color"]])
				term.setBackgroundColor(colors[theme["top-box"]])
				local t = "1 Search Result"
				if #res > 1 then t = #res .. " Search Results" end
				leftPrint(string.rep(" ", t:len() + 3))
				leftPrint(" " .. t .. "  ")
				leftPrint(string.rep(" ", t:len() + 3))
				print("")

				term.setBackgroundColor(colors[theme["bottom-box"]])
				for i = 1, 12 do rightPrint(string.rep(" ", 42)) end
				local opt = scrollingPrompt(res, w - 39, 7, 10, 38)
				if opt then redirect(opt:gsub("rdnt://", ""):gsub("http://", "")) end
			end
		else
			func = graphics.nosearchresults
		end
	elseif a == "false" or a == "cache" then
		func = graphics.nonexistantwebpage
	end

	term.setBackgroundColor(colors.black)
	term.setTextColor(colors.white)
	term.clear()
	term.setCursorPos(1, 1)

	local a = override
	a["browserAgent"] = browserAgent
	setfenv(func, a)
	curtext, curbackground = colors.white, colors.black
	tabs[currentTab] = coroutine.create(func)
end


--  -------- Website

local function loadinternal(site)
	if pages[site] then
		local func = pages[site]
		setfenv(func, override)
		tabs[currentTab] = coroutine.create(function()
			local _, err = pcall(function() func() end)
			if err then
				websiteerror = err
				os.queueEvent(event_error)
			end
		end)

		return true
	else return false end
end

local function loadsite(site)
	w, h = term.getSize()
	clear(site)
	term.setBackgroundColor(colors.black)
	term.setTextColor(colors.white)
	fs.delete(websiteDataFolder)
	fs.makeDir(websiteDataFolder)

	if site ~= "exit" and site ~= addressBarHistory[#addressBarHistory] then
		table.insert(addressBarHistory, site)
	end

	checkForModem()
	if os.clock() - loadingClock > 5 then loadingRate, loadingClock = 0, os.clock() end
	if loadingRate >= 8 then
		local overspeedFunc = errorPages["overspeed"]
		setfenv(overspeedFunc, override)
		overspeedFunc()
		loadingRate, loadingClock = 0, os.clock()
	end

	-- Load
	os.queueEvent(event_load)
	website = site
	setfenv(loadinternal, override)
	setfenv(loadexternal, override)
	if not loadinternal(site) then loadexternal(site) end
	if tabs[currentTab] then _, filters[currentTab] = coroutine.resume(tabs[currentTab]) end
end


--  -------- Address Bar

local function addressbarread()
	local len, list = 4, {}

	local function draw(l)
		local ox, oy = term.getCursorPos()
		for i = 1, len do
			term.setTextColor(colors[theme["address-bar-text"]])
			term.setBackgroundColor(colors[theme["address-bar-background"]])
			term.setCursorPos(1, i + 1)
			write(string.rep(" ", w))
		end

		term.setBackgroundColor(colors[theme["address-bar-base"] or theme["bottom-box"]])
		term.setCursorPos(1, len + 2)
		write(string.rep(" ", w))
		term.setBackgroundColor(colors[theme["address-bar-background"]])
		for i, v in ipairs(l) do term.setCursorPos(2, i + 1) write(v) end
		term.setCursorPos(ox, oy)
	end

	local function update(line, event, ...)
		local params = {...}
		local y = params[3]
		if event == "char" or event == "history" or event == "delete" then
			list = {}
			for _, v in pairs(dnsDatabase[1]) do
				if #list < len and
						v:gsub("rdnt://", ""):gsub("http://", ""):find(line:lower(), 1, true) then
					table.insert(list, v)
				end
			end

			table.sort(list)
			table.sort(list, function(a, b)
				local _, ac = a:gsub("rdnt://", ""):gsub("http://", ""):gsub(line:lower(), "")
				local _, bc = b:gsub("rdnt://", ""):gsub("http://", ""):gsub(line:lower(), "")
				return ac > bc
			end)
			draw(list)
			return false
		elseif event == "mouse_click" then
			for i = 1, #list do
				if y == i + 1 then return true, list[i]:gsub("rdnt://", ""):gsub("http://", "") end
			end
		end
	end

	if isAdvanced() and modemPresent() then
		return modRead({history = addressBarHistory, visibleLength = w - 2, textLength = 300, 
			liveUpdates = update, exitOnKey = "control"})
	else
		return modRead({history = addressBarHistory, visibleLength = w - 2, textLength = 300,
			exitOnKey = "control"})
	end
end


--  -------- Main

local function searchresults()
	if modemPresent() then curProtocol.getSearchResults() end
	local lastCheck = os.clock()
	while true do
		local e = os.pullEvent()
		if e == event_load and modemPresent() and os.clock() - lastCheck > 5 then
			curProtocol.getSearchResults()
			lastCheck = os.clock()
		end
	end
end

local function run()
	loadingClock = os.clock()
	loadsite(homepage)
	while true do
		local e, but, x, y, p4, p5 = os.pullEvent()
		if ((e == "mouse_click" and y == 1 and clickableAddressBar) or
				(e == "key" and (but == 29 or but == 157))) and openAddressBar then
			if e == "key" then x = -1 end
			if not menuBarOpen and x == w then
				-- Open menu bar
				menuBarOpen = true
				term.setBackgroundColor(colors[theme["top-box"]])
				term.setTextColor(colors[theme["address-bar-text"]])
				term.setCursorPos(1, 1)
				write("> [- Exit Firewolf -]                              ")
			elseif menuBarOpen and (x == 1 or (but == 29 or but == 157)) then
				-- Close menu bar
				menuBarOpen = false
				clear(website, nil, true)
			elseif menuBarOpen and x > 2 and x < 22 then
				return
			elseif not menuBarOpen then
				-- Stop
				if tabs[currentTab] then tabs[currentTab] = nil end

				-- Clear
				term.setBackgroundColor(colors[theme["address-bar-background"]])
				term.setTextColor(colors[theme["address-bar-text"]])
				term.setCursorPos(2, 1)
				term.clearLine()
				if curProtocol == protocols.rdnt then write("rdnt://")
				elseif curProtocol == protocols.http then write("http://") end

				-- Read
				local osite = website
				local site = addressbarread()
				if not site then site = osite
				elseif site == "home" or site == "homepage" then site = homepage
				elseif site == "exit" then return end

				-- Load
				loadsite(site)
			end
		elseif e == event_error and websiteerror then
			-- Display
			if tabs[currentTab] then tabs[currentTab] = nil end

			local errf = errorPages["crash"]
			setfenv(errf, override)
			errf(websiteerror)
			websiteerror = nil
		elseif e == event_redirect and openAddressBar then
			-- Stop
			if tabs[currentTab] then tabs[currentTab] = nil end

			-- Load
			if not but or but == "exit" then but = website
			elseif but == "home" or but == "homepage" then but = homepage end
			loadsite(but)
		elseif tabs[currentTab] then
			if coroutine.status(tabs[currentTab]) == "suspended" then
				if e == "mouse_click" and offsetclick then y = y - 1 end
				if (filters[currentTab] or e) == e then
					_, filters[currentTab] = coroutine.resume(tabs[currentTab], e, but, x, y, 
						p4, p5)
				end
			end

			for i = 1, #tabs do
				if tabs[i] and coroutine.status(tabs[i]) == "dead" then
					tabs[i] = nil
				end
			end
		end
	end
end


--  -------- Startup

local function main()
	-- Logo
	term.setBackgroundColor(colors[theme["background"]])
	term.setTextColor(colors[theme["text-color"]])
	term.clear()
	term.setCursorPos(1, 2)
	term.setBackgroundColor(colors[theme["top-box"]])
	leftPrint(string.rep(" ", 47))
	leftPrint([[          ______ ____ ____   ______            ]])
	leftPrint([[ ------- / ____//  _// __ \ / ____/            ]])
	leftPrint([[ ------ / /_    / / / /_/ // __/               ]])
	leftPrint([[ ----- / __/  _/ / / _  _// /___               ]])
	leftPrint([[ ---- / /    /___//_/ |_|/_____/               ]])
	leftPrint([[ --- / /       _       __ ____   __     ______ ]])
	leftPrint([[ -- /_/       | |     / // __ \ / /    / ____/ ]])
	leftPrint([[              | | /| / // / / // /    / /_     ]])
	leftPrint([[              | |/ |/ // /_/ // /___ / __/     ]])
	leftPrint([[              |__/|__/ \____//_____//_/        ]])
	leftPrint(string.rep(" ", 47))
	print("\n")
	term.setBackgroundColor(colors[theme["bottom-box"]])

	-- Load Settings
	if fs.exists(settingsLocation) and not fs.isDir(settingsLocation) then
		local f = io.open(settingsLocation, "r")
		local a = textutils.unserialize(f:read("*l"))
		if type(a) == "table" then autoupdate, incognito, homepage = a.auto, a.incog, a.home end
		f:close()
	else autoupdate, incognito, homepage = "true", "false", "firewolf" end
	curProtocol = protocols.rdnt

	-- Update
	rightPrint(string.rep(" ", 32))
	rightPrint("        Checking for Updates... ")
	rightPrint(string.rep(" ", 32))
	setfenv(updateClient, env)
	if not noInternet then
		if updateClient() then
			if debugFile then debugFile:close() end
			shell.run(firewolfLocation)
			error()
		end
	end

	-- Download Files
	local x, y = term.getCursorPos()
	term.setCursorPos(1, y - 2)
	rightWrite(string.rep(" ", 32))
	rightWrite("  Downloading Required Files... ")
	if not noInternet then resetFilesystem() end
	loadDatabases()
	checkForModem()

	-- Run
	setfenv(run, env)
	parallel.waitForAny(run, searchresults)
end

local function startup()
	-- HTTP
	if not http and not noInternet then
		term.setTextColor(colors[theme["text-color"]])
		term.setBackgroundColor(colors[theme["background"]])
		term.clear()
		term.setCursorPos(1, 2)
		term.setBackgroundColor(colors[theme["top-box"]])
		api.leftPrint(string.rep(" ", 24))
		api.leftPrint(" HTTP API Not Enabled!  ")
		api.leftPrint(string.rep(" ", 24))
		print("")

		term.setBackgroundColor(colors[theme["bottom-box"]])
		api.rightPrint(string.rep(" ", 36))
		api.rightPrint("  Firewolf is unable to run without ")
		api.rightPrint("       the HTTP API Enabled! Please ")
		api.rightPrint("    enable it in your ComputerCraft ")
		api.rightPrint("                            Config! ")
		api.rightPrint(string.rep(" ", 36))

		if isAdvanced() then api.rightPrint("                   Click to exit... ")
		else api.rightPrint("           Press any key to exit... ") end
		api.rightPrint(string.rep(" ", 36))

		while true do
			local e, but, x, y = os.pullEvent()
			if e == "mouse_click" or e == "key" then break end
		end

		return false
	end

	-- Turtle
	if turtle then
		term.clear()
		term.setCursorPos(1, 2)
		api.centerPrint("Advanced computer Required!")
		print("\n")
		api.centerPrint("  This version of Firewolf requires  ")
		api.centerPrint("  an Advanced computer to run!       ")
		print("")
		api.centerPrint("  Turtles may not be used to run     ")
		api.centerPrint("  Firewolf! :(                       ")
		print("")
		api.centerPrint("Press any key to exit...")

		os.pullEvent("key")
		return false
	end

	-- Run
	setfenv(main, env)
	local _, err = pcall(main)
	if err and not err:lower():find("terminated") then
		term.setTextColor(colors[theme["text-color"]])
		term.setBackgroundColor(colors[theme["background"]])
		term.clear()
		term.setCursorPos(1, 2)
		term.setCursorBlink(false)
		term.setBackgroundColor(colors[theme["top-box"]])
		api.leftPrint(string.rep(" ", 27))
		api.leftPrint(" Firewolf has Crashed! D:  ")
		api.leftPrint(string.rep(" ", 27))
		print("")
		term.setBackgroundColor(colors[theme["background"]])
		print("")
		print("  " .. err)
		print("")

		term.setBackgroundColor(colors[theme["bottom-box"]])
		api.rightPrint(string.rep(" ", 41))
		if autoupdate == "true" then
			api.rightPrint("    Please report this error to 1lann or ")
			api.rightPrint("  GravityScore so we are able to fix it! ")
			api.rightPrint("  If this problem persists, try deleting ")
			api.rightPrint("                         " .. rootFolder .. " ")
		else
			api.rightPrint("        Automatic updating is off! A new ")
			api.rightPrint("     version may have have been released ")
			api.rightPrint("                that may fix this error! ")
			api.rightPrint("        If you didn't turn auto updating ")
			api.rightPrint("             off, delete " .. rootFolder .. " ")
		end

		api.rightPrint(string.rep(" ", 41))
		if isAdvanced() then api.rightPrint("                        Click to exit... ")
		else api.rightPrint("                Press any key to exit... ") end
		api.rightPrint(string.rep(" ", 41))

		while true do
			local e, but, x, y = os.pullEvent()
			if e == "mouse_click" or e == "key" then break end
		end

		return false
	end
end

-- Check If Read Only
if fs.isReadOnly(firewolfLocation) or fs.isReadOnly(rootFolder) then
	print("Firewolf cannot modify itself or its root folder!")
	print("")
	print("This cold be caused by Firewolf being placed in")
	print("the rom folder, or another program may be")
	print("preventing the modification of Firewolf.")
	error()
end

-- Theme
if not isAdvanced() then theme = originalTheme
else theme = loadTheme(themeLocation) or defaultTheme end

-- Debug File
if #tArgs > 0 and tArgs[1] == "debug" then
	print("Debug Mode Enabled")
	if fs.exists(debugLogLocation) then debugFile = io.open(debugLogLocation, "a")
	else debugFile = io.open(debugLogLocation, "w") end
	debugFile:write("\n-- [" .. textutils.formatTime(os.time()) .. "] New Log --")
	sleep(1)
end

-- Start
startup()

-- Exit Message
term.setBackgroundColor(colors.black)
term.setTextColor(colors.white)
term.setCursorBlink(false)
term.clear()
term.setCursorPos(1, 1)
api.centerPrint("Thank You for Using Firewolf " .. version)
api.centerPrint("Made by 1lann and GravityScore")

-- Close
for _, v in pairs(rs.getSides()) do 
	if peripheral.getType(v) == "modem" then rednet.close(v) end
end
if debugFile then debugFile:close() end
