package ftp

import (
	"git.loafle.net/overflow/overflow_probe/matcher/packet"
	"git.loafle.net/overflow/overflow_probe/model/scaninfo"
	log "github.com/cihub/seelog"
)

// FTP Status codes, defined in RFC 959
const (
	statusReadyServer         = "120"
	statusOK                  = "200"
	statusNewConnectOK        = "220"
	statusSystemNameOK        = "215"
	statusCloseConnect        = "221"
	statusUnkownCMD           = "202"
	statusTlsUseOK            = "234"
	statusCloseControlConnect = "421"
	statusSyntaxErr           = "500"
	statusParamSyntaxErr      = "501"
	statusNotUseCMD           = "502"
	statusIncorrectCMD        = "503"
	statusTlsNotUse           = "534"
	statusNeedUserId          = "332"
)

type FTPMatcher struct {
	sendPackets []*packet.Packet
	isFtps      bool
}

func (ftp *FTPMatcher) Match(index int, packet *packet.Packet, info scaninfo.ServiceScanInfo) bool {
	result := false

	if packet == nil || packet.Buffer == nil || packet.Len == 0 {
		log.Error("Packet nil")
		return result
	}

	str := string(packet.Buffer)
	//fmt.Println(str)
	code := str[:3]

	if index == 0 {
		switch code {
		case statusNewConnectOK, statusReadyServer:
			//fmt.Println(code)
			result = true
			break
		}
	} else if index == 1 {
		switch code {
		case statusSystemNameOK, statusSyntaxErr, statusParamSyntaxErr, statusNotUseCMD:
			//fmt.Println(code)
			result = true
			break
		}
	} else if index == 2 {
		switch code {
		case statusIncorrectCMD, statusParamSyntaxErr, statusNotUseCMD, statusNeedUserId:
			//fmt.Println(code)
			result = true
			break
		}
	} else if index == 3 {
		switch code {
		case statusCloseConnect, statusSyntaxErr:
			//fmt.Println(code)
			result = true
			break
		}
	}

	if index == 3 && result == true {
		var err error
		var isfs bool

		//fmt.Println(info.Port.Host.Ip, info.Port.Port)

		isfs, err = StartCheckFTPS(info.GetIP(), info.GetPort())

		if isfs && err == nil {
			ftp.isFtps = isfs
		} else if err != nil {
			log.Warn("FTPS Check Error : ", err.Error())
		}
	}

	return result
}

func (ftp *FTPMatcher) PacketCount() int {
	return len(ftp.sendPackets)
}

func (ftp *FTPMatcher) Packet(index int) *packet.Packet {
	return ftp.sendPackets[index]
}

func (ftp *FTPMatcher) ServiceName() string {
	re := ""
	if ftp.isFtps {
		re = "FTPS"
	} else {
		re = "FTP"
	}
	return re
}

func (ftp *FTPMatcher) IsError(index int, packet *packet.Packet, info scaninfo.ServiceScanInfo) bool {
	return false
}

func (ftp *FTPMatcher) IsNoResponse(index int) bool {
	return false
}

func (ftp *FTPMatcher) IsPrePacket() bool {
	return true
}

func NewFTPMatcher() *FTPMatcher {

	ftm := FTPMatcher{}

	sysStr := "SYST\r\n"
	systByte := make([]byte, len(sysStr))
	copy(systByte[:], sysStr)
	ftm.sendPackets = append(ftm.sendPackets, packet.NewPacket(systByte, len(sysStr)))

	passStr := "PASS \r\n"
	passByte := make([]byte, len(passStr))
	copy(passByte[:], passStr)
	ftm.sendPackets = append(ftm.sendPackets, packet.NewPacket(passByte, len(passStr)))

	quitStr := "QUIT\r\n"
	quitByte := make([]byte, len(quitStr))
	copy(quitByte[:], quitStr)
	ftm.sendPackets = append(ftm.sendPackets, packet.NewPacket(quitByte, len(quitStr)))

	return &ftm
}
