#!/usr/bin/env python
from __future__ import print_function
from hashlib import sha1
from jenkins_callback import build_jobs
import os, sys, json, hmac, re

print("Content-Type: text/html\n")
BASE_DIRECTORY = "/data"

def check_event(data):
  if not 'HTTP_X_GITHUB_EVENT' in os.environ: return False,''
  event = os.environ['HTTP_X_GITHUB_EVENT']
  if not event in ['issues', 'issue_comment', 'pull_request' , 'push', "release", "status", "workflow_dispatch"]: return False,event
  ok_event = False
  try:
    try:
      if repo_config.OPEN_ISSUE_FOR_PUSH_TESTS:
        repo_config.VALID_WEB_HOOKS = repo_config.VALID_WEB_HOOKS+['issues', 'issue_comment']
    except: pass
    if repo_config.VALID_WEB_HOOKS:
      for ex in repo_config.VALID_WEB_HOOKS:
        if re.match('^'+ex+'$',event):
          ok_event = True
          break
      if not ok_event:
        print("ERROR: No web hook matched the event",event)
        return False,event
  except: pass
  full_name = data['repository']['full_name']
  if (not ok_event) and (not is_valid_gh_repo(full_name)): return False,event
  if (event == "status"):
    cxt = data["context"].split("/")
    if (len(cxt)<2) or (cxt[0] not in ["bot", "cms"]) or (not re.match('^[1-9][0-9]*$', cxt[1])):
      return False,event
    event = "issue_comment"
    data["issue"] = {"number": cxt[1]}
    return True, event
  if (event == 'push'):
    branch = data["ref"]
    if not branch.startswith("refs/heads/"): return False, event
    if not ok_event:
      if not get_repository_hooks(full_name,"Jenkins_Github_Hook_Push"): return False,event
    return True, event
  if (not ok_event) and (not get_repository_hooks(full_name,"Jenkins_Github_Hook")): return False,event
  if (event == 'pull_request'): return True,event
  elif (event == 'issues') and (not data['action'] in ['opened']): return False,event
  elif (event == 'issue_comment'):
    #if (not data['action'] in ['created','edited','deleted']): return False,event
    if not full_name in ['cms-sw/cmsdist','cms-sw/cmssw']: return True,event
    from categories import USERS_TO_TRIGGER_HOOKS as l2
    from releases import USERS_TO_TRIGGER_HOOKS as managers
    extra_testers = set([lab['name'][6:] for lab in data['issue']['labels'] if lab['name'].startswith('allow-')])
    valid_admins = l2 | managers | extra_testers
    if not data['comment']['user']['login'] in valid_admins:
      try:
        from cmsdist_merge_permissions import isValidWebHook
        return isValidWebHook(data), "comp_issue_comment"
      except Exception as e: pass
      return False, event
  elif (event == 'release') and (data['action'] not in ["created"]):
    return False, event
  return True, event

#check signature function
def check_signature(data, repo_config):
  if not 'HTTP_X_HUB_SIGNATURE' in os.environ: return False
  sha_name, signature = os.environ['HTTP_X_HUB_SIGNATURE'].split('=')
  if sha_name != 'sha1': return False
  secret_file = BASE_DIRECTORY+'/secrets/github_hook_secret'
  try:
    from commands import getstatusoutput as run_cmd
    err, out = run_cmd("echo '%s' | openssl enc -d -a -base64 -aes-256-cbc -salt -pass file:%s" % (repo_config.GITHUB_WEBHOOK_TOKEN, secret_file))
    if err: return False
    secret = out.strip("\n")
  except:
    try:
      secret = open(secret_file).read().split('\n')[0].strip()
    except Exception as e:
      print("Unable to read secret file")
      return False
  mac = hmac.new(str(secret), msg=data, digestmod=sha1)
  print(str(mac.hexdigest()) , str(signature))
  return str(mac.hexdigest()) == str(signature)

#check source ip in white list fucntion
def check_ip():
  sip = os.environ['REMOTE_ADDR']
  if 'HTTP_X_FORWARDED_FOR' in os.environ and os.environ['HTTP_X_FORWARDED_FOR']!='':
    sip = os.environ['HTTP_X_FORWARDED_FOR']
  print("GH IP",sip)
  for ip_net in [i.strip() for i in open(BASE_DIRECTORY+'/sdt/github-hook-meta.txt').readlines()]:
    ip, mask = ip_net.split('/')
    mask = int(mask)
    ip = ''.join(["{0:08b}".format(int(i)) for i in ip.split('.') ])[0:mask]
    src_ip = ''.join([ "{0:08b}".format(int(i)) for i in sip.split('.')])[0:mask]
    print("Checking %s vs %s: %s" % (ip, src_ip, ip == src_ip))
    if ip == src_ip:
      return True
  return False

#get data
data = sys.stdin.read()
try:
  payload = json.loads(data)
except Exception as e:
  print("Invalid data, can not read json input")
  sys.exit()
repo_name = payload["repository"]["full_name"]
sys.path.insert(0,os.path.join(BASE_DIRECTORY,"sdt/cms-bot"))
sys.path.insert(0,os.path.join(BASE_DIRECTORY,"sdt/cms-bot/repos",repo_name.replace("-","_")))
import repo_config
from github_hooks_config import get_repository_hooks, is_valid_gh_repo

#check valid data, ip and signature of source
valid, event = check_event(payload)
print(valid, event)
if (not valid) or (not check_ip()) or (not check_signature(data, repo_config)):
  print("Invalid or wrong signatures")
  sys.exit()

xparam = []
xparam.append({"name":"REPOSITORY","value":repo_name})
xparam.append({"name":"EVENT","value":event})
try: xparam.append({"name":"ACTION","value":payload['action']})
except: xparam.append({"name":"ACTION","value":""})
if ("labeled" in xparam[-1]["value"]) and (not repo_name.startswith("cms-sw/")):
  print("Ignored Event/Action:", xparam)
  sys.exit()

etype=""
if   event in ['pull_request']:etype="pull_request"
elif event in ['issues', 'issue_comment','comp_issue_comment']: etype="issue"
try: xparam.append({"name":"ISSUE_NUMBER","value":payload[etype]["number"]})
except: xparam.append({"name":"ISSUE_NUMBER","value":""})
if event in ["release"]:
  xparam.append({"name":"TITLE","value":payload["release"]["tag_name"]})
else:
  try: xparam.append({"name":"TITLE","value":payload[etype]["title"]})
  except: xparam.append({"name":"TITLE","value":""})
try: xparam.append({"name":"BRANCH","value":payload[etype]["base"]["ref"]})
except:
  try: xparam.append({"name":"BRANCH","value":payload["ref"]})
  except: xparam.append({"name":"BRANCH","value":""})
if 'commits' in payload: xparam.append({"name":"COMMITS","value":' '.join([str(c['id']) for c in payload['commits']])})
else: xparam.append({"name":"COMMITS","value":""})
try:
  if repo_config.WEBHOOK_PAYLOAD:
    xparam.append({"name":"PAYLOAD","value":json.dumps(payload)})
except: pass

build_jobs(repo_config.JENKINS_SERVER, [(json.dumps({"parameter":xparam}),"github-webhook")])
