// Html2SAMLResponse extracts the SAMLResponse from a html document
func Html2SAMLResponse(tp *Testparams) (samlresponse *gosaml.Xp) {
	response := gosaml.NewHtmlXp(tp.Responsebody)
	samlbase64 := response.Query1(nil, `//input[@name="SAMLResponse"]/@value`)
	samlxml, _ := base64.StdEncoding.DecodeString(samlbase64)
	samlresponse = gosaml.NewXp(samlxml)
	if _, err := samlresponse.SchemaValidate(samlSchema); err != nil {
		fmt.Println("SchemaError")
	}

	certs := tp.Firstidpmd.Query(nil, `//md:KeyDescriptor[@use="signing" or not(@use)]/ds:KeyInfo/ds:X509Data/ds:X509Certificate`)
	if len(certs) == 0 {
		fmt.Printf("Could not find signing cert for: %s", tp.Firstidpmd.Query1(nil, "/@entityID"))
		log.Printf("Could not find signing cert for: %s", tp.Firstidpmd.Query1(nil, "/@entityID"))
	}

	_, pub, _ := gosaml.PublicKeyInfo(tp.Firstidpmd.NodeGetContent(certs[0]))
	assertion := samlresponse.Query(nil, "saml:Assertion[1]")
	if assertion == nil {
		fmt.Println("no assertion found")
	}
	if err := samlresponse.VerifySignature(assertion[0], pub); err != nil {
		fmt.Printf("SignatureVerificationError %s", err)
	}
	return
}
// DoRunTestHub runs a test on the hub - applying the necessary modifications on the way.
// Returns a *Testparams which can be analyzed
func DoRunTestHub(m modsset, overwrite *Testparams) (tp *Testparams) {
	if *dokrib {
		return DoRunTestKrib(m, overwrite)
	}
	if !*dohub {
		return
	}
	tp = Newtp(overwrite)
	defer xxx(tp.Trace)
	ApplyMods(tp.Attributestmt, m["attributemods"])
	tp.SSOCreateInitialRequest()
	ApplyMods(tp.Initialrequest, m["requestmods"])

	tp.SSOSendRequest()
	if tp.Resp.StatusCode == 500 {
		response := gosaml.NewHtmlXp(tp.Responsebody)
		fmt.Println(strings.Trim(response.Query1(nil, `//a[@id="errormsg"]/text()`), "\n "))
		return
	}
	ApplyMods(tp.Newresponse, m["responsemods"])
	tp.SSOSendResponse()
	if tp.Resp.StatusCode == 500 {
		response := gosaml.NewHtmlXp(tp.Responsebody)
		fmt.Println(strings.Trim(response.Query1(nil, `//a[@id="errormsg"]/text()`), "\n "))
		return
	}
	if tp.Logxml {
		log.Println("idp response", tp.Newresponse.Pp())
	}

	err := ValidateSignature(tp.Hubidpmd, tp.Newresponse)
	if err != nil {
		fmt.Printf("signature errors: %s\n", err)
	}

	return
}
// SendRequest sends a http request - GET or POST using the supplied url, server, method and cookies
// It updates the cookies and returns a http.Response and a posssible response body and error
// The server parameter contains the dns name of the actual server, which should respond to the host part of the url
func (tp *Testparams) sendRequest(url *url.URL, server, method, body string, cookies map[string]map[string]*http.Cookie) (resp *http.Response, responsebody []byte, err error) {
	if server == "" {
		server = url.Host
	}
	server += ":443"

	tr := &http.Transport{
		TLSClientConfig:    &tls.Config{InsecureSkipVerify: true},
		Dial:               func(network, addr string) (net.Conn, error) { return net.Dial("tcp", server) },
		DisableCompression: true,
	}

	client := &http.Client{
		Transport:     tr,
		CheckRedirect: func(req *http.Request, via []*http.Request) error { return errors.New("redirect-not-allowed") },
	}

	var payload io.Reader
	if method == "POST" {
		payload = strings.NewReader(body)
	}

	host := url.Host
	cookiedomain := "wayf.dk"
	req, err := http.NewRequest(method, url.String(), payload)

	for _, cookie := range cookies[cookiedomain] {
		req.AddCookie(cookie)
	}

	if method == "POST" {
		req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
		req.Header.Add("Content-Length", strconv.Itoa(len(body)))
	}

	req.Header.Add("Host", host)

	resp, err = client.Do(req)
	if err != nil && !strings.HasSuffix(err.Error(), "redirect-not-allowed") {
		// we need to do the redirect ourselves so a self inflicted redirect "error" is not an error
		debug.PrintStack()
		log.Fatalln("client.do", err)
	}

	location, _ := resp.Location()
	loc := ""
	if location != nil {
		loc = location.Host + location.Path
	}

	setcookies := resp.Cookies()
	for _, cookie := range setcookies {
		if cookies[cookiedomain] == nil {
			cookies[cookiedomain] = make(map[string]*http.Cookie)
		}
		cookies[cookiedomain][cookie.Name] = cookie
	}

	// We can't get to the body if we got a redirect pseudo error above
	if err == nil {
		responsebody, err = ioutil.ReadAll(resp.Body)
		defer resp.Body.Close()
	}

	// We didn't get a Location: header - we are POST'ing a SAMLResponse
	if loc == "" {
		response := gosaml.NewHtmlXp(responsebody)
		samlbase64 := response.Query1(nil, `//input[@name="SAMLResponse"]/@value`)
		if samlbase64 != "" {
			samlxml, _ := base64.StdEncoding.DecodeString(samlbase64)
			samlresponse := gosaml.NewXp(samlxml)
			u, _ := url.Parse(samlresponse.Query1(nil, "@Destination"))
			loc = u.Host + u.Path
		}
	}

	if tp.Trace {
		log.Printf("%-4s %-70s %s %-15s %s\n", req.Method, host+req.URL.Path, resp.Proto, resp.Status, loc)
	}

	// we need to nullify the damn redirec-not-allowed error from above
	err = nil
	return
}