From 3f02bd16e620ffb8e2082644c0416033bbe41f80 Mon Sep 17 00:00:00 2001
From: dg <dg@51575b47-30f0-44d4-a5cc-537603b46e54>
Date: Tue, 29 Nov 2011 00:01:23 +0000
Subject: [PATCH] Addons are now validated just as the main module

git-svn-id: http://svn.net-core.org/repos/t-engine4@4695 51575b47-30f0-44d4-a5cc-537603b46e54
---
 game/engines/default/engine/Module.lua        | 70 +++++++++++++++++--
 game/engines/default/engine/PlayerProfile.lua | 15 ++++
 game/modules/tome/class/Game.lua              |  2 +-
 game/modules/tome/data/damage_types.lua       |  2 +-
 game/profile-thread/Client.lua                | 12 +++-
 5 files changed, 94 insertions(+), 7 deletions(-)

diff --git a/game/engines/default/engine/Module.lua b/game/engines/default/engine/Module.lua
index 8a33888851..5a274d067b 100644
--- a/game/engines/default/engine/Module.lua
+++ b/game/engines/default/engine/Module.lua
@@ -287,14 +287,20 @@ function _M:loadAddons(mod)
 
 	table.sort(adds, function(a, b) return a.weight < b.weight end)
 
+	mod.addons = {}
 	for i, add in ipairs(adds) do
-		print("Binding addon", add.long_name, add.teaa)
-		local base
+		add.version_name = ("%s-%s-%d.%d.%d"):format(mod.short_name, add.short_name, add.version[1], add.version[2], add.version[3])
+
+		print("Binding addon", add.long_name, add.teaa, add.version_name)
+		local base, vbase
 		if add.teaa then
 			fs.mount(fs.getRealPath(add.teaa), "/loaded-addons/"..add.short_name, true)
 			base = "bind::/loaded-addons/"..add.short_name
+			vbase = "/loaded-addons/"..add.short_name
 		else
 			base = fs.getRealPath(add.dir)
+			fs.mount(base, "/loaded-addons/"..add.short_name, true)
+			vbase = "/loaded-addons/"..add.short_name
 		end
 
 		if add.data then fs.mount(base.."/data", "/data-"..add.short_name, true) print(" * with data") end
@@ -305,8 +311,46 @@ function _M:loadAddons(mod)
 			dofile("/hooks/"..add.short_name.."/load.lua")
 			print(" * with hooks")
 		end
+
+		-- Compute addon md5
+		local md5 = require "md5"
+		local md5s = {}
+		local function fp(dir)
+			for i, file in ipairs(fs.list(dir)) do
+				local f = dir.."/"..file
+				if fs.isdir(f) then
+					fp(f)
+				elseif f:find("%.lua$") then
+					local fff = fs.open(f, "r")
+					if fff then
+						local data = fff:read(10485760)
+						if data and data ~= "" then
+							md5s[#md5s+1] = f..":"..md5.sumhexa(data)
+						end
+						fff:close()
+					end
+				end
+			end
+		end
+		local hash_valid, hash_err
+		local t = core.game.getTime()
+		if config.settings.cheat then
+			hash_valid, hash_err = false, "cheat mode skipping addon validation"
+		else
+			fp(vbase)
+			table.sort(md5s)
+			table.print(md5s)
+			local fmd5 = md5.sumhexa(table.concat(md5s))
+			print("[MODULE LOADER] addon ", add.short_name, " MD5", fmd5, "computed in ", core.game.getTime() - t, vbase)
+			hash_valid, hash_err = profile:checkAddonHash(mod.short_name, add.version_name, fmd5)
+		end
+
+		if hash_err then hash_err = hash_err .. " [addon: "..add.short_name.."]" end
+		add.hash_valid, add.hash_err = hash_valid, hash_err
+
+		mod.addons[add.short_name] = add
 	end
-	table.print(fs.getSearchPath(true))
+--	os.exit()
 end
 
 --- Make a module loadscreen
@@ -469,10 +513,28 @@ function _M:instanciate(mod, name, new_game, no_reboot)
 
 	self:loadAddons(mod)
 
+	-- Check addons
+	if hash_valid then
+		for name, add in pairs(mod.addons) do
+			if not add.hash_valid then
+				hash_valid = false
+				hash_err = add.hash_err or "?????? unknown ...."
+				profile.hash_valid = false
+				break
+			end
+		end
+	end
+
+	local addl = {}
+	for name, add in pairs(mod.addons) do
+		addl[#addl+1] = add.version_name
+	end
+	mod.full_version_string = mod.version_string.." ["..table.concat(addl, ';').."]"
+
 	profile:addStatFields(unpack(mod.profile_stats_fields or {}))
 	profile:setConfigsBatch(true)
 	profile:loadModuleProfile(mod.short_name, mod)
-	profile:currentCharacter(mod.version_string, "game did not tell us")
+	profile:currentCharacter(mod.full_version_string, "game did not tell us")
 
 	-- Init the module code
 	local M, W = mod.load("init")
diff --git a/game/engines/default/engine/PlayerProfile.lua b/game/engines/default/engine/PlayerProfile.lua
index c095d67843..26ad34f6a1 100644
--- a/game/engines/default/engine/PlayerProfile.lua
+++ b/game/engines/default/engine/PlayerProfile.lua
@@ -580,6 +580,8 @@ function _M:syncOnline(module, mod_def)
 end
 
 function _M:checkModuleHash(module, md5)
+do self.hash_valid = true return true end
+
 	self.hash_valid = false
 	if not self.auth then return nil, "no online profile active" end
 	if config.settings.cheat then return nil, "cheat mode active" end
@@ -594,6 +596,19 @@ function _M:checkModuleHash(module, md5)
 	return true
 end
 
+function _M:checkAddonHash(module, addon, md5)
+	if not self.auth then return nil, "no online profile active" end
+	if config.settings.cheat then return nil, "cheat mode active" end
+	if game and game:isTainted() then return nil, "savefile tainted" end
+	core.profile.pushOrder(table.serialize{o="CheckAddonHash", module=module, addon=addon, md5=md5})
+
+	self:waitEvent("CheckAddonHash", function(e) ok = e.ok end, 10000)
+
+	if not ok then return nil, "bad game addon version" end
+	print("[ONLINE PROFILE] addon hash is valid")
+	return true
+end
+
 function _M:sendError(what, err)
 	print("[ONLINE PROFILE] sending error")
 	core.profile.pushOrder(table.serialize{o="SendError", login=self.login, what=what, err=err, module=game.__mod_info.short_name, version=game.__mod_info.version_name})
diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua
index 3355bf4023..822c7f36db 100644
--- a/game/modules/tome/class/Game.lua
+++ b/game/modules/tome/class/Game.lua
@@ -533,7 +533,7 @@ end
 function _M:updateCurrentChar()
 	if not self.party then return end
 	local player = self.party:findMember{main=true}
-	profile:currentCharacter(self.__mod_info.version_string, ("%s the level %d %s %s"):format(player.name, player.level, player.descriptor.subrace, player.descriptor.subclass), player.__te4_uuid)
+	profile:currentCharacter(self.__mod_info.full_version_string, ("%s the level %d %s %s"):format(player.name, player.level, player.descriptor.subrace, player.descriptor.subclass), player.__te4_uuid)
 end
 
 function _M:getSaveDescription()
diff --git a/game/modules/tome/data/damage_types.lua b/game/modules/tome/data/damage_types.lua
index 6068e14779..06283f6e75 100644
--- a/game/modules/tome/data/damage_types.lua
+++ b/game/modules/tome/data/damage_types.lua
@@ -85,7 +85,7 @@ setDefaultProjector(function(src, x, y, type, dam, tmp, no_martyr)
 			else
 				game:delayedLogDamage(src, {name="Iceblock", x=src.x, y=src.y}, dam, ("%s%d %s#LAST#"):format(DamageType:get(type).text_color or "#aaaaaa#", math.ceil(dam), DamageType:get(type).name))
 				if eff.begone and eff.begone < game.turn and eff.hp < 0 then
-					game.logSeen(src, "%s forces2 the iceblock to shatter.", src.name:capitalize())
+					game.logSeen(src, "%s forces the iceblock to shatter.", src.name:capitalize())
 					src:removeEffect(src.EFF_FROZEN)
 				end
 			end
diff --git a/game/profile-thread/Client.lua b/game/profile-thread/Client.lua
index 7be3e01048..a3f14c7194 100644
--- a/game/profile-thread/Client.lua
+++ b/game/profile-thread/Client.lua
@@ -204,7 +204,7 @@ end
 
 function _M:handleOrder(o)
 	o = o:unserialize()
-	if not self.sock and o.o ~= "Login" and o.o ~= "CurrentCharacter" and o.o ~= "CheckModuleHash" then return end -- Dont do stuff without a connection, unless we try to auth
+	if not self.sock and o.o ~= "Login" and o.o ~= "CurrentCharacter" and o.o ~= "CheckModuleHash" and o.o ~= "CheckAddonHash" then return end -- Dont do stuff without a connection, unless we try to auth
 	if self["order"..o.o] then self["order"..o.o](self, o) end
 end
 
@@ -317,6 +317,16 @@ function _M:orderCheckModuleHash(o)
 	end
 end
 
+function _M:orderCheckAddonHash(o)
+	if not self.sock then cprofile.pushEvent("e='CheckAddonHash' ok=false not_connected=true") end
+	self:command("AMD5", o.md5, o.module, o.addon)
+	if self:read("200") then
+		cprofile.pushEvent("e='CheckAddonHash' ok=true")
+	else
+		cprofile.pushEvent("e='CheckAddonHash' ok=false")
+	end
+end
+
 function _M:orderRegisterNewCharacter(o)
 	self:command("CHAR", "NEW", o.module)
 	if self:read("200") then
-- 
GitLab