This file is indexed.

/usr/share/monotone/hooks/monotone-mail-notify.lua is in monotone-extras 1.1-7.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
-- This script reads a file "notify" in the configuration directory
-- The format is like that of "read-permissions", but instead of key
-- names, the values for 'allow' and 'deny' entries must be real email
-- addresses.
-- Additionally, at the top before anything else, you may have the
-- following entries:
--	server		This must be the server address.  This entry
--			is needed for the .sh script.
--			Default value: "localhost" (see _server below)
--	from		The sender email address.
--			Default value: see _from below
--	keydir		The directory with keys.
--			Default value: get_confdir() .. "/keys"
--	key		A key identity.
--			Default value: (empty)
--	shellscript	The script that does the actual emailing.
--			If left empty, it means it's taken care of
--			throught other means.
--			Note: the script is spawned and then entirely
--			left to its own devices, so as not to let the
--			monotone server hang too long.
--			Default value: (empty)
--
-- This will splat out files in _base. Use the .sh script from
-- cron to process those files
--
-- Copyright (c) 2007, Matthew Sackman (matthew at wellquite dot org)
--                     LShift Ltd (http://www.lshift.net)
--                     Thomas Keller <me@thomaskeller.biz>
--                     Whoever wrote the function "get_netsync_read_permitted"
-- Copyright (c) 2010, Richard Levitte <richard@levitte.org>
-- Copyright (c) 2014, Markus Wanner <markus@bluegap.ch>
-- License: GPLv2 or later

do
   _from = "monotone@my.domain.please.change.me"
   _server = "localhost"
   _base = "/var/spool/monotone"
   _keydir = get_confdir() .. "/keys"
   _key = ""
   _shellscript = ""
   _shellscript_log = get_confdir() .. "/notify.log"
   _shellscript_errlog = get_confdir() .. "/notify.err"

   function write_error(msg)
      io.stderr:write("monotone-mail-notify.lua: " .. msg .. "\n")
   end

   local function table_print(T)
      local done = {}
      local function tprint_r(T, prefix)
	 for k,v in pairs(T) do
	    print(prefix..tostring(k),'=',tostring(v))
	    if type(v) == 'table' then
	       if not done[v] then
		  done[v] = true
		  tprint_r(v, prefix.."  ")
	       end
	    end
	 end
      end
      done[T] = true
      tprint_r(T, "")
   end

   local function table_toarray(t)
      local t1 = {}
      for j, val in pairs(t) do
	 table.insert(t1, val)
      end
      return t1
   end

   local function parse_configuration()
      local notifyfile = io.open(get_confdir() .. "/notify", "r")
      if (notifyfile == nil) then return nil end
      local dat = notifyfile:read("*a")
      io.close(notifyfile)
      local res = parse_basic_io(dat)
      if res == nil then
	 io.stderr:write("file notify cannot be parsed\n")
	 return nil
      end
      --print("monotone-mail-notify: BEGIN Parsed configuration")
      --table.print(res)
      --print("monotone-mail-notify: END Parsed configuration")
      return res
   end

   local function get_configuration(notifydata)
      local data = {}

      -- Set default data
      data["server"] = _server
      data["from"] = _from
      data["keydir"] = _keydir
      data["key"] = _key
      data["shellscript"] = _shellscript
      data["base"] = _base

      for i, item in pairs(notifydata)
      do
	 -- legal names: server, from, keydir, key, shellscript, comment
	 if item.name == "server" then
	    for j, val in pairs(item.values) do
	       data[item.name] = val
	    end
	 elseif item.name == "from" then
	    for j, val in pairs(item.values) do
	       data[item.name] = val
	    end
	 elseif item.name == "keydir" then
	    for j, val in pairs(item.values) do
	       data[item.name] = val
	    end
	 elseif item.name == "key" then
	    for j, val in pairs(item.values) do
	       data[item.name] = val
	    end
	 elseif item.name == "shellscript" then
	    for j, val in pairs(item.values) do
	       data[item.name] = val
	    end
	 elseif item.name == "base" then
	    for j, val in pairs(item.values) do
	       data[item.name] = val
	    end
	    -- Skip past other accepted words
	 elseif item.name == "pattern" then
	 elseif item.name == "allow" then
	 elseif item.name == "deny" then
	 elseif item.name == "continue" then
	 elseif item.name == "comment" then
	 else
	    io.stderr:write("unknown symbol in notify: " .. item.name .. "\n")
	 end
      end
      return data
   end

   local function get_notify_recipients(notifydata,branch)
      local results = {}
      local denied = {}
      local matches = false
      local cont = false

      for i, item in pairs(notifydata)
      do
	 -- Skip past other accepted words
	 if item.name == "server" then
	 elseif item.name == "from" then
	 elseif item.name == "keydir" then
	 elseif item.name == "key" then
	 elseif item.name == "shellscript" then
	 elseif item.name == "base" then
	    -- legal names: pattern, allow, deny, continue, comment
	 elseif item.name == "pattern" then
	    if matches and not cont then
	       return table_toarray(results)
	    end
	    matches = false
	    cont = false
	    for j, val in pairs(item.values) do
	       if globish_match(val, branch) then matches = true end
	    end
	 elseif item.name == "allow" then if matches then
	    for j, val in pairs(item.values) do
	       if nil == denied[val] then results[val] = val end
	    end
	 end elseif item.name == "deny" then if matches then
	    for j, val in pairs(item.values) do
	       denied[val] = val
	    end
	 end elseif item.name == "continue" then if matches then
	    cont = true
	    for j, val in pairs(item.values) do
	       if val == "false" or val == "no" then cont = false end
	    end
	 end elseif item.name ~= "comment" then
	    io.stderr:write("unknown symbol in notify: " .. item.name .. "\n")
	 end
      end
      return table_toarray(results)
   end

   local function summarize_certs(t)
      local str = "revision:            " .. t["revision"] .. "\n"
      local changelog
      -- Certificate keys to print, in order roughly matching rev_output.cc
      local cert_keys = {"author", "date", "branch", "tag", "suspend",
                         "testresult", "changelog", "comment"}
      for i, name in ipairs(cert_keys) do
	 local values = t["certs"][name]
	 if values ~= nil then
	    local formatted_value = ""
	    for j,val in pairs(values) do
	       formatted_value = formatted_value .. name .. ":"
	       if string.match(val, "\n")
	       then formatted_value = formatted_value .. "\n"
	       else formatted_value = formatted_value .. (string.rep(" ", 20 - (# name))) end
	       formatted_value = formatted_value .. val .. "\n"
	    end
	    if name == "changelog" then changelog = formatted_value else str = str .. formatted_value end
	 end
      end
      if nil ~= changelog then str = str .. changelog end
      return (str .. "manifest:\n" .. t["manifest"])
   end

   local function make_subject_line(t)
      local str = ""
      for j,val in pairs(t["certs"]["branch"]) do
	 str = str .. val
	 if j < # t["certs"]["branch"] then str = str .. ", " end
      end
      return str .. ": " .. t["revision"]
   end

   local _emails_to_send = {}
   local _notify_data = {}
   local _configuration_data = {}

   push_hook_functions(
      {
	 start =
	    function (session_id, my_role, sync_type,
		      remote_host, remote_keyname, includes, excludes)
	       _notify_data = parse_configuration()
	       _configuration_data = get_configuration(_notify_data)
	       _emails_to_send[session_id] = {}
	       return "continue",nil
	    end,

	 revision_received =
	    function (new_id, revision, certs, session_id)
	       if _emails_to_send[session_id] == nil then
		  -- no session present
		  return "continue",nil
	       end

	       local rev_data = {["certs"] = {},
				 ["revision"] = new_id,
				 ["manifest"] = revision}
	       for _,cert in ipairs(certs) do
		  if cert["name"] == "branch" then
		     rev_data["recipients"] =
			get_notify_recipients(_notify_data, cert["value"])
		  end
		  if cert["name"] ~= nil then
		     if nil == rev_data["certs"][cert["name"]] then
			rev_data["certs"][cert["name"]] = {}
		     end
		     table.insert(rev_data["certs"][cert["name"]],
				  cert["value"])
		  end
	       end
	       _emails_to_send[session_id][new_id] = rev_data
	       return "continue",nil
	    end,

	 ["end"] =
	    function (session_id, status,
		      bytes_in, bytes_out,
		      certs_in, certs_out,
		      revs_in, revs_out,
		      keys_in, keys_out, ...)
	       if _emails_to_send[session_id] == nil then
		  -- no session present
		  return "continue", nil
	       end

	       if status ~= 200 then
		  -- some error occured, no further processing takes place
		  return "continue", nil
	       end

	       if _emails_to_send[session_id] == "" then
		  -- we got no interesting revisions
		  return "continue", nil
	       end

	       local from = _configuration_data["from"]
	       local server = _configuration_data["server"]
	       local keydir = _configuration_data["keydir"]
	       local key = _configuration_data["key"]
	       local shellscript = _configuration_data["shellscript"]
	       local base = _configuration_data["base"]

	       --print("monotone-mail-notify: About to write data")
	       for rev_id,rev_data in pairs(_emails_to_send[session_id]) do
		  if # (rev_data["recipients"]) > 0 then
		     local subject = make_subject_line(rev_data)
		     local reply_to = ""
		     for j,auth in pairs(rev_data["certs"]["author"]) do
			reply_to = reply_to .. auth
			if j < # (rev_data["certs"]["author"]) then
			   reply_to = reply_to .. ", "
			end
		     end

		     local now = os.time()

		     -- print("monotone-mail-notify: Writing data for revision",
		     --       rev_data["revision"], "to files with base",
		     --       base .. "/" .. now .. "." .. rev_data["revision"])

		     local outputFileRev, rev_errmsg =
			io.open(base .. "/" .. now .. "." .. rev_data["revision"] .. ".rev.txt", "w+")
		     local outputFileHdr, hdr_errmsg =
			io.open(base .. "/" .. now .. "." .. rev_data["revision"] .. ".hdr.txt", "w+")
		     local outputFileDat, dat_errmsg =
			io.open(base .. "/" .. now .. "." .. rev_data["revision"] .. ".dat.txt", "w+")

		     if (outputFileRev == nil) then
			write_error(rev_errmsg)
		     end
		     if (outputFileHdr == nil) then
			write_error(hdr_errmsg)
		     end
		     if (outputFileDat == nil) then
			write_error(dat_errmsg)
		     end
		     if (outputFileRev == nil
			 or outputFileHdr == nil
			 or outputFileDat == nil) then
			return "continue",nil
		     end

		     local to = ""
		     for j,addr in pairs(rev_data["recipients"]) do
			to = to .. addr
			if j < # (rev_data["recipients"]) then
			   to = to .. ", "
			end
		     end

		     outputFileDat:write("server='" .. server .. "'\n")
		     outputFileDat:write("keydir='" .. keydir .. "'\n")
		     outputFileDat:write("key='" .. key .. "'\n")
		     outputFileDat:close()

		     outputFileHdr:write("To: " .. to .. "\n")
		     outputFileHdr:write("From: " .. from .. "\n")
		     outputFileHdr:write("Subject: " .. subject .. "\n")
		     outputFileHdr:write("Reply-To: " .. reply_to .. "\n")
		     outputFileHdr:close()

		     outputFileRev:write(summarize_certs(rev_data))
		     outputFileRev:close()
		  end
	       end

	       if shellscript and shellscript ~= "" then
		  print("monotone-mail-notify.lua: Running script:",
			shellscript, base)
		  spawn_redirected("/dev/null",
				   _shellscript_log,
				   _shellscript_errlog,
				   shellscript, base)
	       end
	       _emails_to_send[session_id] = nil
	       return "continue",nil
	    end
      })
end