amfora/display/download.go

104 lines
2.6 KiB
Go
Raw Normal View History

2020-07-09 23:28:39 +00:00
package display
import (
"io/ioutil"
"net/url"
"os"
"path"
"path/filepath"
"strconv"
"strings"
"github.com/makeworld-the-better-one/amfora/config"
"github.com/makeworld-the-better-one/amfora/structs"
)
// getSafeDownloadName is used by downloads.go only.
// It returns a modified name that is unique for the downloads folder.
// This way duplicate saved files will not overwrite each other.
//
// lastDot should be set to true if the number added to the name should come before
// the last dot in the filename instead of the first.
//
// n should be set to 0, it is used for recursiveness.
func getSafeDownloadName(name string, lastDot bool, n int) (string, error) {
// newName("test.txt", 3) -> "test(3).txt"
newName := func() string {
if n <= 0 {
return name
}
if lastDot {
ext := filepath.Ext(name)
return strings.TrimSuffix(name, ext) + "(" + strconv.Itoa(n) + ")" + ext
} else {
idx := strings.Index(name, ".")
if idx == -1 {
return name + "(" + strconv.Itoa(n) + ")"
}
return name[:idx] + "(" + strconv.Itoa(n) + ")" + name[idx:]
}
}
d, err := os.Open(config.DownloadsDir)
if err != nil {
return "", err
}
files, err := d.Readdirnames(-1)
if err != nil {
d.Close()
return "", err
}
nn := newName()
for i := range files {
if nn == files[i] {
d.Close()
return getSafeDownloadName(name, lastDot, n+1)
}
}
d.Close()
return nn, nil // Name doesn't exist already
}
// downloadPage saves the passed Page to a file.
// It returns the saved path and an error.
// It always cleans up, so if an error is returned there is no file saved
func downloadPage(p *structs.Page) (string, error) {
// Figure out file name
var name string
var err error
parsed, _ := url.Parse(p.Url)
if parsed.Path == "" || path.Base(parsed.Path) == "/" {
// No file, just the root domain
if p.Mediatype == structs.TextGemini {
name, err = getSafeDownloadName(parsed.Hostname()+".gmi", true, 0)
if err != nil {
return "", err
}
} else {
name, err = getSafeDownloadName(parsed.Hostname()+".txt", true, 0)
if err != nil {
return "", err
}
}
} else {
// There's a specific file
name = path.Base(parsed.Path)
if p.Mediatype == structs.TextGemini && !strings.HasSuffix(name, ".gmi") && !strings.HasSuffix(name, ".gemini") {
name += ".gmi"
}
name, err = getSafeDownloadName(name, false, 0)
if err != nil {
return "", err
}
}
savePath := filepath.Join(config.DownloadsDir, name)
err = ioutil.WriteFile(savePath, []byte(p.Raw), 0644)
if err != nil {
// Just in case
os.Remove(savePath)
return "", err
}
return savePath, err
}