2017-04-03 19:00:38 +05:00
|
|
|
//
|
|
|
|
// 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.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
|
2017-04-04 16:48:45 +05:00
|
|
|
self.store.Remove(ev.MessageID())
|
2017-04-03 19:00:38 +05:00
|
|
|
}
|