"""Mixin class for putting new messages in the right place for archival.
Public archives are separated from private ones. An external archival
mechanism (eg, pipermail) should be pointed to the right places, to do the
archival.
"""
import os
import errno
import traceback
import re
from cStringIO import StringIO
from Mailman import mm_cfg
from Mailman import Mailbox
from Mailman import Utils
from Mailman import Site
from Mailman.SafeDict import SafeDict
from Mailman.Logging.Syslog import syslog
from Mailman.i18n import _
try:
True, False
except NameError:
True = 1
False = 0
def makelink(old, new):
try:
os.symlink(old, new)
except OSError, e:
if e.errno <> errno.EEXIST:
raise
def breaklink(link):
try:
os.unlink(link)
except OSError, e:
if e.errno <> errno.ENOENT:
raise
class Archiver:
def InitVars(self):
self.archive = mm_cfg.DEFAULT_ARCHIVE
self.archive_private = mm_cfg.DEFAULT_ARCHIVE_PRIVATE
self.archive_volume_frequency = \
mm_cfg.DEFAULT_ARCHIVE_VOLUME_FREQUENCY
omask = os.umask(0)
try:
try:
os.mkdir(self.archive_dir()+'.mbox', 02775)
except OSError, e:
if e.errno <> errno.EEXIST: raise
try:
os.mkdir(self.archive_dir(), 02775)
except OSError, e:
if e.errno <> errno.EEXIST: raise
indexfile = os.path.join(self.archive_dir(), 'index.html')
fp = None
try:
fp = open(indexfile)
except IOError, e:
if e.errno <> errno.ENOENT: raise
omask = os.umask(002)
try:
fp = open(indexfile, 'w')
finally:
os.umask(omask)
fp.write(Utils.maketext(
'emptyarchive.html',
{'listname': self.real_name,
'listinfo': self.GetScriptURL('listinfo', absolute=1),
}, mlist=self))
if fp:
fp.close()
finally:
os.umask(omask)
def archive_dir(self):
return Site.get_archpath(self.internal_name())
def ArchiveFileName(self):
"""The mbox name where messages are left for archive construction."""
return os.path.join(self.archive_dir() + '.mbox',
self.internal_name() + '.mbox')
def GetBaseArchiveURL(self):
url = self.GetScriptURL('private', absolute=1) + '/'
if self.archive_private:
return url
else:
hostname = re.match('[^:]*://([^/]*)/.*', url).group(1)\
or mm_cfg.DEFAULT_URL_HOST
url = mm_cfg.PUBLIC_ARCHIVE_URL % {
'listname': self.internal_name(),
'hostname': hostname
}
if not url.endswith('/'):
url += '/'
return url
def __archive_file(self, afn):
"""Open (creating, if necessary) the named archive file."""
omask = os.umask(002)
try:
return Mailbox.Mailbox(open(afn, 'a+'))
finally:
os.umask(omask)
def __archive_to_mbox(self, post):
"""Retain a text copy of the message in an mbox file."""
try:
afn = self.ArchiveFileName()
mbox = self.__archive_file(afn)
mbox.AppendMessage(post)
mbox.fp.close()
except IOError, msg:
syslog('error', 'Archive file access failure:\n\t%s %s', afn, msg)
raise
def ExternalArchive(self, ar, txt):
d = SafeDict({'listname': self.internal_name(),
'hostname': self.host_name,
})
cmd = ar % d
extarch = os.popen(cmd, 'w')
extarch.write(txt)
status = extarch.close()
if status:
syslog('error', 'external archiver non-zero exit status: %d\n',
(status & 0xff00) >> 8)
def ArchiveMail(self, msg):
"""Store postings in mbox and/or pipermail archive, depending."""
if mm_cfg.ARCHIVE_TO_MBOX == -1:
return
if mm_cfg.ARCHIVE_TO_MBOX in (1, 2):
self.__archive_to_mbox(msg)
if mm_cfg.ARCHIVE_TO_MBOX == 1:
return
txt = str(msg)
private_p = self.archive_private
if mm_cfg.PUBLIC_EXTERNAL_ARCHIVER and not private_p:
self.ExternalArchive(mm_cfg.PUBLIC_EXTERNAL_ARCHIVER, txt)
elif mm_cfg.PRIVATE_EXTERNAL_ARCHIVER and private_p:
self.ExternalArchive(mm_cfg.PRIVATE_EXTERNAL_ARCHIVER, txt)
else:
f = StringIO(txt)
import HyperArch
h = HyperArch.HyperArchive(self)
h.processUnixMailbox(f)
h.close()
f.close()
def CheckHTMLArchiveDir(self):
if mm_cfg.ARCHIVE_TO_MBOX == -1:
return
pubdir = Site.get_archpath(self.internal_name(), public=True)
privdir = self.archive_dir()
pubmbox = pubdir + '.mbox'
privmbox = privdir + '.mbox'
if self.archive_private:
breaklink(pubdir)
breaklink(pubmbox)
else:
makelink(privdir, pubdir)
if mm_cfg.PUBLIC_MBOX:
makelink(privmbox, pubmbox)