From 8f26c31df640260c27358f25230d74bd16444bde Mon Sep 17 00:00:00 2001 From: Caleb <57644769+pressstartearly@users.noreply.github.com> Date: Tue, 15 Dec 2020 22:59:18 -0500 Subject: [PATCH] Support downloading remote files to a server via the API Co-authored-by: Dane Everitt --- router/middleware.go | 30 +++++++++++++++++------ router/router.go | 1 + router/router_server_files.go | 45 +++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 7 deletions(-) diff --git a/router/middleware.go b/router/middleware.go index ce9f0f4..6135604 100644 --- a/router/middleware.go +++ b/router/middleware.go @@ -1,6 +1,7 @@ package router import ( + "errors" "github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/pterodactyl/wings/config" @@ -60,6 +61,9 @@ func AuthorizationMiddleware(c *gin.Context) { } // Helper function to fetch a server out of the servers collection stored in memory. +// +// This function should not be used in new controllers, prefer ExtractServer where +// possible. func GetServer(uuid string) *server.Server { return server.GetServers().Find(func(s *server.Server) bool { return uuid == s.Id() @@ -70,12 +74,24 @@ func GetServer(uuid string) *server.Server { // locate it. func ServerExists(c *gin.Context) { u, err := uuid.Parse(c.Param("server")) - if err != nil || GetServer(u.String()) == nil { - c.AbortWithStatusJSON(http.StatusNotFound, gin.H{ - "error": "The resource you requested does not exist.", - }) - return + if err == nil { + if s := GetServer(u.String()); s != nil { + c.Set("server", s) + c.Next() + return + } } - - c.Next() + c.AbortWithStatusJSON(http.StatusNotFound, gin.H{ + "error": "The resource you requested does not exist.", + }) +} + +// Returns the server instance from the gin context. If there is no server set in the +// context (e.g. calling from a controller not protected by ServerExists) this function +// will panic. +func ExtractServer(c *gin.Context) *server.Server { + if s, ok := c.Get("server"); ok { + return s.(*server.Server) + } + panic(errors.New("cannot extract server, missing on gin context")) } diff --git a/router/router.go b/router/router.go index 824acfa..3c0f4ff 100644 --- a/router/router.go +++ b/router/router.go @@ -83,6 +83,7 @@ func Configure() *gin.Engine { files.PUT("/rename", putServerRenameFiles) files.POST("/copy", postServerCopyFile) files.POST("/write", postServerWriteFile) + files.POST("/writeUrl", postServerDownloadRemoteFile) files.POST("/create-directory", postServerCreateDirectory) files.POST("/delete", postServerDeleteFiles) files.POST("/compress", postServerCompressFiles) diff --git a/router/router_server_files.go b/router/router_server_files.go index b6ea657..514af14 100644 --- a/router/router_server_files.go +++ b/router/router_server_files.go @@ -225,6 +225,51 @@ func postServerWriteFile(c *gin.Context) { c.Status(http.StatusNoContent) } +// Writes the contents of the remote URL to a file on a server. +func postServerDownloadRemoteFile(c *gin.Context) { + s := ExtractServer(c) + var data struct { + URL string `binding:"required" json:"url"` + BasePath string `json:"path"` + } + if err := c.BindJSON(&data); err != nil { + return + } + + u, err := url.Parse(data.URL) + if err != nil { + if e, ok := err.(*url.Error); ok { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{ + "error": "An error occurred while parsing that URL: " + e.Err.Error(), + }) + return + } + TrackedServerError(err, s).AbortWithServerError(c) + return + } + + resp, err := http.Get(u.String()) + if err != nil { + TrackedServerError(err, s).AbortWithServerError(c) + return + } + defer resp.Body.Close() + + filename := strings.Split(u.Path, "/") + if err := s.Filesystem().Writefile(filepath.Join(data.BasePath, filename[len(filename)-1]), resp.Body); err != nil { + if errors.Is(err, filesystem.ErrIsDirectory) { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{ + "error": "Cannot write file, name conflicts with an existing directory by the same name.", + }) + return + } + TrackedServerError(err, s).AbortFilesystemError(c) + return + } + + c.Status(http.StatusNoContent) +} + // Create a directory on a server. func postServerCreateDirectory(c *gin.Context) { s := GetServer(c.Param("server"))