// Package cache proivdes a simple on disk cache for gopass. // It stores the data in a user specific directory. package cache import ( "fmt" "os" "path/filepath" "strings" "time" "github.com/gopasspw/gopass/pkg/appdir" "github.com/gopasspw/gopass/pkg/debug" "github.com/gopasspw/gopass/pkg/fsutil" ) // OnDisk is a simple on disk cache. type OnDisk struct { ttl time.Duration name string dir string } // NewOnDisk creates a new on disk cache. func NewOnDisk(name string, ttl time.Duration) (*OnDisk, error) { d := filepath.Join(appdir.UserCache(), "gopass", name) return NewOnDiskWithDir(name, d, ttl) } // NewOnDiskWithDir creates a new on disk cache. func NewOnDiskWithDir(name, dir string, ttl time.Duration) (*OnDisk, error) { debug.V(1).Log("New on disk cache %s created at %s", name, dir) o := &OnDisk{ ttl: ttl, name: name, dir: dir, } return o, o.ensureDir() } func (o *OnDisk) ensureDir() error { if err := os.MkdirAll(o.dir, 0o700); err != nil { return fmt.Errorf("failed to create ondisk cache dir %s: %w", o.dir, err) } return nil } // String return the identity of this cache instance. func (o *OnDisk) String() string { return fmt.Sprintf("OnDiskCache(name: %s, ttl: %d, dir: %s)", o.name, o.ttl, o.dir) } // Get fetches an entry from the cache. func (o *OnDisk) Get(key string) ([]string, error) { key = fsutil.CleanFilename(key) fn := filepath.Join(o.dir, key) fi, err := os.Stat(fn) if err != nil { return nil, fmt.Errorf("failed to stat %s: %w", fn, err) } if time.Now().After(fi.ModTime().Add(o.ttl)) { return nil, fmt.Errorf("expired") } buf, err := os.ReadFile(fn) if err != nil { return nil, fmt.Errorf("failed to read file %s: %w", fn, err) } return strings.Split(string(buf), "\n"), nil } // Set adds an entry to the cache. func (o *OnDisk) Set(key string, value []string) error { // we need to make sure not to log things here as plugin Identities' recipients // can contain secret data if err := o.ensureDir(); err != nil { return err } key = fsutil.CleanFilename(key) fn := filepath.Join(o.dir, key) if err := os.WriteFile(fn, []byte(strings.Join(value, "\n")), 0o644); err != nil { return fmt.Errorf("failed to write %s to %s: %w", key, fn, err) } return nil } // ModTime returns the modification time of the cache entry. func (o *OnDisk) ModTime(key string) time.Time { key = fsutil.CleanFilename(key) fn := filepath.Join(o.dir, key) fi, err := os.Stat(fn) if err != nil { return time.Time{} } return fi.ModTime() } // Remove removes an entry from the cache. func (o *OnDisk) Remove(key string) error { if err := o.ensureDir(); err != nil { return err } key = fsutil.CleanFilename(key) fn := filepath.Join(o.dir, key) return os.Remove(fn) } // Purge removes all entries from the cache. func (o *OnDisk) Purge() error { return os.RemoveAll(o.dir) }