2020-06-23 20:07:25 -04:00
|
|
|
package bookmarks
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/base32"
|
2021-02-27 00:13:11 -05:00
|
|
|
"encoding/xml"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
2020-06-24 13:18:23 -04:00
|
|
|
"sort"
|
2020-06-23 20:07:25 -04:00
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/makeworld-the-better-one/amfora/config"
|
|
|
|
)
|
|
|
|
|
2021-02-27 00:13:11 -05:00
|
|
|
func Init() error {
|
|
|
|
f, err := os.Open(config.BkmkPath)
|
|
|
|
if err == nil {
|
|
|
|
// File exists and could be opened
|
2020-06-23 20:07:25 -04:00
|
|
|
|
2021-02-27 00:13:11 -05:00
|
|
|
fi, err := f.Stat()
|
|
|
|
if err == nil && fi.Size() > 0 {
|
|
|
|
// File is not empty
|
2020-06-23 20:07:25 -04:00
|
|
|
|
2021-02-27 00:13:11 -05:00
|
|
|
xbelBytes, err := ioutil.ReadAll(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("read bookmarks.xml error: %w", err)
|
|
|
|
}
|
|
|
|
err = xml.Unmarshal(xbelBytes, &data)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("bookmarks.xml is corrupted: %w", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
f.Close()
|
|
|
|
} else if !os.IsNotExist(err) {
|
|
|
|
// There's an error opening the file, but it's not bc is doesn't exist
|
|
|
|
return fmt.Errorf("open bookmarks.xml error: %w", err)
|
|
|
|
}
|
2020-06-23 20:07:25 -04:00
|
|
|
|
2021-02-27 00:13:11 -05:00
|
|
|
if data.Bookmarks == nil {
|
|
|
|
data.Bookmarks = make([]*xbelBookmark, 0)
|
|
|
|
data.Version = xbelVersion
|
|
|
|
}
|
2020-06-23 20:07:25 -04:00
|
|
|
|
2021-02-27 00:13:11 -05:00
|
|
|
if config.BkmkStore != nil {
|
|
|
|
// There's still bookmarks stored in the old format
|
|
|
|
// Add them and delete the file
|
2020-06-23 20:07:25 -04:00
|
|
|
|
2021-02-27 00:13:11 -05:00
|
|
|
names, urls := oldBookmarks()
|
|
|
|
for i := range names {
|
|
|
|
data.Bookmarks = append(data.Bookmarks, &xbelBookmark{
|
|
|
|
URL: urls[i],
|
|
|
|
Name: names[i],
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
err := writeXbel()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error saving old bookmarks into new format: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = os.Remove(config.OldBkmkPath)
|
|
|
|
if err != nil {
|
2021-02-27 00:21:57 -05:00
|
|
|
//nolint:goerr113
|
2021-02-27 00:13:11 -05:00
|
|
|
return fmt.Errorf(
|
|
|
|
"couldn't delete old bookmarks file (%s), you must delete it yourself to prevent duplicate bookmarks: %w",
|
|
|
|
config.OldBkmkPath,
|
|
|
|
err,
|
2021-02-27 00:21:57 -05:00
|
|
|
)
|
2021-02-27 00:13:11 -05:00
|
|
|
}
|
|
|
|
config.BkmkStore = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2020-06-23 20:07:25 -04:00
|
|
|
|
2021-02-27 00:13:11 -05:00
|
|
|
// oldBookmarks returns a slice of names and a slice of URLs of the
|
|
|
|
// bookmarks in config.BkmkStore.
|
|
|
|
func oldBookmarks() ([]string, []string) {
|
|
|
|
bkmksMap, ok := config.BkmkStore.AllSettings()["bookmarks"].(map[string]interface{})
|
2020-06-23 20:07:25 -04:00
|
|
|
if !ok {
|
|
|
|
// No bookmarks stored yet, return empty map
|
2021-02-27 00:13:11 -05:00
|
|
|
return []string{}, []string{}
|
2020-06-23 20:07:25 -04:00
|
|
|
}
|
2020-06-24 13:18:23 -04:00
|
|
|
|
2021-02-27 00:13:11 -05:00
|
|
|
names := make([]string, 0, len(bkmksMap))
|
|
|
|
urls := make([]string, 0, len(bkmksMap))
|
2020-06-24 13:18:23 -04:00
|
|
|
|
2020-06-23 20:07:25 -04:00
|
|
|
for b32Url, name := range bkmksMap {
|
|
|
|
if n, ok := name.(string); n == "" || !ok {
|
|
|
|
// name is not a string, or it's empty - ignore
|
2020-06-24 13:18:23 -04:00
|
|
|
// Likely means it is a removed bookmark
|
2020-06-23 20:07:25 -04:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
url, err := base32.StdEncoding.DecodeString(strings.ToUpper(b32Url))
|
|
|
|
if err != nil {
|
|
|
|
// This would only happen if a user messed around with the bookmarks file
|
|
|
|
continue
|
|
|
|
}
|
2020-06-24 13:18:23 -04:00
|
|
|
names = append(names, name.(string))
|
2021-02-27 00:13:11 -05:00
|
|
|
urls = append(urls, string(url))
|
|
|
|
}
|
|
|
|
return names, urls
|
|
|
|
}
|
|
|
|
|
|
|
|
func writeXbel() error {
|
|
|
|
xbelBytes, err := xml.MarshalIndent(&data, "", " ")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
xbelBytes = append(xbelHeader, xbelBytes...)
|
|
|
|
err = ioutil.WriteFile(config.BkmkPath, xbelBytes, 0666)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Change the name of the bookmark at the provided URL.
|
|
|
|
func Change(url, name string) {
|
|
|
|
for _, bkmk := range data.Bookmarks {
|
|
|
|
if bkmk.URL == url {
|
|
|
|
bkmk.Name = name
|
|
|
|
writeXbel() //nolint:errcheck
|
|
|
|
return
|
|
|
|
}
|
2020-06-23 20:07:25 -04:00
|
|
|
}
|
2021-02-27 00:13:11 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add will add a new bookmark.
|
|
|
|
func Add(url, name string) {
|
|
|
|
data.Bookmarks = append(data.Bookmarks, &xbelBookmark{
|
|
|
|
URL: url,
|
|
|
|
Name: name,
|
|
|
|
})
|
|
|
|
writeXbel() //nolint:errcheck
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get returns the NAME of the bookmark, given the URL.
|
|
|
|
// It also returns a bool indicating whether it exists.
|
|
|
|
func Get(url string) (string, bool) {
|
|
|
|
for _, bkmk := range data.Bookmarks {
|
|
|
|
if bkmk.URL == url {
|
|
|
|
return bkmk.Name, true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
|
|
|
|
func Remove(url string) {
|
|
|
|
for i, bkmk := range data.Bookmarks {
|
|
|
|
if bkmk.URL == url {
|
|
|
|
data.Bookmarks[i] = data.Bookmarks[len(data.Bookmarks)-1]
|
|
|
|
data.Bookmarks = data.Bookmarks[:len(data.Bookmarks)-1]
|
|
|
|
writeXbel() //nolint:errcheck
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-14 18:45:41 -04:00
|
|
|
// bkmkNameSlice is used for sorting bookmarks alphabetically.
|
|
|
|
// It implements sort.Interface.
|
|
|
|
type bkmkNameSlice struct {
|
|
|
|
names []string
|
|
|
|
urls []string
|
|
|
|
}
|
2021-02-27 00:13:11 -05:00
|
|
|
|
2021-05-14 18:45:41 -04:00
|
|
|
func (b *bkmkNameSlice) Len() int {
|
|
|
|
return len(b.names)
|
|
|
|
}
|
|
|
|
func (b *bkmkNameSlice) Less(i, j int) bool {
|
|
|
|
return b.names[i] < b.names[j]
|
|
|
|
}
|
|
|
|
func (b *bkmkNameSlice) Swap(i, j int) {
|
|
|
|
b.names[i], b.names[j] = b.names[j], b.names[i]
|
|
|
|
b.urls[i], b.urls[j] = b.urls[j], b.urls[i]
|
|
|
|
}
|
2021-02-27 00:13:11 -05:00
|
|
|
|
2021-05-14 18:45:41 -04:00
|
|
|
// All returns all the bookmarks, as two arrays, one for names and one for URLs.
|
|
|
|
// They are sorted alphabetically.
|
|
|
|
func All() ([]string, []string) {
|
|
|
|
b := bkmkNameSlice{
|
|
|
|
make([]string, len(data.Bookmarks)),
|
|
|
|
make([]string, len(data.Bookmarks)),
|
2021-02-27 00:13:11 -05:00
|
|
|
}
|
2021-05-14 18:45:41 -04:00
|
|
|
for i, bkmk := range data.Bookmarks {
|
|
|
|
b.names[i] = bkmk.Name
|
|
|
|
b.urls[i] = bkmk.URL
|
2020-06-24 13:18:23 -04:00
|
|
|
}
|
2021-05-14 18:45:41 -04:00
|
|
|
sort.Sort(&b)
|
|
|
|
return b.names, b.urls
|
2020-06-23 20:07:25 -04:00
|
|
|
}
|