-- gitano.actions
--
-- Special hook actions
--
-- Copyright 2012-2017 Daniel Silverstone <dsilvers@digital-scurf.org>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions
-- are met:
-- 1. Redistributions of source code must retain the above copyright
--    notice, this list of conditions and the following disclaimer.
-- 2. Redistributions in binary form must reproduce the above copyright
--    notice, this list of conditions and the following disclaimer in the
--    documentation and/or other materials provided with the distribution.
-- 3. Neither the name of the author nor the names of their contributors
--    may be used to endorse or promote products derived from this software
--    without specific prior written permission.
--
-- THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-- ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-- SUCH DAMAGE.
--

local util = require "gitano.util"
local log = require "gitano.log"
local gall = require "gall"
local config = require "gitano.config"
local pat = require "gitano.patterns"
local i18n = require 'gitano.i18n'
local sio = require 'luxio.simple'
local supple = require 'gitano.supple'
local subprocess = require 'luxio.subprocess'


local function update_actions(conf, repo, tags)
   log.ddebug("Repo", repo.name, "ref", tags["ref"])
   if repo.name == "gitano-admin" and
   tags["ref"] == "refs/heads/master" then
      -- Update to the master branch of the gitano-admin repo, perform a conf
      -- check based on the target sha
      log.chat(i18n.expand("VERIFY_NEW_GITANO_ADMIN"))
      local conf, msg = config.parse(repo.git:get(tags["newsha"]))
      if not conf then
         return false, msg
      end
      log.chat(i18n.expand("LOOKS_OKAY"))
   end
   if tags["ref"] == "refs/gitano/admin" then
      -- Update to the admin branch, see if we can cause the repo to
      -- parse the new admin branch safely.
      log.chat(i18n.expand("VERIFY_NEW_ADMIN_SHA", {sha=tags["newsha"]}))
      local ok, msg = repo:validate_admin_sha(tags["newsha"])
      if not ok then
         return false, msg
      end
      log.chat(i18n.expand("LOOKS_OKAY"))
   end


   -- No reason to reject, so let it through
   return true
end

local function _curl_txn(url, headers, body, content_type)
   local method = (body and body ~= "") and "POST" or "GET"
   local args = { "curl", "--max-filesize", tostring(1024*1024),
                  "--insecure", "-X", method, "-D-", "-s" }
   if type(url) ~= "string" then
      return "500", "url must be a string", {}, ""
   end
   if url:sub(1,7) ~= "http://" and url:sub(1,8) ~= "https://" then
      return "500", "url must be http:// or https://", {}, ""
   end
   headers = headers or {}
   if type(headers) ~= "table" then
      return "500", "headers must be a table if provided", {} , ""
   end
   if method == "POST" then
      headers["Content-Type"] = content_type or "application/octet-stream"
      headers["Content-Length"] = tostring(#body)
      args[#args+1] = "--data-binary"
      args[#args+1] = "@-"
   end
   for k, v in pairs(headers) do
      args[#args+1] = "-H"
      args[#args+1] = tostring(k) .. ": " .. tostring(v)
   end
   args[#args+1] = url
   args.stdin = body or ""
   args.stdout = subprocess.PIPE
   args.stderr = subprocess.PIPE
   local proc = subprocess.spawn_simple(args)
   local response = proc.stdout:read("*a")
   local err = proc.stderr:read("*a")
   proc.stdout:close()
   proc.stderr:close()
   local how, why = proc:wait()
   if (how ~= "exit" or why ~= 0) then
      return "500", err, {}, ""
   end
   local code, msg, _headers, content = response:match(pat.HTTP_RESPONSE)
   local headers = {}
   for k, v in _headers:gmatch(pat.HTTP_HEADER) do
      local r = headers[k] or {}
      r[#r+1] = v
      headers[k] = r
   end

   return code, msg, headers, content
end

local function curl_txn(...)
   local ok, code, msg, headers, content = pcall(_curl_txn, ...)
   if not ok then
      code, msg, headers, content = "500", code, {}, ""
   end
   return code, msg, headers, content
end

local function set_supple_globals(action)
   local globs = {}
   local logcopy = {}

   for _, k in pairs({ "state", "crit", "critical", "err", "error", "warn",
                       "warning", "chat", "info", "debug", "ddebug",
                       "deepdebug" }) do
      logcopy[k] = log[k]
   end
   globs["log"] = logcopy
   if action == "post-receive" then
      globs["fetch"] = curl_txn
   end
   supple.set_globals(globs)
end

return {
   update_actions = update_actions,
   set_supple_globals = set_supple_globals,
}
