package cli

import (
	"bufio"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"net"
	"os"
	"strconv"
	"strings"

	"github.com/vulncheck-oss/go-exploit/c2"
	"github.com/vulncheck-oss/go-exploit/config"
	"github.com/vulncheck-oss/go-exploit/db"
	"github.com/vulncheck-oss/go-exploit/output"
	"github.com/vulncheck-oss/go-exploit/protocol"
)

// VulnCheck IPIntel data is shipped in single line JSON blobs. There is substantially more metadata, but
// for the purposes of the scann we need to support:
// {"ip":"127.0.0.1","port":80,"ssl":false}.
type IPIntel struct {
	IP   string `json:"ip"`
	Port int    `json:"port"`
	SSL  bool   `json:"ssl"`
}

// A structure that defines the special flags this exploit implements.
type CustomFlag struct {
	Name    string
	Type    string
	Default string
}

// Increment the provided IP address.
func inc(ip net.IP) {
	for j := len(ip) - 1; j >= 0; j-- {
		ip[j]++
		if ip[j] > 0 {
			break
		}
	}
}

// Generate all the IP Addresses specified by the CIDR. This... could consume a decent amount of memory.
func generateIPv4CIDR(cidr string) []string {
	cidrSlice := make([]string, 0)
	ip, ipNet, err := net.ParseCIDR(cidr)
	if err != nil {
		output.PrintFrameworkError(err.Error())

		return cidrSlice
	}

	for ip := ip.Mask(ipNet.Mask); ipNet.Contains(ip); inc(ip) {
		cidrSlice = append(cidrSlice, ip.String())
	}

	return cidrSlice
}

func buildRhosts(conf *config.Config, rhosts string, rports string) bool {
	// convert the provided Rports to a slice of int
	rportsSlice := make([]int, 0)
	if len(rports) != 0 {
		splitPorts := strings.Split(rports, ",")
		for _, port := range splitPorts {
			portInt, err := strconv.Atoi(port)
			if err != nil {
				output.PrintfFrameworkError("Failed to convert provided rports: %s", err)

				return false
			}
			rportsSlice = append(rportsSlice, portInt)
		}
	} else {
		rportsSlice = append(rportsSlice, conf.Rport)
	}

	// convert the rhosts csv into a slice of strings
	rhostsSlice := make([]string, 0)
	if len(rhosts) != 0 {
		splitRhosts := strings.Split(rhosts, ",")
		for _, host := range splitRhosts {
			if strings.Contains(host, "/") {
				rhostsSlice = append(rhostsSlice, generateIPv4CIDR(host)...)
			} else {
				rhostsSlice = append(rhostsSlice, host)
			}
		}
	} else {
		rhostsSlice = append(rhostsSlice, conf.Rhost)
	}

	// determine the SSL status to assign everything
	SSL := config.SSLDisabled
	if conf.SSL {
		SSL = config.SSLEnabled
	} else if conf.DetermineSSL {
		SSL = config.SSLAutodiscover
	}

	// convert rhostsSlice and rportsSlices to many RhostTriplet. Obviously
	// this will consume a lot of memory if you provide a ton of ip/port combos
	// so probably just don't do that ok? Like 1 million is probably fine, right?
	// 1 billion, not so much.
	conf.RhostsNTuple = make([]config.RhostTriplet, 0)
	for iHost := range rhostsSlice {
		for iPort := range rportsSlice {
			triplet := config.RhostTriplet{
				Rhost: rhostsSlice[iHost],
				Rport: rportsSlice[iPort],
				SSL:   SSL,
			}
			conf.RhostsNTuple = append(conf.RhostsNTuple, triplet)
		}
	}

	return true
}

// Converts a CSV / triplet into a config.RhostTriplet to be used by the framework.
func parseTriplet(conf *config.Config, line string) bool {
	triplet := strings.Split(line, ",")
	if len(triplet) != 3 {
		output.PrintfFrameworkError("Failed to convert the CSV into a triplet: %s", line)

		return false
	}

	portInt, err := strconv.Atoi(triplet[1])
	if err != nil {
		output.PrintfFrameworkError("Failed to convert the provided rport: %s", triplet[1])

		return false
	}

	// determine the SSL status to assign everything
	SSL := config.SSLDisabled
	if len(triplet[2]) != 0 {
		SSL = config.SSLEnabled
	} else if conf.DetermineSSL {
		SSL = config.SSLAutodiscover
	}

	entry := config.RhostTriplet{
		Rhost: triplet[0],
		Rport: portInt,
		SSL:   SSL,
	}
	conf.RhostsNTuple = append(conf.RhostsNTuple, entry)

	return true
}

// Converts a tuple (e.g. host.com:80) into a config.RhostTriplet to be used by the framework.
func parseTuple(conf *config.Config, line string) bool {
	// Use last index in case line contains an ipv6 address
	lastIndex := strings.LastIndex(line, ":")
	if lastIndex == -1 {
		output.PrintFrameworkError("The tuple doesn't contain a ':' character")

		return false
	}

	portInt, err := strconv.Atoi(line[lastIndex+1:])
	if err != nil {
		output.PrintfFrameworkError("Failed to convert the provided rport: %s", line[lastIndex+1:])

		return false
	}

	// user configured SSL autodiscovery
	SSL := config.SSLDisabled
	if conf.DetermineSSL {
		SSL = config.SSLAutodiscover
	}

	entry := config.RhostTriplet{
		Rhost: line[:lastIndex],
		Rport: portInt,
		SSL:   SSL,
	}
	conf.RhostsNTuple = append(conf.RhostsNTuple, entry)

	return true
}

// Converts an VulnCheck IP Intel JSON object into a config.RhostTriplet to be used by the framework.
func parseIPIntel(conf *config.Config, line string) bool {
	var intel map[string]interface{}

	err := json.Unmarshal([]byte(line), &intel)
	if err != nil {
		output.PrintfFrameworkError("Failed to unmarshal JSON: %s", line)

		return false
	}

	// determine the SSL status to assign everything
	SSL := config.SSLDisabled
	if intel["ssl"].(bool) {
		SSL = config.SSLEnabled
	} else if conf.DetermineSSL {
		SSL = config.SSLAutodiscover
	}

	entry := config.RhostTriplet{
		Rhost: intel["ip"].(string),
		Rport: int(intel["port"].(float64)),
		SSL:   SSL,
	}
	conf.RhostsNTuple = append(conf.RhostsNTuple, entry)

	return true
}

// Consumes a file of remote hosts. The allowed formats are:
// 1. ip,port,<any value for ssl>
// 2. ip:port
// 3. VulnCheck IP Intel JSON
// The file can also be "-" which means stdin.
// Return true if conf.RhostsNTuple is not 0.
func parseRhostsFile(conf *config.Config, rhostsFile string) bool {
	hostsFile := os.Stdin
	if rhostsFile != "-" {
		tmpHostsFile, err := os.Open(rhostsFile)
		if err != nil {
			output.PrintFrameworkError(err.Error())

			return false
		}
		hostsFile = tmpHostsFile
		defer hostsFile.Close()
	}

	conf.RhostsNTuple = make([]config.RhostTriplet, 0)
	lineScan := bufio.NewScanner(hostsFile)
	for lineScan.Scan() {
		line := lineScan.Text()
		switch {
		case strings.HasPrefix(line, "{") && strings.HasSuffix(line, "}"):
			if !parseIPIntel(conf, line) {
				return false
			}
		case strings.Contains(line, ","):
			if !parseTriplet(conf, line) {
				return false
			}
		case strings.Contains(line, ":"):
			if !parseTuple(conf, line) {
				return false
			}
		case len(line) == 0:
			return len(conf.RhostsNTuple) != 0
		default:
			output.PrintfFrameworkError("Unexpected rhosts-file line: %s", line)

			return false
		}
	}

	return len(conf.RhostsNTuple) != 0
}

// parse the user provided rhosts into config.Config.
func handleRhostsOptions(conf *config.Config, rhosts string, rports string, rhostsFile string) bool {
	if len(rhostsFile) != 0 {
		return parseRhostsFile(conf, rhostsFile)
	}

	// dump the selected user agent to debug
	output.PrintfFrameworkDebug("Using the HTTP User-Agent: %s", protocol.GlobalUA)

	return buildRhosts(conf, rhosts, rports)
}

func commonValidate(conf *config.Config, rhosts string, rports string, rhostsFile string) bool {
	switch {
	case len(conf.Rhost) == 0 && len(rhosts) == 0 && len(rhostsFile) == 0:
		output.PrintFrameworkError("Missing required option 'rhost', 'rhosts', or 'rhosts-file'")

		return false
	case conf.Rport == 0 && len(rports) == 0 && len(rhostsFile) == 0:
		output.PrintFrameworkError("Missing required option 'rport', 'rports', or 'rhosts-file'")

		return false
	case len(conf.Rhost) != 0 && len(rhosts) != 0:
		output.PrintFrameworkError("'rhost' and 'rhosts' are mutually exclusive")

		return false
	case len(conf.Rhost) != 0 && len(rhostsFile) != 0:
		output.PrintFrameworkError("'rhost' and 'rhosts-file' are mutually exclusive")

		return false
	case len(rhosts) != 0 && len(rhostsFile) != 0:
		output.PrintFrameworkError("'rhosts' and 'rhosts-file' are mutually exclusive")

		return false
	case !conf.DoVerify && !conf.DoVersionCheck && !conf.DoExploit:
		output.PrintFrameworkError("Please provide an action (-v, -c, -e)")

		return false
	case conf.SSL && conf.DetermineSSL:
		output.PrintFrameworkError("-a and -s are mutually exclusive")

		return false
	}

	// the user can also specify '-' for stdin which can't be used with os.Stat
	if len(rhostsFile) != 0 && rhostsFile != "-" {
		_, err := os.Stat(rhostsFile)
		if err != nil {
			output.PrintfFrameworkError("Failed to stat %s: %s", rhostsFile, err)

			return false
		}
	}

	return true
}

// command line options for proxying.
func proxyFlags(proxy *string) {
	flag.StringVar(proxy, "proxy", "", "A proxy that will be used for all communication")
}

// Go can automatically handle HTTP proxying (if HTTP_PROXY env is set) and it can automatically
// handle HTTPS proxying (if HTTPS_PROXY env is set). It can't handle normal tcp socket proxying
// though, so we'll set ALL_PROXY for that (and handle the logic in protocol/tcpsocket.go).
func handleProxyOptions(proxy string) {
	if len(proxy) == 0 {
		return
	}
	os.Setenv("HTTP_PROXY", proxy)
	os.Setenv("HTTPS_PROXY", proxy)
	os.Setenv("ALL_PROXY", proxy)
}

// command line options for logging.
func loggingFlags(logFile *string, frameworkLogLevel *string, exploitLogLevel *string) {
	flag.BoolVar(&output.FormatJSON, "log-json", false, "Indicates if logging should use JSON")
	flag.StringVar(logFile, "log-file", "", "The file to write log messages to.")

	logLevels := ""
	for key := range output.LogLevels {
		logLevels += "\n" + key
	}

	flag.StringVar(frameworkLogLevel, "fll", "STATUS", "The minimum log level for the framework:"+logLevels)
	flag.StringVar(exploitLogLevel, "ell", "STATUS", "The minimum log level for the exploit:"+logLevels)
}

func handleLogOptions(logFile string, frameworkLogLevel string, exploitLogLevel string) bool {
	value, found := output.LogLevels[frameworkLogLevel]
	if !found {
		output.PrintFrameworkError("Invalid framework log level provided")

		return false
	}
	output.SetFrameworkLogLevel(value)

	value, found = output.LogLevels[exploitLogLevel]
	if !found {
		output.PrintFrameworkError("Invalid exploit log level provided")

		return false
	}
	output.SetExploitLogLevel(value)

	if len(logFile) != 0 && !output.SetOutputFile(logFile) {
		return false
	}

	return true
}

// command line flags for defining the remote host.
func remoteHostFlags(conf *config.Config, rhosts *string, rhostsFile *string, rports *string) {
	flag.StringVar(&conf.Rhost, "rhost", "", "The remote target's IP address")
	flag.StringVar(rhosts, "rhosts", "", "A comma delimited list of remote target IP addresses")
	flag.StringVar(rhostsFile, "rhosts-file", "", "A CSV file or stdin with a list of targets")
	// the Rport should be pre-configured to have a default value (see config.go:New())
	flag.IntVar(&conf.Rport, "rport", conf.Rport, "The remote target's server port")
	flag.StringVar(rports, "rports", "", "A comma delimited list of the remote target's server ports")
	// generic all comms connection timeout
	flag.IntVar(&protocol.GlobalCommTimeout, "timeout", 10, "A default timeout for all socket communication")
	// allow the user to use their own HTTP user-agent if they so wish
	flag.StringVar(&protocol.GlobalUA, "user-agent", protocol.GlobalUA, "The User-Agent to use in HTTP requests")
}

// command line flags for defining the local host.
func localHostFlags(conf *config.Config) {
	flag.StringVar(&conf.Lhost, "lhost", "", "The IP address the configured c2 will bind to")
	flag.IntVar(&conf.Lport, "lport", 0, "The port the configured c2 will bind to")
}

// command line flags that control the exploits behavior.
func exploitFunctionality(conf *config.Config) {
	flag.BoolVar(&conf.DoVerify, "v", false, "Verify the target is "+conf.Product)
	flag.BoolVar(&conf.DoVersionCheck, "c", false, "Perform a version check before attempting exploitation")
	flag.BoolVar(&conf.DoExploit, "e", false, "Exploit the target")
}

// command line flags that control ssl communication with the target.
func sslFlags(conf *config.Config) {
	flag.BoolVar(&conf.SSL, "s", false, "Use https to communicate with the target")
	flag.BoolVar(&conf.DetermineSSL, "a", false, "Automatically determine if the remote target uses SSL")
}

// Allow the user to provide the `-db` command to share content across exploits and limit the size of the HTTP cache.
func dbFlags(conf *config.Config) {
	flag.StringVar(&conf.DBName, "db", "", "An SQLite3 DB to share target data across")
	flag.IntVar(&db.GlobalHTTPRespCacheLimit, "cacheMax", 10*(1024*1024), "The maximum size of an HTTP response that can be cached")
}

// handle generic sounding c2 flags.
func c2Flags(c2Selection *string, conf *config.Config) {
	flag.BoolVar(&conf.ThirdPartyC2Server, "o", false, "Indicates if the reverse shell should be caught by an outside program (nc, openssl)")

	if len(conf.SupportedC2) == 0 {
		// the implementing exploit doesn't support any c2, just exit
		return
	}

	c2Default := conf.SupportedC2[0].Name
	c2Available := "The C2 server implementation to use. Supported: "
	for _, value := range conf.SupportedC2 {
		c2Name := value.Name
		c2Available += "\n\t" + c2Name

		// add the supported c2 flags, allowing for command line config of the backend
		impl, success := c2.GetInstance(value)
		if success {
			impl.CreateFlags()
		}
	}
	c2Available += "\n"
	flag.StringVar(c2Selection, "c2", c2Default, c2Available)

	// flags unique to remote code execution
	flag.IntVar(&conf.Bport, "bport", 0, "The port to attach the bind shell to")

	// checks if the default values have been changed in the exploit directly
	if conf.C2Timeout != 30 && conf.C2Timeout != 0 {
		flag.IntVar(&conf.C2Timeout, "t", conf.C2Timeout, "The number of seconds to listen for reverse shells.")
	} else {
		flag.IntVar(&conf.C2Timeout, "t", 30, "The number of seconds to listen for reverse shells.")
	}
}

// loop through the c2 the exploit supports and find the one the user actually selected.
func validateC2Selection(c2Selection string, conf *config.Config) bool {
	c2Selected, ok := c2.StringToImpl(c2Selection)
	if !ok {
		output.PrintFrameworkError("The user provided an invalid c2 implementation")

		return false
	}
	// is this a supported c2?
	foundSupported := false
	for _, value := range conf.SupportedC2 {
		if c2Selected == value {
			foundSupported = true
		}
	}
	if !foundSupported {
		output.PrintFrameworkError("The c2 you selected is not supported by this exploit.")

		return false
	}
	conf.C2Type = c2Selected

	return true
}

// Print the details of the implementation to the configured log.
func printDetails(conf *config.Config) {
	supportedC2Strings := make([]string, 0)
	for _, value := range conf.SupportedC2 {
		supportedC2Strings = append(supportedC2Strings, value.Name)
	}

	customFlags := make([]CustomFlag, 0)
	for key, value := range conf.StringFlagsMap {
		customFlags = append(customFlags, CustomFlag{
			Name:    key,
			Type:    fmt.Sprintf("%T", *value),
			Default: *value,
		})
	}
	for key, value := range conf.UintFlagsMap {
		customFlags = append(customFlags, CustomFlag{
			Name:    key,
			Type:    fmt.Sprintf("%T", *value),
			Default: strconv.FormatUint(uint64(*value), 10),
		})
	}
	for key, value := range conf.IntFlagsMap {
		customFlags = append(customFlags, CustomFlag{
			Name:    key,
			Type:    fmt.Sprintf("%T", *value),
			Default: strconv.Itoa(*value),
		})
	}
	for key, value := range conf.BoolFlagsMap {
		customFlags = append(customFlags, CustomFlag{
			Name:    key,
			Type:    fmt.Sprintf("%T", *value),
			Default: strconv.FormatBool(*value),
		})
	}

	output.PrintSuccess("Implementation Details",
		"ExploitType", fmt.Sprintf("%v", conf.ExType),
		"AssetDetection", conf.Impl.AssetDetection,
		"VersionScanner", conf.Impl.VersionScanning,
		"Exploitation", conf.Impl.Exploitation,
		"SupportedC2", supportedC2Strings,
		"Vendor", conf.Vendor,
		"Products", conf.Products,
		"CPE", conf.CPE,
		"CVE", conf.CVE,
		"Protocol", conf.Protocol,
		"DefaultPort", conf.Rport,
		"CustomFlags", customFlags,
	)
}

// Parses the command line arguments used by RCE exploits.
func CodeExecutionCmdLineParse(conf *config.Config) bool {
	var rhosts string
	var rhostsFile string
	var rports string
	var logFile string
	var frameworkLogLevel string
	var exploitLogLevel string
	var proxy string
	var c2Selection string

	dbFlags(conf)
	proxyFlags(&proxy)
	loggingFlags(&logFile, &frameworkLogLevel, &exploitLogLevel)
	remoteHostFlags(conf, &rhosts, &rhostsFile, &rports)
	localHostFlags(conf)
	exploitFunctionality(conf)
	sslFlags(conf)
	c2Flags(&c2Selection, conf)
	detailsFlag := flag.Bool("details", false, "Print the implementation details for this exploit")

	flag.Usage = func() {
		// banner explaining what the software is
		fmt.Printf("An exploit for %s %s that can generate a reverse shell or bind shell\n\n", conf.Product, conf.CVE)

		// print default usage information
		flag.PrintDefaults()
	}
	flag.Parse()

	if *detailsFlag {
		printDetails(conf)

		return false
	}
	handleProxyOptions(proxy)

	// validate remaining command line arguments
	success := handleLogOptions(logFile, frameworkLogLevel, exploitLogLevel) &&
		commonValidate(conf, rhosts, rports, rhostsFile) && handleRhostsOptions(conf, rhosts, rports, rhostsFile)

	// validate a reverse shell or bind shell params are correctly specified
	if conf.DoExploit {
		success = validateC2Selection(c2Selection, conf)

		if rhostsFile == "-" {
			// this is a pretty dirty check but c2 that use stdin can't be used after piping targets in
			if conf.C2Type == c2.SimpleShellServer || conf.C2Type == c2.SimpleShellClient ||
				conf.C2Type == c2.HTTPServeShell || conf.C2Type == c2.SSLShellServer {
				output.PrintFrameworkError("Piping targets via stdin cannot be paired with c2 that uses stdin")
				success = false
			}
		}

		if conf.Bport == 0 && (conf.Lport == 0 || len(conf.Lhost) == 0) && conf.C2Type.Category != c2.ExternalCategory {
			output.PrintFrameworkError("Missing exploitation options (bindshell or reverse shell)")
			success = false
		}
		if conf.Bport != 0 && conf.Lport != 0 {
			output.PrintFrameworkError("User specified both bind shell and reverse shell ports")
			success = false
		}
	}

	return success
}

func InformationDisclosureCmdLineParse(conf *config.Config) bool {
	var rhosts string
	var rhostsFile string
	var rports string
	var logFile string
	var frameworkLogLevel string
	var exploitLogLevel string
	var proxy string

	dbFlags(conf)
	proxyFlags(&proxy)
	loggingFlags(&logFile, &frameworkLogLevel, &exploitLogLevel)
	remoteHostFlags(conf, &rhosts, &rhostsFile, &rports)
	localHostFlags(conf)
	exploitFunctionality(conf)
	sslFlags(conf)
	detailsFlag := flag.Bool("details", false, "Print the implementation details for this exploit")

	flag.Usage = func() {
		// banner explaining what the software is
		fmt.Printf("An exploit for %s %s that can leak sensitive data\n\n", conf.Product, conf.CVE)

		// print default usage information
		flag.PrintDefaults()

		// usage examples
		fmt.Println("Usage example:")
		fmt.Println("\t./exploit -v -c -e -a -rhost 10.12.70.247 -rport 443")
	}
	flag.Parse()

	if *detailsFlag {
		printDetails(conf)

		return false
	}
	handleProxyOptions(proxy)

	return handleLogOptions(logFile, frameworkLogLevel, exploitLogLevel) &&
		commonValidate(conf, rhosts, rports, rhostsFile) && handleRhostsOptions(conf, rhosts, rports, rhostsFile)
}

func WebShellCmdLineParse(conf *config.Config) bool {
	var rhosts string
	var rhostsFile string
	var rports string
	var logFile string
	var frameworkLogLevel string
	var exploitLogLevel string
	var proxy string

	dbFlags(conf)
	proxyFlags(&proxy)
	loggingFlags(&logFile, &frameworkLogLevel, &exploitLogLevel)
	remoteHostFlags(conf, &rhosts, &rhostsFile, &rports)
	localHostFlags(conf)
	exploitFunctionality(conf)
	sslFlags(conf)
	detailsFlag := flag.Bool("details", false, "Print the implementation details for this exploit")

	flag.Usage = func() {
		// banner explaining what the software is
		fmt.Printf("An exploit for %s %s that drops a webshell\n\n", conf.Product, conf.CVE)

		// print default usage information
		flag.PrintDefaults()

		// usage examples
		fmt.Println("Usage example:")
		fmt.Println("\t./exploit -v -c -e -a -rhost 10.12.70.247 -rport 443")
	}
	flag.Parse()

	if *detailsFlag {
		printDetails(conf)

		return false
	}
	handleProxyOptions(proxy)

	return handleLogOptions(logFile, frameworkLogLevel, exploitLogLevel) &&
		commonValidate(conf, rhosts, rports, rhostsFile) && handleRhostsOptions(conf, rhosts, rports, rhostsFile)
}

func loadFileFormatTemplate(templateFilePath string, conf *config.Config) bool {
	if len(templateFilePath) == 0 {
		// the user doesn't have to provide an -in. There are plenty of scenarios where I could imagine
		// the template would be embedded in the exploit. That seems fine to me.
		return true
	}

	fileTemplate, err := os.Open(templateFilePath)
	if err != nil {
		output.PrintfFrameworkError("Failed to open the template file: %s", err.Error())

		return false
	}
	defer fileTemplate.Close()

	content, err := io.ReadAll(fileTemplate)
	if err != nil {
		output.PrintfFrameworkError("Failed to read the template file: %s", err.Error())

		return false
	}

	conf.FileTemplateData = string(content)
	if len(conf.FileTemplateData) == 0 {
		output.PrintfFrameworkError("The template file was empty")

		return false
	}

	return true
}

// FileFormat doesn't handle any type of remote host configuration. FileFormat exploits just
// take an -in and -out, where "in" is expected to be some type of template and "out" is
// the file to generate.
func FormatFileCmdLineParse(conf *config.Config) bool {
	var logFile string
	var frameworkLogLevel string
	var exploitLogLevel string
	var templateFile string
	var c2Selection string

	loggingFlags(&logFile, &frameworkLogLevel, &exploitLogLevel)
	localHostFlags(conf)
	exploitFunctionality(conf)
	c2Flags(&c2Selection, conf)
	detailsFlag := flag.Bool("details", false, "Print the implementation details for this exploit")
	flag.StringVar(&templateFile, "in", "", "The file format template to work with")
	flag.StringVar(&conf.FileFormatFilePath, "out", "", "The file to write the malicious file to")

	flag.Usage = func() {
		// banner explaining what the software is
		fmt.Printf("An exploit for %s %s that crafts a malicious file\n\n", conf.Product, conf.CVE)

		// print default usage information
		flag.PrintDefaults()

		// usage examples
		fmt.Println("Usage example:")
		fmt.Println("\t./exploit -e -in <file> -out <file>")
	}
	flag.Parse()

	if *detailsFlag {
		printDetails(conf)

		return false
	}

	if !loadFileFormatTemplate(templateFile, conf) {
		return false
	}
	if len(conf.FileFormatFilePath) == 0 {
		output.PrintFrameworkError("Must provide an -out parameter")

		return false
	}
	if !conf.DoExploit {
		output.PrintFrameworkError("Exploitation must be invoked for file format exploits")

		return false
	}
	if conf.DoVerify || conf.DoVersionCheck {
		output.PrintFrameworkError("Verification and version checking are disabled for file format exploits")

		return false
	}

	// must be validate (to set default for payload gen) and then check third party c2
	if !validateC2Selection(c2Selection, conf) {
		return false
	}

	if conf.Lport == 0 || len(conf.Lhost) == 0 {
		output.PrintFrameworkError("Missing exploitation options (-Lhost or -Lport)")

		return false
	}

	return handleLogOptions(logFile, frameworkLogLevel, exploitLogLevel)
}

func LocalCmdLineParse(conf *config.Config) bool {
	var logFile string
	var frameworkLogLevel string
	var exploitLogLevel string
	var c2Selection string

	loggingFlags(&logFile, &frameworkLogLevel, &exploitLogLevel)
	localHostFlags(conf)
	exploitFunctionality(conf)
	c2Flags(&c2Selection, conf)
	detailsFlag := flag.Bool("details", false, "Print the implementation details for this exploit")

	flag.Usage = func() {
		// banner explaining what the software is
		fmt.Printf("An exploit for %s %s that exploits a local vulnerability\n\n", conf.Product, conf.CVE)

		// print default usage information
		flag.PrintDefaults()

		// usage examples
		fmt.Println("Usage example:")
		fmt.Println("\t./exploit -e")
	}
	flag.Parse()

	if *detailsFlag {
		printDetails(conf)

		return false
	}

	// must be validate (to set default for payload gen) and then check third party c2
	if !conf.ThirdPartyC2Server && !validateC2Selection(c2Selection, conf) {
		return false
	}

	if !conf.ThirdPartyC2Server && (conf.Lport == 0 || len(conf.Lhost) == 0) {
		output.PrintFrameworkError("Missing exploitation options (-Lhost or -Lport)")

		return false
	}

	return handleLogOptions(logFile, frameworkLogLevel, exploitLogLevel)
}
