summaryrefslogtreecommitdiffstats
path: root/cgi-bin/provisioning.cgi
blob: 0e7e5be28ce57dea6f433929460d7ee3bc224741 (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
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
#!/usr/bin/haserl --shell=lua --accept-none

<%
posix = require "posix"

local path_info = ENV["PATH_INFO"] or ENV["REQUEST_URI"] or ""
local root = ENV["DOCUMENT_ROOT"] or ""
local request_method = ENV["REQUEST_METHOD"] or ""
local user_agent = ENV["HTTP_USER_AGENT"] or ""
local ip_address = ENV["REMOTE_ADDR"] or ""
local server = ENV["SERVER_NAME"] or ""

local log = io.open("/var/log/provisioning.log", "a+")
log:write("Running provisioning.cgi ".. os.date() .. "\n")
log:write("Processing a "..request_method.." on "..path_info.."\n")
--	for a,b in pairs(ENV) do log:write(a.."=", b,"\n") end

local ACL_FILE = "/etc/provisioning/acl.conf"

function acl_check (...)
	local allowed = false
	local f = io.open(ACL_FILE)
	if f then
		for line in f:lines() do
			if string.match(user_agent, line) or string.match(ip_address, line) then
				log:write("ALLOW MATCH FOUND " .. line .. "\n")
				allowed = true
				break
			end
		end
		f:close()
	end
	if not allowed then
		log:write("ALLOW MATCH NOT FOUND. ACCESS DENIED\n")
	end
	return allowed
end

function http_code (code)
	log:write("HTTP code processed " .. code .. "\n")
	if code == 200 then
		io.stdout:write("Status: 200 OK\n")
		io.stdout:write("Content-Type: \n\n")
	elseif code == 204 then
		io.stdout:write("Status: 204 No Content\n")
		io.stdout:write("Content-Type: \n\n")
	elseif code == 302 then
		-- redirect to same file with empty mac
		io.stdout:write("Location: " .. string.gsub(path_info, "%x%x%x%x%x%x%x%x%x%x%x%x", "000000000000") .. "\n")
		io.stdout:write("Content-Type: \n\n")
		io.stdout:write("<a href=\"".. string.gsub(path_info, "%x%x%x%x%x%x%x%x%x%x%x%x", "000000000000") .."\">New Link</a>")	
	elseif code == 404 then
		io.stdout:write("Status: 404 Not Found\n")
		io.stdout:write("Content-Type: \n\n")
		io.stdout:write("Status: 404 Not Found\n")
		io.stdout:write("\n")
	elseif code == 403 then
		io.stdout:write("Status: 403 Forbidden\n")
		io.stdout:write("Content-Type: \n\n")
		io.stdout:write("Status: 403 Forbidden\n")
		io.stdout:write("\n")
	else
		io.stdout:write("Status: 400 Bad Request\n")
		io.stdout:write("Content-Type: \n\n")
		io.stdout:write("Status: 400 Bad Request\n")
		io.stdout:write("\n")
	end
end

--------------------------------------------------------------------------------------------------------------
-- Code Begins Here --
--------------------------------------------------------------------------------------------------------------
if not acl_check() then
	http_code(403)
	log:close()
	os.exit()
end

local basename = posix.basename(path_info)
local mac = string.match(basename, "%x%x%x%x%x%x%x%x%x%x%x%x")

if ( request_method == "GET" ) then
	-- Hack to allow download of config files with firmware URL, replacing $SERV with SERVER_NAME
	local firmwaretricks = {"Linksys", "Cisco", "snom"}
	for i,n in ipairs(firmwaretricks) do
		if string.match(user_agent, n) and posix.stat(root.."/"..n.."/"..basename) then
			local f = io.open(root.."/"..n.."/"..basename, "r")
			if f then
				log:write("Translating local file\n")
				io.stdout:write("Content-Type: \n\n")
				io.stdout:write((string.gsub(f:read("*a"), "%$SERV", server)))
				f:close()
				log:close()
				os.exit()
			end
		end
	end

	-- don't even bother for files that don't contain a MAC
	if mac == nil then 
		http_code(404)
		log:close()
		os.exit()
	end

        -- If it's a Polycom, 404 the MAC.cfg and MAC-license files and redirect the MAC-directory.xml, MAC-phone.cfg, and MAC-web.cfg files to blanks
	local f = string.match(basename, mac.."(.*)")
	if string.match(user_agent, "Polycom") and (f==".cfg" or f=="-license.cfg" or mac=="000000000000") then
		http_code(404)
		log:close()
		os.exit()
	elseif string.match(user_agent, "Polycom") and (f=="-directory.xml" or f=="-phone.cfg" or f=="-web.cfg") then
		http_code(302)
		log:close()
		os.exit()
	-- If it's a Grandstream, 404 the cfgMAC without extension
	elseif string.match(user_agent, "Grandstream") and (f=="") then
		http_code(404)
		log:close()
		os.exit()
	end

	log:write("Checking PROV Table for results\n")
	-- Load the ACF mvc
	mvc = require("acf.mvc")
	-- We'll change the view resolver to call the template
	APP=mvc:new()
	APP:read_config("acf")
	APP.view_resolver = function(self)
		return function (data)
			if not data.errtxt and data.value.values and data.value.values.value.device and data.value.values.value.device.template then
				local func = haserl.loadfile(data.value.values.value.device.template)
				func(data.value.values.value)
				log:write("Success\n")
			else
				http_code(404)
			end
		end
	end
	-- Set up the parameters
	local clientdata = {mac=mac, ip=ip_address, agent=user_agent}
	-- Dispatch the command
	APP:dispatch("/provisioning/", "provisioning", "getfile", clientdata)
	APP:destroy()
elseif ( request_method == "PUT" ) then
	local data = io.stdin:read("*all")
	local success = true

	-- Protect against writing to arbitrary paths
	if not mac or mac == "" or string.match(path_info, "%.%.") then
		http_code(403)
		log:close()
		os.exit()
	end

	-- Don't bother for .log, -calls.xml (VVX related) and -directory.xml files
	local f = string.match(basename, mac.."(.*)")
	if not ( string.match(path_info, "%.log$") or string.match(path_info, "%-calls.xml$") or string.match(path_info, "%-directory.xml$") ) then
		log:write("Checking PROV Table for results\n")
		-- Load the ACF mvc
		mvc = require("acf.mvc")
		-- We'll change the view resolver to report HTTP code
		APP=mvc:new()
		APP:read_config("acf")
		APP.view_resolver = function(self)
			return function (output)
				if output.errtxt then
					log:write(output.errtxt.."\n")
					success = false
				else
					data = output.value
				end
			end
		end
		-- Set up the action and parameters
		local clientdata = {mac=mac, data=data}
		-- Dispatch the command
		APP:dispatch("/provisioning/", "provisioning", "putfile", clientdata)
		APP:destroy()
	end
	if success then
		local path = root..path_info
		log:write("Writing to "..path.."\n")
		posix.mkdir(posix.dirname(path))
		local f = io.open(path, "w+")
		f:write(data)
		f:close()
		http_code(200)
	else
		http_code(400)
	end
end
log:close() 
%>