summaryrefslogtreecommitdiff
path: root/Mailman/Archiver/Archiver.py
diff options
context:
space:
mode:
Diffstat (limited to 'Mailman/Archiver/Archiver.py')
-rw-r--r--Mailman/Archiver/Archiver.py265
1 files changed, 265 insertions, 0 deletions
diff --git a/Mailman/Archiver/Archiver.py b/Mailman/Archiver/Archiver.py
new file mode 100644
index 000000000..97591f305
--- /dev/null
+++ b/Mailman/Archiver/Archiver.py
@@ -0,0 +1,265 @@
+
+# Copyright (C) 1998 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+"""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.
+"""
+
+
+#
+# system modules
+#
+import sys, os, string
+
+#
+# package/project modules
+#
+import Mailman.Utils
+import Mailman.Mailbox
+import Mailman.mm_cfg
+
+#
+# assignments to make them local
+#
+Utils = Mailman.Utils
+Mailbox = Mailman.Mailbox
+mm_cfg = Mailman.mm_cfg
+
+class Archiver:
+ def InitVars(self):
+ # Configurable
+ self.archive = 1
+ # 0=public, 1=private:
+ self.archive_private = mm_cfg.DEFAULT_ARCHIVE_PRIVATE
+ self.archive_volume_frequency = \
+ mm_cfg.DEFAULT_ARCHIVE_VOLUME_FREQUENCY
+
+ # Not configurable
+ self.clobber_date = 0
+ # Though the archive file dirs are list-specific, they are not
+ # settable from the web interface. If you REALLY want to redirect
+ # something to a different dir, you can set the member vars by
+ # hand, from the python interpreter!
+ self.public_archive_file_dir = mm_cfg.PUBLIC_ARCHIVE_FILE_DIR
+ self.private_archive_file_dir = mm_cfg.PRIVATE_ARCHIVE_FILE_DIR
+ self.archive_directory = os.path.join(mm_cfg.HTML_DIR,
+ 'archives',
+ self._internal_name)
+
+ def GetBaseArchiveURL(self):
+ if self.archive_private:
+ return os.path.join(mm_cfg.PRIVATE_ARCHIVE_URL,
+ self._internal_name + mm_cfg.PRIVATE_ARCHIVE_URL_EXT)
+ else:
+ return os.path.join(mm_cfg.PUBLIC_ARCHIVE_URL,
+ self._internal_name + mm_cfg.PRIVATE_ARCHIVE_URL_EXT)
+
+ def GetConfigInfo(self):
+ return [
+ "List traffic archival policies.",
+
+ ('archive', mm_cfg.Toggle, ('No', 'Yes'), 0,
+ 'Archive messages?'),
+
+ ('archive_private', mm_cfg.Radio, ('public', 'private'), 0,
+ 'Is archive file source for public or private archival?'),
+
+ ('clobber_date', mm_cfg.Radio, ('When sent', 'When resent'), 0,
+ 'Set date in archive to when the mail is claimed to have been '
+ 'sent, or to the time we resend it?'),
+
+ ('archive_volume_frequency', mm_cfg.Radio,
+ ('Yearly', 'Monthly','Quarterly', 'Weekly', 'Daily'), 0,
+ 'How often should a new archive volume be started?'),
+
+ ]
+
+ def UpdateArchive(self):
+ # This method is not being used, in favor of external archiver!
+ if not self.archive:
+ return
+ archive_file_name = os.path.join(self._full_path, ARCHIVE_PENDING)
+ archive_dir = os.path.join(self.archive_directory, 'volume_%d'
+ % self.volume)
+
+ # Test to make sure there are posts to archive
+ archive_file = open(archive_file_name, 'r')
+ text = string.strip(archive_file.read())
+ archive_file.close()
+ if not text:
+ return
+ Utils.MakeDirTree(archive_dir, 0755)
+ # Pipermail 0.0.2 always looks at sys.argv, and I wasn't into hacking
+ # it more than I had to, so here's a small hack to get around that,
+ # calling pipermail w/ the correct options.
+ real_argv = sys.argv
+ sys.argv = ['pipermail', '-d%s' % archive_dir, '-l%s' %
+ self._internal_name, '-m%s' % archive_file_name,
+ '-s%s' % os.path.join(archive_dir, "INDEX")]
+
+ import pipermail
+ sys.argv = real_argv
+ f = open(archive_file_name, 'w+')
+ f.truncate(0)
+ f.close()
+
+ #
+ # old ArchiveMail function, retained under a new name
+ # for optional archiving to an mbox
+ #
+ def ArchiveToMbox(self, post):
+ """Retain a text copy of the message in an mbox file."""
+ if self.clobber_date:
+ import time
+ olddate = post.getheader('date')
+ post.SetHeader('Date', time.ctime(time.time()))
+ try:
+ afn = self.ArchiveFileName()
+ mbox = self.ArchiveFile(afn)
+ mbox.AppendMessage(post)
+ mbox.fp.close()
+ except IOError, msg:
+ self.LogMsg("error", ("Archive file access failure:\n"
+ "\t%s %s"
+ % (afn, `msg[1]`)))
+ if self.clobber_date:
+ # Resurrect original date setting.
+ post.SetHeader('Date', olddate)
+ self.Save ()
+
+ #
+ # archiving in real time this is called from list.post(msg)
+ #
+ def ArchiveMail(self, msg):
+ #
+ # first we fork so that errors here won't
+ # disrupt normal list delivery -scott
+ #
+ if os.fork():
+ return
+ #
+ # archive to mbox only
+ #
+ if mm_cfg.ARCHIVE_TO_MBOX == 1:
+ self.ArchiveToMbox(msg)
+ return
+ #
+ # archive to both mbox and built in html archiver
+ #
+ elif mm_cfg.ARCHIVE_TO_MBOX == 2:
+ self.ArchiveToMbox(msg)
+ try:
+ from cStringIO import StringIO
+ except ImportError:
+ from StringIO import StringIO
+ txt = msg.unixfrom
+ for h in msg.headers:
+ txt = txt + h
+ if msg.body[0] != '\n':
+ txt = txt + "\n"
+ txt = txt + msg.body
+ f = StringIO(txt)
+ import HyperArch
+ h = HyperArch.HyperArchive(self)
+ h.processUnixMailbox(f, HyperArch.Article)
+ h.close()
+ f.close()
+ os._exit(0)
+
+
+ def ArchiveFileName(self):
+ """The mbox name where messages are left for archive construction."""
+ if self.archive_private:
+ return os.path.join(self.private_archive_file_dir,
+ self._internal_name)
+ else:
+ return os.path.join(self.public_archive_file_dir,
+ self._internal_name)
+
+ def ArchiveFile(self, afn):
+ """Open (creating, if necessary) the named archive file."""
+ ou = os.umask(002)
+ try:
+ try:
+ return Mailbox.Mailbox(open(afn, "a+"))
+ except IOError, msg:
+ raise IOError, msg
+ finally:
+ os.umask(ou)
+
+ #
+ # called from MailList.MailList.Save()
+ #
+ def CheckHTMLArchiveDir(self):
+ #
+ # we need to make sure that the archive
+ # directory has the right perms for public vs
+ # private. If it doesn't exist, or some weird
+ # permissions errors prevent us from stating
+ # the directory, it's pointless to try to
+ # fix the perms, so we just return -scott
+ #
+ try:
+ st = os.stat(self.archive_directory)
+ except os.error, rest:
+ import errno
+ try:
+ val, msg = rest
+ except ValueError:
+ self.LogMsg("error", "MailList.Save(): error getting archive mode for %s!: %s\n",
+ self.real_name, str(rest))
+ return
+ if val == errno.ENOENT: # no such file
+ ou = os.umask(0)
+ if self.archive_private:
+ mode = 02770
+ else:
+ mode = 02775
+ try:
+ os.mkdir(self.archive_directory)
+ os.chmod(self.archive_directory, mode)
+ finally:
+ os.umask(ou)
+ return
+ else:
+ self.LogMsg("error", "CheckHTMLArchiveDir: error getting archive mode for %s!: %s\n",
+ self.real_name, str(rest))
+ return
+ import stat
+ mode = st[stat.ST_MODE]
+ if self.archive_private:
+ if mode != 02770:
+ try:
+ ou = os.umask(0)
+ os.chmod(self.archive_directory, 02770)
+ except os.error, rest:
+ self.LogMsg("error", "CheckHTMLArchiveDir: error getting archive mode for %s!: %s\n",
+ self.real_name, str(rest))
+ else:
+ if mode != 02775:
+ try:
+ os.chmod(self.archive_directory, 02775)
+ except os.error, rest:
+ self.LogMsg("error", "CheckHTMLArchiveDir: error getting archive mode for %s!: %s\n",
+ self.real_name, str(rest))
+
+
+