blob: eceef56b0a23fd6f13d53de34d08c1137cb59df6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
-- cgit email-filter that puts a Libravatar icon next to an author name. Enable
-- it with the email-filter or repo.email-filter setting and the lua: prefix,
-- so it runs in cgit's embedded interpreter with no per-request process. The
-- companion email-gravatar.lua is the same filter pointed at Gravatar instead.
--
--     email-filter=lua:/path/to/email-libravatar.lua
--
-- Runs on Lua 5.1, 5.2, 5.3, 5.4 and LuaJIT. Lua 5.5 is not supported, because
-- luaossl has no 5.5 build.
--
-- The one dependency is luaossl, the OpenSSL binding that provides
-- openssl.digest, from <https://github.com/wahern/luaossl>. The reliable
-- cross-platform install is LuaRocks, matched to the Lua version cgit is built
-- against, and it needs the OpenSSL development headers present.
--
--     # Debian and Ubuntu
--     sudo apt install luarocks libssl-dev
--     sudo luarocks --lua-version 5.1 install luaossl
--
--     # Fedora
--     sudo dnf install luarocks openssl-devel
--     sudo luarocks --lua-version 5.1 install luaossl
--
--     # macOS with Homebrew
--     brew install luarocks openssl
--     luarocks install luaossl OPENSSL_DIR="$(brew --prefix openssl)"
--
-- Every page view sends the visitor's IP address and a hash of each
-- committer's email to a third-party service, so leave this filter off if that
-- is not acceptable for your instance. Addresses are hashed with MD5.

local digest = require("openssl.digest")


-- These are the values to change. The size is in pixels and serves both as the
-- image asked of the service and as the width and height attributes. The
-- default image is the style Libravatar draws for an address it has never
-- seen, and its documented choices include retro, identicon, monsterid and mm.
-- The endpoint is the secure CDN so the icon loads over https and is not
-- blocked as mixed content on an https page.
local avatar_size = 13
local default_image = "retro"
local base_url = "https://seccdn.libravatar.org/avatar/"
local alt_text = "Libravatar"


-- cgit calls filter_open once, then filter_write for each piece of the name,
-- then filter_close, so what one call works out has to be left here for the
-- next one.
local buffer = ""
local avatar_hash = nil

local function hash_hex(input)
	local raw = digest.new("md5"):final(input)
	local hex = ""
	for i = 1, #raw do
		hex = hex .. string.format("%.2x", string.byte(raw, i))
	end
	return hex
end

-- cgit can hand over the address still wrapped in angle brackets, and the
-- service hashes the trimmed lowercase form, so an address that skipped this
-- would hash to something the service has never heard of. A missing or empty
-- address becomes nil, which is how the caller knows to draw no icon.
local function normalize_email(email)
	if email == nil then
		return nil
	end
	local inner = email:match("<(.*)>")
	if inner ~= nil then
		email = inner
	end
	email = (email:gsub("^%s*(.-)%s*$", "%1")):lower()
	if email == "" then
		return nil
	end
	return email
end

function filter_open(email, page)
	buffer = ""
	local address = normalize_email(email)
	if address == nil then
		avatar_hash = nil
	else
		avatar_hash = hash_hex(address)
	end
end

function filter_write(text)
	buffer = buffer .. text
end

-- A log page can carry one icon per commit, so the attributes let the
-- browser defer the fetches for icons still off screen and keep decoding
-- off the critical path.
function filter_close()
	if avatar_hash == nil then
		html(buffer)
	else
		html("<img src='" .. base_url .. avatar_hash ..
			"?s=" .. avatar_size ..
			"&amp;d=" .. default_image ..
			"' width='" .. avatar_size ..
			"' height='" .. avatar_size ..
			"' loading='lazy' decoding='async'" ..
			" alt='" .. alt_text .. "'> " .. buffer)
	end
	return 0
end