diff --git a/internal/api/room/handler.go b/internal/api/room/handler.go index 3d7026f7..247d3a14 100644 --- a/internal/api/room/handler.go +++ b/internal/api/room/handler.go @@ -69,6 +69,7 @@ func (h *RoomHandler) Route(r chi.Router) { r.With(h.uploadMiddleware).Route("/upload", func(r chi.Router) { r.Post("/drop", h.uploadDrop) + r.Post("/dialog", h.uploadDialog) }) } diff --git a/internal/api/room/upload.go b/internal/api/room/upload.go index e98d5ff3..cec1d8e3 100644 --- a/internal/api/room/upload.go +++ b/internal/api/room/upload.go @@ -86,3 +86,65 @@ func (h *RoomHandler) uploadDrop(w http.ResponseWriter, r *http.Request) { utils.HttpSuccess(w) } + + +func (h *RoomHandler) uploadDialog(w http.ResponseWriter, r *http.Request) { + r.ParseMultipartForm(MAX_UPLOAD_SIZE) + + if r.MultipartForm == nil { + utils.HttpBadRequest(w, "No MultipartForm received.") + return + } + + defer r.MultipartForm.RemoveAll() + + if !h.desktop.IsFileChooserDialogOpen() { + utils.HttpBadRequest(w, "Open file chooser dialog first.") + return + } + + req_files := r.MultipartForm.File["files"] + if len(req_files) == 0 { + utils.HttpBadRequest(w, "No files received.") + return + } + + dir, err := ioutil.TempDir("", "neko-dialog-*") + if err != nil { + utils.HttpInternalServerError(w, err) + return + } + + for _, req_file := range req_files { + path := path.Join(dir, req_file.Filename) + + srcFile, err := req_file.Open() + if err != nil { + utils.HttpInternalServerError(w, err) + return + } + + defer srcFile.Close() + + dstFile, err := os.OpenFile(path, os.O_APPEND | os.O_CREATE | os.O_WRONLY, 0644) + if err != nil { + utils.HttpInternalServerError(w, err) + return + } + + defer dstFile.Close() + + _, err = io.Copy(dstFile, srcFile) + if err != nil { + utils.HttpInternalServerError(w, err) + return + } + } + + if err := h.desktop.HandleFileChooserDialog(dir); err != nil { + utils.HttpInternalServerError(w, "Unable to handle file chooser dialog.") + return + } + + utils.HttpSuccess(w) +} diff --git a/internal/desktop/filechooser.go b/internal/desktop/filechooser.go new file mode 100644 index 00000000..9fc25af8 --- /dev/null +++ b/internal/desktop/filechooser.go @@ -0,0 +1,58 @@ +package desktop + +import ( + "os/exec" +) + +func (manager *DesktopManagerCtx) HandleFileChooserDialog(uri string) error { + mu.Lock() + defer mu.Unlock() + + // TOOD: Use native API. + cmd := exec.Command( + "xdotool", + "search", "--name", "Open", "windowfocus", + "sleep", "0.2", + "key", "--clearmodifiers", "ctrl+l", + "type", "--args", "1", uri + "//", + "key", "--clearmodifiers", "Return", + "sleep", "1", + "key", "--clearmodifiers", "Down", + "key", "--clearmodifiers", "ctrl+a", + "key", "--clearmodifiers", "Return", + "sleep", "0.3", + ) + + _, err := cmd.Output() + return err +} + +func (manager *DesktopManagerCtx) CloseFileChooserDialog() error { + mu.Lock() + defer mu.Unlock() + + // TOOD: Use native API. + cmd := exec.Command( + "xdotool", + "search", "--name", "Open", "windowfocus", + "sleep", "0.2", + "key", "--clearmodifiers", "alt+f4", + ) + + _, err := cmd.Output() + return err +} + +func (manager *DesktopManagerCtx) IsFileChooserDialogOpen() bool { + mu.Lock() + defer mu.Unlock() + + // TOOD: Use native API. + cmd := exec.Command( + "xdotool", + "search", "--name", "Open", "windowfocus", + ) + + _, err := cmd.Output() + return err == nil +} diff --git a/internal/types/desktop.go b/internal/types/desktop.go index a0aaad3b..7fa60538 100644 --- a/internal/types/desktop.go +++ b/internal/types/desktop.go @@ -66,4 +66,9 @@ type DesktopManager interface { // drop DropFiles(x int, y int, files []string) bool + + // filechooser + HandleFileChooserDialog(uri string) error + CloseFileChooserDialog() error + IsFileChooserDialogOpen() bool }