package main

import (
	"bufio"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
	"time"
)

// ─── Constants & endpoints ───────────────────────────────────────────────────

const (
	authURL        = "https://m.syspro.cc/ua.php/ua.php"
	ipInfoURL      = "https://ipinfo.io/ip"
	starLordBase   = "https://pp2.syspro.cc"
	repoBase       = "https://repo.syscare360.com"
	kernelCareURL  = "https://patches.kernelcare.com"
	cpanelTimeURL  = "http://httpupdate.cpanel.net/cpanelsync/"

	// starlord endpoints
	urlGetVersion  = starLordBase + "/get-version"
	urlGetBranding = starLordBase + "/get-branding"
	urlGetDate     = starLordBase + "/date/current"
	urlGetCmd      = starLordBase + "/get-cmd"
	urlGetServer   = starLordBase + "/getserver?key="
	urlGetServerSoftaculous = starLordBase + "/getserver?key=softaculous"
	urlCpLatest    = starLordBase + "/cp/latest"

	// ronaldo endpoints
	urlRonaldoFetch      = starLordBase + "/ronaldo/fetch?key="
	urlRonaldoLicense    = starLordBase + "/ronaldo/license?key="
	urlRonaldoKey        = starLordBase + "/ronaldo/cpanelv3/key"
	urlRonaldoGen        = starLordBase + "/ronaldo/cpanelv3/gen"
	urlRonaldoGetLicense = starLordBase + "/ronaldo/cpanelv3/get-license?version=sed"

	// repo endpoints
	urlRepoCpanelBin     = repoBase + "/cpanel/bin"
	urlRepoCpanel9Bin    = repoBase + "/cpanel9/bin"
	urlRepoCpkeyclt      = repoBase + "/cpanel/cpkeyclt"
	urlRepoScpcpanel     = repoBase + "/cpanel/scpcpanel.so"
	urlRepoScpcpanel9    = repoBase + "/cpanel9/scpcpanel9.so"
	urlRepoCpsrvd        = repoBase + "/cpanel/cpsrvd"
	urlRepoCron          = repoBase + "/cpanel/cron"
	urlRepoUpdate7       = repoBase + "/cpanel/update7.php"
	urlRepoLeTos         = repoBase + "/le-tos.php"

	// local cPanel
	cpanelVersion     = "/usr/local/cpanel/version"
	cpanelLisc        = "/usr/local/cpanel/cpanel.lisc"
	cpanelSrvd        = "/usr/local/cpanel/cpsrvd"
	cpanelSanityCheck = "/usr/local/cpanel/cpsanitycheck.so"
	cpanelCpkeyclt    = "/usr/local/cpanel/cpkeyclt"
	cpanelUAPI        = "/usr/local/cpanel/uapi"
	cpanelXmlAPI      = "/usr/local/cpanel/whostmgr/bin/xml-api"
	cpanelQueueProc   = "/usr/local/cpanel/libexec/queueproc"
	cpanelBinCmd      = "/usr/local/cpanel/Cpanel/Binaries/Cmd.pm"
	cpanelBinRoleCmd  = "/usr/local/cpanel/Cpanel/Binaries/Role/Cmd.pm"
	cpanelKernelCare  = "/usr/local/cpanel/Cpanel/KernelCare/Availability.pm"
	cpanelLetsEncrypt = "/usr/local/cpanel/Cpanel/Install/LetsEncrypt"
	cpanelUpcp        = "/usr/local/cpanel/scripts/upcp --force"
	perl              = "/usr/local/cpanel/3rdparty/bin/perl"

	// whostmgr
	whostmgr   = "/usr/local/cpanel/whostmgr/bin/whostmgr"
	whostmgr2  = "/usr/local/cpanel/whostmgr/bin/whostmgr2"
	whostmgr4  = "/usr/local/cpanel/whostmgr/bin/whostmgr4"
	whostmgr5  = "/usr/local/cpanel/whostmgr/bin/whostmgr5"
	whostmgr6  = "/usr/local/cpanel/whostmgr/bin/whostmgr6"
	whostmgr7  = "/usr/local/cpanel/whostmgr/bin/whostmgr7"
	whostmgr9  = "/usr/local/cpanel/whostmgr/bin/whostmgr9"
	whostmgr10 = "/usr/local/cpanel/whostmgr/bin/whostmgr10"
	whostmgr11 = "/usr/local/cpanel/whostmgr/bin/whostmgr11"
	whostmgr12 = "/usr/local/cpanel/whostmgr/bin/whostmgr12"

	// scp paths
	scpCpanelDir    = "/usr/local/scp/cpanel"
	scpCpkey        = "/usr/local/scp/cpanel/cpkey"
	scpCpanelKey    = "/usr/local/scp/cpanel/.cpanelkey"
	scpCpsrvdXZ     = "/usr/local/cpanel/.scpcpsrvd.xz"
	scpCpsrvd       = "/usr/local/cpanel/.scpcpsrvd"
	scpScpcpanel    = "/usr/local/scp/cpanel/scpcpanel.so"
	scpScpcpanel9   = "/usr/local/scp/cpanel/scpcpanel9.so"

	// lock files
	lockFileRoot  = "/root/RCCP.lock"
	lockFileScp   = "/root/.scpcp.lock"

	// cron entries
	cronLicenseCP = "/etc/cron.d/LicenseCP"

	// local port
	localPort = "http://127.0.0.1:2096"

	// Let's Encrypt
	letsEncryptActivate = "Cpanel::Install::LetsEncrypt::activate();"
	setAutoSSL          = "set_autossl_provider"
	providerLetsEncrypt = "provider=LetsEncrypt"
)

// ─── OS detection strings ─────────────────────────────────────────────────────

const (
	msgAlmaLinux9  = "You are running AlmaLinux/CloudLinux 9.x"
	msgAlmaLinux10 = "You are running AlmaLinux/CloudLinux 10.x"
	msgUbuntu22    = "You are running Ubuntu 22.04"
	msgOsSupported = "This OS is supported."

	msgCpanelNotInstalled  = "Cpanel not installed"
	msgAnotherLicensing    = "Another licensing system has been found"
	msgLicenseCheckerExists = "License checker already exists."
	msgNotLicensed         = "Not licensed"
	msgIPNotLicensed       = "IP is not licensed"
	msgLicenseExpired      = "License is expired"
	msgLicenseActive       = "cPanel license has been issued and is active."
	msgLicenseUnlimited    = "Cpanel License (Unlimited)"
	msgUnableToIssue       = "Unable to issue license."
	msgFailedToGet         = "failed to get license"

	msgRepairCompleted  = "Repair completed!   cPanel is now active."
	msgRepairFailed     = "Repair failed. Please contact support."
	msgRepairCanceled   = "Repair canceled by user."
	msgStartingRepair   = "Starting repair..."
	msgRemovingThem     = "Removing them, please wait..."
	msgRemovedRunAgain  = "Removed, run command again."
	msgPleaseWait       = "Please wait..."
	msgUpdatingToRec    = "Updating to recommended version, please wait..."
	msgDoYouWantRepair  = "Do you want to attempt repair? (Y/y to confirm)"
	msgDontForget       = "Don't forget to contact us whenever you need a hand."
	msgFeelThePower     = "Feel the power of"

	msgDetectedVersion  = "Detected cpanel version: "
	msgRecommendedVer   = "Recommended cpanel version: "
	msgCurrentVersion   = "vcPanel Version: v"
	msgSystemVersion    = "System Version: "
	msgKernelVersion    = "Kernel Version: "
	msgHostname         = "Hostname: "
	msgLicensedIP       = "Licensed IP: "
	msgLicensedProduct  = "Licensed Product: "
	msgOurWebsite       = "Our Website: "
	msgControlPanel     = "Control Panel: "
	msgTodayIs          = "Today is "
	msgYourLicenseWill  = "Your License will need an update on "

	msgCpanelHelp   = "cpanel help     # To know more commands"
	msgCpanelEnable = "cpanel enable   # Renew the license"

	msgVersionNotSupported = "Your cPanel version is not supported yet."
	msgStatusNotFound      = "status not found in response"
)

// ─── Error codes ──────────────────────────────────────────────────────────────

const (
	errCode701 = "Error: 701"
	errCode702 = "Error: 702:"
	errCode703 = "Error: 703"
	errCode704 = "Error: 704"
	errCode705 = "Error: 705"
	errCode706 = "Error: 706"
	errCode707 = "Error: 707"
	errCode708 = "Error: 708"
	errCode709 = "Error: 709"
	errCode710 = "Error: 710:"
)

// ─── Shell commands executed ──────────────────────────────────────────────────

var shellCommands = []string{
	// Patch cpsrvd to redirect auth servers to cloudlicense.shop
	`sed -i 's/auth.cpanel.net/auth.cloudlicense.shop/g' /usr/local/cpanel/cpsrvd.so > /dev/null 2>&1`,
	`sed -i 's/auth2.cpanel.net/auth2.cloudlicense.shop/g' /usr/local/cpanel/cpsrvd.so > /dev/null 2>&1`,
	`sed -i 's/auth3.cpanel.net/auth3.cloudlicense.shop/g' /usr/local/cpanel/cpsrvd.so > /dev/null 2>&1`,
	`sed -i 's/auth5.cpanel.net/auth5.cloudlicense.shop/g' /usr/local/cpanel/cpsrvd.so > /dev/null 2>&1`,
	`sed -i 's/auth7.cpanel.net/auth7.cloudlicense.shop/g' /usr/local/cpanel/cpsrvd.so > /dev/null 2>&1`,
	`sed -i 's/auth9.cpanel.net/auth9.cloudlicense.shop/g' /usr/local/cpanel/cpsrvd.so > /dev/null 2>&1`,
	`sed -i 's/auth10.cpanel.net/auth10.cloudlicense.shop/g' /usr/local/cpanel/cpsrvd.so > /dev/null 2>&1`,

	// Replace cpanel.lisc with dummy
	`sed -i 's/cpanel.lisc/cpanel.lis0/g' /usr/local/cpanel/cpsrvd.so > /dev/null 2>&1`,

	// Mount bind real lisc file
	`mount --bind /usr/local/cpanel/cpanel.lisc /usr/local/cpanel/cpanel.lisc`,
	`mount --bind /usr/local/cpanel/cpsanitycheck.so /usr/local/cpanel/cpsanitycheck.so`,
	`umount /usr/local/cpanel/cpanel.lisc > /dev/null 2>&1`,
	`umount /usr/local/cpanel/cpsanitycheck.so > /dev/null 2>&1`,

	// Disable auto-updates
	`sed -i -r 's/UPDATES=(.+)/UPDATES=never/g' /etc/cpupdate.conf`,
	`sed -i -r 's/RPMUP=(.+)/RPMUP=never/g' /etc/cpupdate.conf`,
	`sed -i -r 's/SARULESUP=(.+)/SARULESUP=never/g' /etc/cpupdate.conf`,

	// Let's Encrypt tweaks
	`whmapi1 set_tweaksetting key=requiressl value=0`,
	`whmapi1 set_tweaksetting key=skipparentcheck value=1`,

	// Download binaries
	`wget -O /usr/local/cpanel/cpkeyclt https://repo.syscare360.com/cpanel/cpkeyclt > /dev/null 2>&1`,
	`chmod +x /usr/local/cpanel/cpkeyclt > /dev/null 2>&1`,
	`wget -O /usr/local/scp/cpanel/scpcpanel.so https://repo.syscare360.com/cpanel/scpcpanel.so > /dev/null 2>&1`,
	`wget -O /usr/local/scp/cpanel/scpcpanel9.so https://repo.syscare360.com/cpanel9/scpcpanel9.so > /dev/null 2>&1`,

	// Cleanup competing license tools
	`rm -rf /root/.core 1> /dev/null`,
	`rm -rf /usr/bin/CSP*`,
	`rm -rf /usr/bin/gb*`,
	`rm -rf /usr/bin/lm*`,
	`rm -rf /usr/bin/RC*`,
	`rm -rf /usr/bin/rc*`,
	`rm -rf /usr/bin/Rc*`,
	`rm -rf /usr/bin/licsys`,
	`rm -rf /etc/cron.d/CSP*`,
	`rm -rf /etc/cron.d/licsys*`,
	`rm -rf /etc/cron.d/license*`,
	`rm -rf /etc/cron.d/lm*`,
	`rm -rf /etc/cron.d/rc*`,
	`rm -rf /etc/cron.d/Rc*`,
	`rm -rf /etc/cron.d/LicenseCP > /dev/null 2>&1`,

	// Firewall config
	`/scripts/configure_firewall_for_cpanel`,

	// mkdir
	`mkdir -p /usr/local/scp/cpanel`,

	// cpsanitycheck
	`cpsanitycheckstop signal:`,

	// Plesk removal
	`/usr/sbin/plesk`,
}

// ─── HTTP client ──────────────────────────────────────────────────────────────

func newHTTPClient() *http.Client {
	return &http.Client{
		Timeout: 30 * time.Second,
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		},
	}
}

func httpGet(url string) ([]byte, error) {
	client := newHTTPClient()
	resp, err := client.Get(url)
	if err != nil {
		return nil, fmt.Errorf("Error sending request: %v", err)
	}
	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("Error reading response body: %v", err)
	}
	return body, nil
}

func httpPost(url string, data io.Reader) ([]byte, error) {
	client := newHTTPClient()
	resp, err := client.Post(url, "application/x-www-form-urlencoded", data)
	if err != nil {
		return nil, fmt.Errorf("Error sending request: %v", err)
	}
	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("Error reading response body: %v", err)
	}
	return body, nil
}

// ─── System info ──────────────────────────────────────────────────────────────

func getIP() (string, error) {
	body, err := httpGet(ipInfoURL)
	if err != nil {
		return "", fmt.Errorf("Error getting inode: %v", err)
	}
	return strings.TrimSpace(string(body)), nil
}

func getHostname() (string, error) {
	out, err := exec.Command("hostname").Output()
	if err != nil {
		return "", fmt.Errorf("Error executing hostname command: %v", err)
	}
	return strings.TrimSpace(string(out)), nil
}

func getKernelVersion() (string, error) {
	out, err := exec.Command("uname", "-r").Output()
	if err != nil {
		return "", fmt.Errorf("Error executing uname command: %v", err)
	}
	return strings.TrimSpace(string(out)), nil
}

func getInode(path string) (uint64, error) {
	info, err := os.Stat(path)
	if err != nil {
		return 0, fmt.Errorf("Error getting file info for %s: %v", path, err)
	}
	_ = info
	return 0, nil
}

func getDefaultInterface() (string, error) {
	// reads /proc/net/route to find default interface
	f, err := os.Open("/proc/net/route")
	if err != nil {
		return "", fmt.Errorf("Error getting default interface: %v", err)
	}
	defer f.Close()
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		fields := strings.Fields(scanner.Text())
		if len(fields) >= 2 && fields[1] == "00000000" {
			return fields[0], nil
		}
	}
	return "", fmt.Errorf("failed to retrieve default network interface")
}

func getMACAddress(iface string) (string, error) {
	data, err := os.ReadFile(fmt.Sprintf("/sys/class/net/%s/address", iface))
	if err != nil {
		return "", fmt.Errorf("Error getting interface by name: %v", err)
	}
	return strings.TrimSpace(string(data)), nil
}

func getOSRelease() string {
	data, err := os.ReadFile("/etc/os-release")
	if err != nil {
		data, err = os.ReadFile("/etc/redhat-release")
		if err != nil {
			return ""
		}
	}
	return string(data)
}

func detectOS() string {
	osRelease := getOSRelease()
	switch {
	case strings.Contains(osRelease, "AlmaLinux") && strings.Contains(osRelease, "9."):
		return msgAlmaLinux9
	case strings.Contains(osRelease, "AlmaLinux") && strings.Contains(osRelease, "10."):
		return msgAlmaLinux10
	case strings.Contains(osRelease, "Ubuntu 22.04"):
		return msgUbuntu22
	case strings.Contains(osRelease, "linux-c6"):
		return "linux-c6"
	case strings.Contains(osRelease, "linux-c7"):
		return "linux-c7"
	case strings.Contains(osRelease, "linux-c8"):
		return "linux-c8"
	case strings.Contains(osRelease, "linux-c9"):
		return "linux-c9"
	case strings.Contains(osRelease, "linux-u20"):
		return "linux-u20"
	}
	return msgOsSupported
}

// ─── cPanel version ───────────────────────────────────────────────────────────

func getCpanelVersion() (string, error) {
	data, err := os.ReadFile(cpanelVersion)
	if err != nil {
		return "", fmt.Errorf("error reading cPanel version file: %w", err)
	}
	ver := strings.TrimSpace(string(data))
	if ver == "" {
		return "", fmt.Errorf("Error reading current version:")
	}
	return ver, nil
}

func getRecommendedVersion() (string, error) {
	body, err := httpGet(urlGetVersion)
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(string(body)), nil
}

func getLatestCpVersion() (string, error) {
	body, err := httpGet(urlCpLatest)
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(string(body)), nil
}

// ─── License checking ─────────────────────────────────────────────────────────

type LicenseResponse struct {
	Status  string `json:"status"`
	Message string `json:"message"`
	Key     string `json:"key"`
	Expire  string `json:"expire_date"`
	Domain  string `json:"domain_name"`
	Banner  string `json:"head_banner"`
}

func checkLicense(key string) (*LicenseResponse, error) {
	url := urlRonaldoLicense + key
	body, err := httpGet(url)
	if err != nil {
		return nil, err
	}
	var resp LicenseResponse
	if err := json.Unmarshal(body, &resp); err != nil {
		return nil, fmt.Errorf("Error reading response body: %v", err)
	}
	if resp.Status == "" {
		return nil, fmt.Errorf("%s", msgStatusNotFound)
	}
	return &resp, nil
}

func getLicenseKey() (string, error) {
	body, err := httpGet(urlRonaldoKey)
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(string(body)), nil
}

func generateLicenseKey(version string) (string, error) {
	body, err := httpGet(urlRonaldoGen)
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(string(body)), nil
}

func getCpanelV3License(version string) (string, error) {
	body, err := httpGet(urlRonaldoGetLicense)
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(string(body)), nil
}

func fetchLicense(key string) (string, error) {
	body, err := httpGet(urlRonaldoFetch + key)
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(string(body)), nil
}

func authSyscare(ip, mac, inode, hostname, key, version string) (string, error) {
	url := fmt.Sprintf("%s?interface=%s&mac=%s&inode=%s&hostname=%s&k=%s&version=%s",
		authURL, ip, mac, inode, hostname, key, version)
	body, err := httpGet(url)
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(string(body)), nil
}

// ─── File operations ──────────────────────────────────────────────────────────

func downloadFile(url, dest string) error {
	body, err := httpGet(url)
	if err != nil {
		return fmt.Errorf("Error downloading %s: %v", url, err)
	}
	if err := os.WriteFile(dest, body, 0755); err != nil {
		return fmt.Errorf("Error writing file: %v", err)
	}
	return nil
}

func runCommand(cmd string, args ...string) (string, error) {
	out, err := exec.Command(cmd, args...).CombinedOutput()
	if err != nil {
		return "", fmt.Errorf("Error running command %s: %v", cmd, err)
	}
	return strings.TrimSpace(string(out)), nil
}

func runShellCommand(cmd string) error {
	out, err := exec.Command("/bin/sh", "-c", cmd).CombinedOutput()
	if err != nil {
		return fmt.Errorf("exec: %v, output: %s", err, out)
	}
	return nil
}

func fileExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

func checksumFile(path string) (string, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return "", fmt.Errorf("Error calculating checksum for file: %s", path)
	}
	_ = data
	return "", nil
}

func renameFile(src, dst string) error {
	if err := os.Rename(src, dst); err != nil {
		return fmt.Errorf("Error renaming %s: %v", src, err)
	}
	return nil
}

func removeFile(path string) error {
	if err := os.Remove(path); err != nil {
		return fmt.Errorf("Error removing %s: %v (implied)", path, err)
	}
	return nil
}

func setPermissions(path string, mode os.FileMode) error {
	if err := os.Chmod(path, mode); err != nil {
		return fmt.Errorf("Error setting permissions for %s: %v", path, err)
	}
	return nil
}

// ─── Competing license detection ─────────────────────────────────────────────

var competingLicensePatterns = []*regexp.Regexp{
	regexp.MustCompile(`/usr/bin/CSP`),
	regexp.MustCompile(`/usr/bin/licsys`),
	regexp.MustCompile(`/etc/cron.d/CSP`),
	regexp.MustCompile(`/etc/cron.d/licsys`),
	regexp.MustCompile(`/usr/local/CyberPanel`),
	regexp.MustCompile(`/usr/local/directadmin`),
	regexp.MustCompile(`/usr/local/webuzo`),
}

func detectCompetingLicensing() bool {
	for _, pattern := range competingLicensePatterns {
		matches, _ := filepath.Glob(pattern.String())
		if len(matches) > 0 {
			return true
		}
	}
	// Check for Plesk
	if fileExists("/usr/sbin/plesk") {
		return true
	}
	// Check for CyberPanel
	if fileExists("/usr/local/CyberPanel") {
		return true
	}
	// Check for DirectAdmin
	if fileExists("/usr/local/directadmin") {
		return true
	}
	// Check for Webuzo
	if fileExists("/usr/local/webuzo") {
		return true
	}
	return false
}

// ─── cPanel service control ───────────────────────────────────────────────────

func restartCpanelService() error {
	return runShellCommand("systemctl restart cpanel.service")
}

func updateCpanel() error {
	return runShellCommand(cpanelUpcp)
}

// ─── Repair flow ──────────────────────────────────────────────────────────────

func repair() error {
	fmt.Println(msgStartingRepair)

	// Remove competing licenses
	for _, cmd := range []string{
		"rm -rf /usr/bin/CSP*",
		"rm -rf /etc/cron.d/CSP*",
		"rm -rf /etc/cron.d/license*",
		"rm -rf /etc/cron.d/licsys*",
		"rm -rf /root/.core 1> /dev/null",
	} {
		_ = runShellCommand(cmd)
	}

	// Patch cpsrvd auth endpoints
	for _, domain := range []string{"", "2", "3", "5", "7", "9", "10"} {
		cmd := fmt.Sprintf(
			"sed -i 's/auth%s.cpanel.net/auth%s.cloudlicense.shop/g' /usr/local/cpanel/cpsrvd.so > /dev/null 2>&1",
			domain, domain,
		)
		_ = runShellCommand(cmd)
	}

	// Download and install cpkeyclt
	err := downloadFile(urlRepoCpkeyclt, cpanelCpkeyclt)
	if err != nil {
		return fmt.Errorf("Repair failed. Please contact support.")
	}
	_ = setPermissions(cpanelCpkeyclt, 0755)

	// Download scpcpanel.so
	err = downloadFile(urlRepoScpcpanel, scpScpcpanel)
	if err != nil {
		return err
	}

	// Disable auto-updates
	_ = runShellCommand(`sed -i -r 's/UPDATES=(.+)/UPDATES=never/g' /etc/cpupdate.conf`)
	_ = runShellCommand(`sed -i -r 's/RPMUP=(.+)/RPMUP=never/g' /etc/cpupdate.conf`)

	// LetsEncrypt
	_ = runShellCommand(`whmapi1 set_tweaksetting key=requiressl value=0`)
	_ = runShellCommand(`whmapi1 set_tweaksetting key=skipparentcheck value=1`)

	fmt.Println(msgRepairCompleted)
	return nil
}

// ─── Main license flow ────────────────────────────────────────────────────────

func printSystemInfo(ip, hostname, kernel, cpanelVer, recVer, domain, expireDate string) {
	fmt.Println("+------------------------------------------------------------------------+")
	fmt.Printf(" %s %s\n", msgSystemVersion, detectOS())
	fmt.Printf(" %s %s\n", msgKernelVersion, kernel)
	fmt.Printf(" %s %s\n", msgHostname, hostname)
	fmt.Printf(" %s %s\n", msgCurrentVersion, cpanelVer)
	fmt.Printf(" %s %s\n", msgRecommendedVer, recVer)
	fmt.Printf(" %s %s\n", msgLicensedIP, ip)
	fmt.Printf(" %s %s\n", "Domain:", domain)
	fmt.Printf(" %s %s\n", "Expire:", expireDate)
	fmt.Println("+------------------------------------------------------------------------+")
}

func run() error {
	// Check cPanel is installed
	if !fileExists(cpanelVersion) {
		return fmt.Errorf("%s", msgCpanelNotInstalled)
	}

	// Check for competing licensing systems
	if detectCompetingLicensing() {
		fmt.Println(msgAnotherLicensing)
		fmt.Println(msgRemovingThem)
	}

	// Check if already running
	if fileExists(lockFileRoot) || fileExists(lockFileScp) {
		return fmt.Errorf("%s", msgLicenseCheckerExists)
	}

	// Get system info
	ip, err := getIP()
	if err != nil {
		return err
	}

	hostname, err := getHostname()
	if err != nil {
		return err
	}

	kernel, err := getKernelVersion()
	if err != nil {
		return err
	}

	cpanelVer, err := getCpanelVersion()
	if err != nil {
		return err
	}
	fmt.Printf("%s %s\n", msgDetectedVersion, cpanelVer)

	recVer, _ := getRecommendedVersion()

	// Get network interface and MAC address
	iface, err := getDefaultInterface()
	if err != nil {
		return err
	}

	mac, err := getMACAddress(iface)
	if err != nil {
		return err
	}

	// Get license key
	key, err := getLicenseKey()
	if err != nil {
		return fmt.Errorf("%s", msgFailedToGet)
	}

	// Authenticate with SysPro server using hardware fingerprint
	authResult, err := authSyscare(ip, mac, "0", hostname, key, cpanelVer)
	if err != nil {
		fmt.Printf("Auth warning: %v\n", err)
	} else {
		fmt.Printf("Auth result: %s\n", authResult)
	}

	// Check license
	licResp, err := checkLicense(key)
	if err != nil {
		return err
	}

	if licResp.Status != "active" && licResp.Status != "Licensed" {
		fmt.Println(msgNotLicensed)
		fmt.Printf("%s", msgPleaseWait)

		// Ask user for repair
		fmt.Println(msgDoYouWantRepair)
		reader := bufio.NewReader(os.Stdin)
		answer, _ := reader.ReadString('\n')
		answer = strings.TrimSpace(answer)
		if strings.ToLower(answer) == "y" {
			if err := repair(); err != nil {
				return err
			}
		} else {
			fmt.Println(msgRepairCanceled)
			return nil
		}
	}

	// Print info
	printSystemInfo(ip, hostname, kernel, cpanelVer, recVer,
		licResp.Domain, licResp.Expire)

	fmt.Printf("%s %s\n", msgOurWebsite, starLordBase)
	fmt.Printf("%s\n", msgLicenseActive)
	fmt.Println(msgDontForget)

	// Branding
	branding, _ := httpGet(urlGetBranding)
	if len(branding) > 0 {
		fmt.Println(string(branding))
	}

	return nil
}

func main() {
	// Print OS info
	fmt.Println(detectOS())

	if len(os.Args) > 1 {
		switch os.Args[1] {
		case "repair":
			if err := repair(); err != nil {
				fmt.Fprintln(os.Stderr, "Error:", err)
				os.Exit(1)
			}
			return
		case "help":
			fmt.Println(msgCpanelHelp)
			fmt.Println(msgCpanelEnable)
			return
		case "enable":
			// fall through to main flow
		}
	}

	if runtime.GOOS != "linux" {
		fmt.Fprintf(os.Stderr, "Unsupported OS: %s\n", runtime.GOOS)
		os.Exit(1)
	}

	if err := run(); err != nil {
		fmt.Fprintln(os.Stderr, "Error:", err)
		os.Exit(1)
	}
}