package main

import (
	"bufio"
	"context"
	"fmt"
	"io"
	"os/exec"
	"regexp"
	"strings"
	"time"
)

// Run an external program
func RunProgram(program string, args []string, timeout time.Duration) {
	// Flag if we are starting or stopping a container
	managingContainer := program == "docker"

	// Create context and command
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()
	cmd := exec.CommandContext(ctx, program, args...)

	// Stringify and censor the program's arguments
	strArgs := redactString(strings.Join(args, " "))
	fmt.Printf("  [+] Running %s %s\n", program, strArgs)

	// Setup pipes
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		panic(err)
	}

	stderr, err := cmd.StderrPipe()
	if err != nil {
		panic(err)
	}

	// Start the command
	if err := cmd.Start(); err != nil {
		panic(err)
	}

	// Stream any messages to the terminal
	for _, r := range []io.Reader{stdout, stderr} {
		// Don't log stdout from the container
		if managingContainer && r == stdout {
			continue
		}

		scanner := bufio.NewScanner(r)
		scanner.Split(bufio.ScanRunes)

		for scanner.Scan() {
			fmt.Print(scanner.Text())
		}
	}
}

// Redacts sensitive command line arguments.
func redactString(raw string) string {
	redactionRegex := regexp.MustCompilePOSIX(`-(user|pass|key) [^ ]+`)
	return redactionRegex.ReplaceAllString(raw, "-$1 REDACTED")
}