Files
oneterm/backend/internal/guacd/file_instruction.go
2025-06-08 11:21:29 +08:00

122 lines
3.6 KiB
Go

package guacd
import (
"fmt"
"strconv"
)
// File transfer instruction constants
const (
INSTRUCTION_FILE_UPLOAD = "file-upload"
INSTRUCTION_FILE_DOWNLOAD = "file-download"
INSTRUCTION_FILE_DATA = "file-data"
INSTRUCTION_FILE_ACK = "file-ack"
INSTRUCTION_FILE_COMPLETE = "file-complete"
INSTRUCTION_FILE_ERROR = "file-error"
)
// Object instruction constants for filesystem operations
const (
INSTRUCTION_FILESYSTEM = "filesystem"
INSTRUCTION_GET = "get"
INSTRUCTION_PUT = "put"
INSTRUCTION_BODY = "body"
INSTRUCTION_UNDEFINE = "undefine"
)
// Stream instruction constants
const (
INSTRUCTION_BLOB = "blob"
INSTRUCTION_END = "end"
)
// Filesystem mimetypes
const (
MIMETYPE_STREAM_INDEX = "application/vnd.glyptodon.guacamole.stream-index+json"
MIMETYPE_TEXT_PLAIN = "text/plain"
)
// HandleFileInstruction processes file transfer related instructions
func (t *Tunnel) HandleFileInstruction(instruction *Instruction) (*Instruction, error) {
switch instruction.Opcode {
case INSTRUCTION_FILE_UPLOAD:
if len(instruction.Args) < 2 {
return NewInstruction(INSTRUCTION_FILE_ERROR, "Invalid upload request"), nil
}
filename := instruction.Args[0]
size, err := strconv.ParseInt(instruction.Args[1], 10, 64)
if err != nil {
return NewInstruction(INSTRUCTION_FILE_ERROR, "Invalid file size"), nil
}
transferId, err := t.HandleFileUpload(filename, size)
if err != nil {
return NewInstruction(INSTRUCTION_FILE_ERROR, err.Error()), nil
}
return NewInstruction(INSTRUCTION_FILE_ACK, transferId), nil
case INSTRUCTION_FILE_DOWNLOAD:
if len(instruction.Args) < 1 {
return NewInstruction(INSTRUCTION_FILE_ERROR, "Invalid download request"), nil
}
filename := instruction.Args[0]
transferId, size, err := t.HandleFileDownload(filename)
if err != nil {
return NewInstruction(INSTRUCTION_FILE_ERROR, err.Error()), nil
}
// Send acknowledgement with transfer ID and file size
ackInstr := NewInstruction(INSTRUCTION_FILE_ACK, transferId, strconv.FormatInt(size, 10))
if _, err := t.WriteInstruction(ackInstr); err != nil {
return NewInstruction(INSTRUCTION_FILE_ERROR, fmt.Sprintf("Failed to send ACK: %s", err.Error())), nil
}
// Start file download process in a new goroutine
go func() {
if err := t.SendDownloadData(transferId); err != nil {
// Log error, but we can't send error instruction here as it would interfere with protocol
fmt.Printf("Download failed: %s\n", err.Error())
}
}()
// Return nil to avoid sending another response
return nil, nil
case INSTRUCTION_FILE_DATA:
if len(instruction.Args) < 2 {
return NewInstruction(INSTRUCTION_FILE_ERROR, "Invalid data request"), nil
}
transferId := instruction.Args[0]
data := []byte(instruction.Args[1])
// If this is an upload, write the data
n, err := t.WriteFileData(transferId, data)
if err != nil {
return NewInstruction(INSTRUCTION_FILE_ERROR, fmt.Sprintf("Write error: %s", err.Error())), nil
}
return NewInstruction(INSTRUCTION_FILE_ACK, transferId, strconv.Itoa(n)), nil
case INSTRUCTION_FILE_COMPLETE:
if len(instruction.Args) < 1 {
return NewInstruction(INSTRUCTION_FILE_ERROR, "Invalid complete request"), nil
}
transferId := instruction.Args[0]
err := t.CloseFileTransfer(transferId)
if err != nil {
return NewInstruction(INSTRUCTION_FILE_ERROR, fmt.Sprintf("Failed to complete transfer: %s", err.Error())), nil
}
return NewInstruction(INSTRUCTION_FILE_ACK, transferId, "complete"), nil
default:
return nil, fmt.Errorf("Unknown file instruction: %s", instruction.Opcode)
}
}