Archived
1
0
This repository has been archived on 2023-08-12. You can view files and clone it, but cannot push or open issues or pull requests.
nntpchan/contrib/backends/srndv2/src/srnd/expiration.go
2017-08-08 18:38:48 -04:00

151 lines
3.6 KiB
Go

//
// expiration.go
// content expiration
//
package srnd
import (
"log"
"os"
"path/filepath"
"time"
)
// content expiration interface
type ExpirationCore interface {
// do expiration for a group
ExpireGroup(newsgroup string, keep int)
// Delete a single post and all children
ExpirePost(messageID string)
// expire all orphaned articles
ExpireOrphans()
// expire all articles posted before time
ExpireBefore(t time.Time)
}
type ExpireCacheFunc func(string, string, string)
func createExpirationCore(database Database, store ArticleStore, ex ExpireCacheFunc) ExpirationCore {
return expire{database, store, ex}
}
type deleteEvent string
func (self deleteEvent) Path() string {
return string(self)
}
func (self deleteEvent) MessageID() string {
return filepath.Base(string(self))
}
type expire struct {
database Database
store ArticleStore
expireCache ExpireCacheFunc
}
func (self expire) ExpirePost(messageID string) {
self.handleEvent(deleteEvent(self.store.GetFilename(messageID)))
// get article headers
headers := self.store.GetHeaders(messageID)
if headers != nil {
group := headers.Get("Newsgroups", "")
// is this a root post ?
ref := headers.Get("References", "")
if ref == "" || ref == messageID {
// ya, expire the entire thread
self.ExpireThread(group, messageID)
} else {
self.expireCache(group, messageID, ref)
}
}
}
func (self expire) ExpireGroup(newsgroup string, keep int) {
threads := self.database.GetRootPostsForExpiration(newsgroup, keep)
for _, root := range threads {
self.ExpireThread(newsgroup, root)
}
}
func (self expire) ExpireThread(group, rootMsgid string) {
replies, err := self.database.GetMessageIDByHeader("References", rootMsgid)
if err == nil {
for _, reply := range replies {
self.handleEvent(deleteEvent(self.store.GetFilename(reply)))
}
}
self.database.DeleteThread(rootMsgid)
self.database.DeleteArticle(rootMsgid)
self.expireCache(group, rootMsgid, rootMsgid)
}
func (self expire) ExpireBefore(t time.Time) {
articles, err := self.database.GetPostsBefore(t)
if err == nil {
for _, msgid := range articles {
self.ExpirePost(msgid)
}
} else {
log.Println("failed to expire older posts", err)
}
}
// expire all orphaned articles
func (self expire) ExpireOrphans() {
// get all articles in database
articles := self.database.GetAllArticles()
if articles != nil {
log.Println("expire all orphan posts")
// for each article
for _, article := range articles {
// load headers
hdr := self.store.GetHeaders(article.MessageID())
if hdr == nil {
// article does not exist?
// ensure it's deleted
self.ExpirePost(article.MessageID())
} else {
// check if we are a reply
rootMsgid := hdr.Get("References", "")
if len(rootMsgid) == 0 {
// root post
} else {
// reply
// do we have this root post?
if self.store.HasArticle(rootMsgid) {
// yes, do nothing
} else {
// no, expire post
self.ExpirePost(article.MessageID())
}
}
}
}
}
}
func (self expire) handleEvent(ev deleteEvent) {
atts := self.database.GetPostAttachments(ev.MessageID())
// remove all attachments
if atts != nil {
for _, att := range atts {
img := self.store.AttachmentFilepath(att)
os.Remove(img)
thm := self.store.ThumbnailFilepath(att)
os.Remove(thm)
}
}
err := self.database.BanArticle(ev.MessageID(), "expired")
if err != nil {
log.Println("failed to ban for expiration", err)
}
err = self.database.DeleteArticle(ev.MessageID())
if err != nil {
log.Println("failed to delete article", err)
}
// remove article
self.store.Remove(ev.MessageID())
}