diff --git a/waveshell/pkg/packet/packet.go b/waveshell/pkg/packet/packet.go index 22e9828fe..d3c6e38e5 100644 --- a/waveshell/pkg/packet/packet.go +++ b/waveshell/pkg/packet/packet.go @@ -485,6 +485,7 @@ type FileInfo struct { ModTs int64 `json:"modts"` IsDir bool `json:"isdir,omitempty"` Perm int `json:"perm"` + MimeType string `json:"mimetype,omitempty"` NotFound bool `json:"notfound,omitempty"` // when NotFound is set, Perm will be set to permission for directory } diff --git a/waveshell/pkg/server/server.go b/waveshell/pkg/server/server.go index 192b39dc7..520c7d1b5 100644 --- a/waveshell/pkg/server/server.go +++ b/waveshell/pkg/server/server.go @@ -575,12 +575,14 @@ func (m *MServer) streamFile(pk *packet.StreamFilePacketType) { m.Sender.SendPacket(resp) return } + mimeType := utilfn.DetectMimeType(pk.Path) resp.Info = &packet.FileInfo{ - Name: pk.Path, - Size: finfo.Size(), - ModTs: finfo.ModTime().UnixMilli(), - IsDir: finfo.IsDir(), - Perm: int(finfo.Mode().Perm()), + Name: pk.Path, + Size: finfo.Size(), + ModTs: finfo.ModTime().UnixMilli(), + IsDir: finfo.IsDir(), + MimeType: mimeType, + Perm: int(finfo.Mode().Perm()), } if pk.StatOnly { resp.Done = true diff --git a/waveshell/pkg/utilfn/utilfn.go b/waveshell/pkg/utilfn/utilfn.go index 8e8638e36..608e36cc8 100644 --- a/waveshell/pkg/utilfn/utilfn.go +++ b/waveshell/pkg/utilfn/utilfn.go @@ -13,6 +13,8 @@ import ( "io" "math" mathrand "math/rand" + "net/http" + "os" "regexp" "sort" "strings" @@ -611,3 +613,25 @@ func CopyToChannel(outputCh chan<- []byte, reader io.Reader) error { } } } + +// on error just returns "" +// does not return "application/octet-stream" as this is considered a detection failure +func DetectMimeType(path string) string { + fd, err := os.Open(path) + if err != nil { + return "" + } + defer fd.Close() + buf := make([]byte, 512) + // ignore the error (EOF / UnexpectedEOF is fine, just process how much we got back) + n, _ := io.ReadAtLeast(fd, buf, 512) + if n == 0 { + return "" + } + buf = buf[:n] + rtn := http.DetectContentType(buf) + if rtn == "application/octet-stream" { + return "" + } + return rtn +} diff --git a/wavesrv/cmd/main-server.go b/wavesrv/cmd/main-server.go index d62a93a11..e139a8163 100644 --- a/wavesrv/cmd/main-server.go +++ b/wavesrv/cmd/main-server.go @@ -508,11 +508,8 @@ func HandleReadFile(w http.ResponseWriter, r *http.Request) { qvals := r.URL.Query() screenId := qvals.Get("screenid") lineId := qvals.Get("lineid") - path := qvals.Get("path") // validate path? - contentType := qvals.Get("mimetype") - if contentType == "" { - contentType = "application/octet-stream" - } + path := qvals.Get("path") // validate path? + contentType := qvals.Get("mimetype") // force a mimetype if screenId == "" || lineId == "" { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("must specify sessionid, screenid, and lineid")) @@ -533,7 +530,7 @@ func HandleReadFile(w http.ResponseWriter, r *http.Request) { w.Write([]byte(fmt.Sprintf(ErrorInvalidLineId, err))) return } - if !ContentTypeHeaderValidRe.MatchString(contentType) { + if contentType != "" && !ContentTypeHeaderValidRe.MatchString(contentType) { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("invalid mimetype specified")) return @@ -599,6 +596,12 @@ func HandleReadFile(w http.ResponseWriter, r *http.Request) { return } infoJson, _ := json.Marshal(resp.Info) + if contentType == "" && resp.Info.MimeType != "" { + contentType = resp.Info.MimeType + } + if contentType == "" { + contentType = "application/octet-stream" + } w.Header().Set("X-FileInfo", base64.StdEncoding.EncodeToString(infoJson)) w.Header().Set(ContentTypeHeaderKey, contentType) w.WriteHeader(http.StatusOK)