76 lines
2.0 KiB
Go
76 lines
2.0 KiB
Go
package grab
|
|
|
|
import (
|
|
"fmt"
|
|
"mime"
|
|
"net/http"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"github.com/lordwelch/pathvalidate"
|
|
)
|
|
|
|
// setLastModified sets the last modified timestamp of a local file according to
|
|
// the Last-Modified header returned by a remote server.
|
|
func setLastModified(resp *http.Response, filename string) error {
|
|
// https://tools.ietf.org/html/rfc7232#section-2.2
|
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified
|
|
header := resp.Header.Get("Last-Modified")
|
|
if header == "" {
|
|
return nil
|
|
}
|
|
lastmod, err := time.Parse(http.TimeFormat, header)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
return os.Chtimes(filename, lastmod, lastmod)
|
|
}
|
|
|
|
// mkdirp creates all missing parent directories for the destination file path.
|
|
func mkdirp(path string) error {
|
|
dir := filepath.Dir(path)
|
|
if fi, err := os.Stat(dir); err != nil {
|
|
if !os.IsNotExist(err) {
|
|
return fmt.Errorf("error checking destination directory: %v", err)
|
|
}
|
|
if err := os.MkdirAll(dir, 0777); err != nil {
|
|
return fmt.Errorf("error creating destination directory: %v", err)
|
|
}
|
|
} else if !fi.IsDir() {
|
|
panic("grab: developer error: destination path is not directory")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// guessFilename returns a filename for the given http.Response. If none can be
|
|
// determined ErrNoFilename is returned.
|
|
//
|
|
// TODO: NoStore operations should not require a filename
|
|
func guessFilename(resp *http.Response) (string, error) {
|
|
var (
|
|
err error
|
|
filename = resp.Request.URL.Path
|
|
)
|
|
if cd := resp.Header.Get("Content-Disposition"); cd != "" {
|
|
if _, params, err := mime.ParseMediaType(cd); err == nil {
|
|
if val, ok := params["filename"]; ok {
|
|
filename = val
|
|
} // else filename directive is missing. fallback to URL.Path
|
|
}
|
|
}
|
|
|
|
// sanitize
|
|
filename, err = pathvalidate.SanitizeFilename(path.Base(filename), '_')
|
|
if err != nil {
|
|
return "", fmt.Errorf("%w: %v", ErrNoFilename, err)
|
|
}
|
|
|
|
if filename == "" || filename == "." || filename == "/" {
|
|
return "", ErrNoFilename
|
|
}
|
|
|
|
return filename, nil
|
|
}
|