init: pristine aerc 0.20.0 source

This commit is contained in:
Mortdecai
2026-04-07 19:54:54 -04:00
commit 083402a548
502 changed files with 68722 additions and 0 deletions
+45
View File
@@ -0,0 +1,45 @@
package cache
import (
"os"
"path"
"git.sr.ht/~rockorager/go-jmap"
)
func (c *JMAPCache) GetBlob(id jmap.ID) ([]byte, error) {
fpath := c.blobPath(id)
if fpath == "" {
return nil, notfound
}
return os.ReadFile(fpath)
}
func (c *JMAPCache) PutBlob(id jmap.ID, buf []byte) error {
fpath := c.blobPath(id)
if fpath == "" {
return nil
}
_ = os.MkdirAll(path.Dir(fpath), 0o700)
return os.WriteFile(fpath, buf, 0o600)
}
func (c *JMAPCache) DeleteBlob(id jmap.ID) error {
fpath := c.blobPath(id)
if fpath == "" {
return nil
}
defer func() {
_ = os.Remove(path.Dir(fpath))
}()
return os.Remove(fpath)
}
func (c *JMAPCache) blobPath(id jmap.ID) string {
if c.blobsDir == "" || id == "" {
return ""
}
name := string(id)
sub := name[len(name)-2:]
return path.Join(c.blobsDir, sub, name)
}
+109
View File
@@ -0,0 +1,109 @@
package cache
import (
"errors"
"os"
"path"
"strings"
"git.sr.ht/~rjarry/aerc/lib/log"
"git.sr.ht/~rjarry/aerc/lib/xdg"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/util"
)
type JMAPCache struct {
mem map[string][]byte
file *leveldb.DB
blobsDir string
}
func NewJMAPCache(state, blobs bool, accountName string) *JMAPCache {
c := new(JMAPCache)
cacheDir := xdg.CachePath()
if state && cacheDir != "" {
var err error
dir := path.Join(cacheDir, "aerc", accountName, "state")
_ = os.MkdirAll(dir, 0o700)
c.file, err = leveldb.OpenFile(dir, nil)
if err != nil {
log.Errorf("failed to open goleveldb: %s", err)
c.mem = make(map[string][]byte)
}
} else {
c.mem = make(map[string][]byte)
}
if blobs && cacheDir != "" {
c.blobsDir = path.Join(cacheDir, "aerc", accountName, "blobs")
}
return c
}
var notfound = errors.New("key not found")
func (c *JMAPCache) get(key string) ([]byte, error) {
switch {
case c.file != nil:
return c.file.Get([]byte(key), nil)
case c.mem != nil:
value, ok := c.mem[key]
if !ok {
return nil, notfound
}
return value, nil
}
panic("jmap cache with no backend")
}
func (c *JMAPCache) put(key string, value []byte) error {
switch {
case c.file != nil:
return c.file.Put([]byte(key), value, nil)
case c.mem != nil:
c.mem[key] = value
return nil
}
panic("jmap cache with no backend")
}
func (c *JMAPCache) delete(key string) error {
switch {
case c.file != nil:
return c.file.Delete([]byte(key), nil)
case c.mem != nil:
delete(c.mem, key)
return nil
}
panic("jmap cache with no backend")
}
func (c *JMAPCache) purge(prefix string) error {
switch {
case c.file != nil:
txn, err := c.file.OpenTransaction()
if err != nil {
return err
}
iter := txn.NewIterator(util.BytesPrefix([]byte(prefix)), nil)
for iter.Next() {
err = txn.Delete(iter.Key(), nil)
if err != nil {
break
}
}
iter.Release()
if err != nil {
txn.Discard()
return err
}
return txn.Commit()
case c.mem != nil:
for key := range c.mem {
if strings.HasPrefix(key, prefix) {
delete(c.mem, key)
}
}
return nil
}
panic("jmap cache with no backend")
}
+40
View File
@@ -0,0 +1,40 @@
package cache
import (
"git.sr.ht/~rockorager/go-jmap"
"git.sr.ht/~rockorager/go-jmap/mail/email"
)
func (c *JMAPCache) HasEmail(id jmap.ID) bool {
_, err := c.get(emailKey(id))
return err == nil
}
func (c *JMAPCache) GetEmail(id jmap.ID) (*email.Email, error) {
buf, err := c.get(emailKey(id))
if err != nil {
return nil, err
}
e := new(email.Email)
err = unmarshal(buf, e)
if err != nil {
return nil, err
}
return e, nil
}
func (c *JMAPCache) PutEmail(id jmap.ID, e *email.Email) error {
buf, err := marshal(e)
if err != nil {
return err
}
return c.put(emailKey(id), buf)
}
func (c *JMAPCache) DeleteEmail(id jmap.ID) error {
return c.delete(emailKey(id))
}
func emailKey(id jmap.ID) string {
return "email/" + string(id)
}
+59
View File
@@ -0,0 +1,59 @@
package cache
import (
"reflect"
"git.sr.ht/~rjarry/aerc/lib/log"
"git.sr.ht/~rjarry/aerc/worker/types"
"git.sr.ht/~rockorager/go-jmap"
)
type FolderContents struct {
MailboxID jmap.ID
QueryState string
Filter *types.SearchCriteria
Sort []*types.SortCriterion
MessageIDs []jmap.ID
}
func (c *JMAPCache) GetFolderContents(mailboxId jmap.ID) (*FolderContents, error) {
key := folderContentsKey(mailboxId)
buf, err := c.get(key)
if err != nil {
return nil, err
}
m := new(FolderContents)
err = unmarshal(buf, m)
if err != nil {
log.Debugf("cache format has changed, purging foldercontents")
if e := c.purge("foldercontents/"); e != nil {
log.Errorf("foldercontents cache purge: %s", e)
}
return nil, err
}
return m, nil
}
func (c *JMAPCache) PutFolderContents(mailboxId jmap.ID, m *FolderContents) error {
buf, err := marshal(m)
if err != nil {
return err
}
return c.put(folderContentsKey(mailboxId), buf)
}
func (c *JMAPCache) DeleteFolderContents(mailboxId jmap.ID) error {
return c.delete(folderContentsKey(mailboxId))
}
func folderContentsKey(mailboxId jmap.ID) string {
return "foldercontents/" + string(mailboxId)
}
func (f *FolderContents) NeedsRefresh(
filter *types.SearchCriteria, sort []*types.SortCriterion,
) bool {
return f.QueryState == "" ||
!reflect.DeepEqual(f.Sort, sort) ||
!reflect.DeepEqual(f.Filter, filter)
}
+33
View File
@@ -0,0 +1,33 @@
package cache
import (
"bytes"
"encoding/gob"
"git.sr.ht/~rockorager/go-jmap/mail/email"
"git.sr.ht/~rockorager/go-jmap/mail/mailbox"
)
type jmapObject interface {
*email.Email |
*email.QueryResponse |
*mailbox.Mailbox |
*FolderContents |
*IDList
}
func marshal[T jmapObject](obj T) ([]byte, error) {
buf := bytes.NewBuffer(nil)
encoder := gob.NewEncoder(buf)
err := encoder.Encode(obj)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func unmarshal[T jmapObject](data []byte, obj T) error {
buf := bytes.NewBuffer(data)
decoder := gob.NewDecoder(buf)
return decoder.Decode(obj)
}
+35
View File
@@ -0,0 +1,35 @@
package cache
import (
"git.sr.ht/~rockorager/go-jmap"
"git.sr.ht/~rockorager/go-jmap/mail/mailbox"
)
func (c *JMAPCache) GetMailbox(id jmap.ID) (*mailbox.Mailbox, error) {
buf, err := c.get(mailboxKey(id))
if err != nil {
return nil, err
}
m := new(mailbox.Mailbox)
err = unmarshal(buf, m)
if err != nil {
return nil, err
}
return m, nil
}
func (c *JMAPCache) PutMailbox(id jmap.ID, m *mailbox.Mailbox) error {
buf, err := marshal(m)
if err != nil {
return err
}
return c.put(mailboxKey(id), buf)
}
func (c *JMAPCache) DeleteMailbox(id jmap.ID) error {
return c.delete(mailboxKey(id))
}
func mailboxKey(id jmap.ID) string {
return "mailbox/" + string(id)
}
+32
View File
@@ -0,0 +1,32 @@
package cache
import (
"git.sr.ht/~rockorager/go-jmap"
)
type IDList struct {
IDs []jmap.ID
}
func (c *JMAPCache) GetMailboxList() ([]jmap.ID, error) {
buf, err := c.get(mailboxListKey)
if err != nil {
return nil, err
}
var list IDList
err = unmarshal(buf, &list)
if err != nil {
return nil, err
}
return list.IDs, nil
}
func (c *JMAPCache) PutMailboxList(list []jmap.ID) error {
buf, err := marshal(&IDList{IDs: list})
if err != nil {
return err
}
return c.put(mailboxListKey, buf)
}
const mailboxListKey = "mailbox/list"
+34
View File
@@ -0,0 +1,34 @@
package cache
import (
"encoding/json"
"git.sr.ht/~rockorager/go-jmap"
)
func (c *JMAPCache) GetSession() (*jmap.Session, error) {
buf, err := c.get(sessionKey)
if err != nil {
return nil, err
}
s := new(jmap.Session)
err = json.Unmarshal(buf, s)
if err != nil {
return nil, err
}
return s, nil
}
func (c *JMAPCache) PutSession(s *jmap.Session) error {
buf, err := json.Marshal(s)
if err != nil {
return err
}
return c.put(sessionKey, buf)
}
func (c *JMAPCache) DeleteSession() error {
return c.delete(sessionKey)
}
const sessionKey = "session"
+43
View File
@@ -0,0 +1,43 @@
package cache
func (c *JMAPCache) GetMailboxState() (string, error) {
buf, err := c.get(mailboxStateKey)
if err != nil {
return "", err
}
return string(buf), nil
}
func (c *JMAPCache) PutMailboxState(state string) error {
return c.put(mailboxStateKey, []byte(state))
}
func (c *JMAPCache) GetEmailState() (string, error) {
buf, err := c.get(emailStateKey)
if err != nil {
return "", err
}
return string(buf), nil
}
func (c *JMAPCache) PutEmailState(state string) error {
return c.put(emailStateKey, []byte(state))
}
func (c *JMAPCache) GetThreadState() (string, error) {
buf, err := c.get(threadStateKey)
if err != nil {
return "", err
}
return string(buf), nil
}
func (c *JMAPCache) PutThreadState(state string) error {
return c.put(threadStateKey, []byte(state))
}
const (
mailboxStateKey = "state/mailbox"
emailStateKey = "state/email"
threadStateKey = "state/thread"
)
+34
View File
@@ -0,0 +1,34 @@
package cache
import (
"git.sr.ht/~rockorager/go-jmap"
)
func (c *JMAPCache) GetThread(id jmap.ID) ([]jmap.ID, error) {
buf, err := c.get(threadKey(id))
if err != nil {
return nil, err
}
var list IDList
err = unmarshal(buf, &list)
if err != nil {
return nil, err
}
return list.IDs, nil
}
func (c *JMAPCache) PutThread(id jmap.ID, list []jmap.ID) error {
buf, err := marshal(&IDList{IDs: list})
if err != nil {
return err
}
return c.put(threadKey(id), buf)
}
func (c *JMAPCache) DeleteThread(id jmap.ID) error {
return c.delete(mailboxKey(id))
}
func threadKey(id jmap.ID) string {
return "thread/" + string(id)
}