1
# Copyright (C) 1998-2003 by the Free Software Foundation, Inc.
3
# This program is free software; you can redistribute it and/or
4
# modify it under the terms of the GNU General Public License
5
# as published by the Free Software Foundation; either version 2
6
# of the License, or (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
"""Mixin class for putting new messages in the right place for archival.
20
Public archives are separated from private ones. An external archival
21
mechanism (eg, pipermail) should be pointed to the right places, to do the
29
from cStringIO import StringIO
31
from Mailman import mm_cfg
32
from Mailman import Mailbox
33
from Mailman import Utils
34
from Mailman import Site
35
from Mailman.SafeDict import SafeDict
36
from Mailman.Logging.Syslog import syslog
37
from Mailman.i18n import _
47
def makelink(old, new):
51
if e.errno <> errno.EEXIST:
58
if e.errno <> errno.ENOENT:
65
# Interface to Pipermail. HyperArch.py uses this method to get the
66
# archive directory for the mailing list
70
self.archive = mm_cfg.DEFAULT_ARCHIVE
71
# 0=public, 1=private:
72
self.archive_private = mm_cfg.DEFAULT_ARCHIVE_PRIVATE
73
self.archive_volume_frequency = \
74
mm_cfg.DEFAULT_ARCHIVE_VOLUME_FREQUENCY
75
# The archive file structure by default is:
82
# lots-of-pipermail-stuff
84
# listname.mbox@ -> ../private/listname.mbox
85
# listname@ -> ../private/listname
87
# IOW, the mbox and pipermail archives are always stored in the
88
# private archive for the list. This is safe because archives/private
89
# is always set to o-rx. Public archives have a symlink to get around
90
# the private directory, pointing directly to the private/listname
91
# which has o+rx permissions. Private archives do not have the
96
os.mkdir(self.archive_dir()+'.mbox', 02775)
98
if e.errno <> errno.EEXIST: raise
99
# We also create an empty pipermail archive directory into
100
# which we'll drop an empty index.html file into. This is so
101
# that lists that have not yet received a posting have
102
# /something/ as their index.html, and don't just get a 404.
104
os.mkdir(self.archive_dir(), 02775)
106
if e.errno <> errno.EEXIST: raise
107
# See if there's an index.html file there already and if not,
108
# write in the empty archive notice.
109
indexfile = os.path.join(self.archive_dir(), 'index.html')
114
if e.errno <> errno.ENOENT: raise
115
omask = os.umask(002)
117
fp = open(indexfile, 'w')
120
fp.write(Utils.maketext(
122
{'listname': self.real_name,
123
'listinfo': self.GetScriptURL('listinfo', absolute=1),
130
def archive_dir(self):
131
return Site.get_archpath(self.internal_name())
133
def ArchiveFileName(self):
134
"""The mbox name where messages are left for archive construction."""
135
return os.path.join(self.archive_dir() + '.mbox',
136
self.internal_name() + '.mbox')
138
def GetBaseArchiveURL(self):
139
url = self.GetScriptURL('private', absolute=1) + '/'
140
if self.archive_private:
143
hostname = re.match('[^:]*://([^/]*)/.*', url).group(1)\
144
or mm_cfg.DEFAULT_URL_HOST
145
url = mm_cfg.PUBLIC_ARCHIVE_URL % {
146
'listname': self.internal_name(),
149
if not url.endswith('/'):
153
def __archive_file(self, afn):
154
"""Open (creating, if necessary) the named archive file."""
155
omask = os.umask(002)
157
return Mailbox.Mailbox(open(afn, 'a+'))
162
# old ArchiveMail function, retained under a new name
163
# for optional archiving to an mbox
165
def __archive_to_mbox(self, post):
166
"""Retain a text copy of the message in an mbox file."""
168
afn = self.ArchiveFileName()
169
mbox = self.__archive_file(afn)
170
mbox.AppendMessage(post)
173
syslog('error', 'Archive file access failure:\n\t%s %s', afn, msg)
176
def ExternalArchive(self, ar, txt):
177
d = SafeDict({'listname': self.internal_name(),
178
'hostname': self.host_name,
181
extarch = os.popen(cmd, 'w')
183
status = extarch.close()
185
syslog('error', 'external archiver non-zero exit status: %d\n',
186
(status & 0xff00) >> 8)
189
# archiving in real time this is called from list.post(msg)
191
def ArchiveMail(self, msg):
192
"""Store postings in mbox and/or pipermail archive, depending."""
193
# Fork so archival errors won't disrupt normal list delivery
194
if mm_cfg.ARCHIVE_TO_MBOX == -1:
197
# We don't need an extra archiver lock here because we know the list
198
# itself must be locked.
199
if mm_cfg.ARCHIVE_TO_MBOX in (1, 2):
200
self.__archive_to_mbox(msg)
201
if mm_cfg.ARCHIVE_TO_MBOX == 1:
202
# Archive to mbox only.
205
# should we use the internal or external archiver?
206
private_p = self.archive_private
207
if mm_cfg.PUBLIC_EXTERNAL_ARCHIVER and not private_p:
208
self.ExternalArchive(mm_cfg.PUBLIC_EXTERNAL_ARCHIVER, txt)
209
elif mm_cfg.PRIVATE_EXTERNAL_ARCHIVER and private_p:
210
self.ExternalArchive(mm_cfg.PRIVATE_EXTERNAL_ARCHIVER, txt)
212
# use the internal archiver
215
h = HyperArch.HyperArchive(self)
216
h.processUnixMailbox(f)
221
# called from MailList.MailList.Save()
223
def CheckHTMLArchiveDir(self):
224
# We need to make sure that the archive directory has the right perms
225
# for public vs private. If it doesn't exist, or some weird
226
# permissions errors prevent us from stating the directory, it's
227
# pointless to try to fix the perms, so we just return -scott
228
if mm_cfg.ARCHIVE_TO_MBOX == -1:
229
# Archiving is completely disabled, don't require the skeleton.
231
pubdir = Site.get_archpath(self.internal_name(), public=True)
232
privdir = self.archive_dir()
233
pubmbox = pubdir + '.mbox'
234
privmbox = privdir + '.mbox'
235
if self.archive_private:
239
# BAW: privdir or privmbox could be nonexistant. We'd get an
240
# OSError, ENOENT which should be caught and reported properly.
241
makelink(privdir, pubdir)
242
# Only make this link if the site has enabled public mbox files
243
if mm_cfg.PUBLIC_MBOX:
244
makelink(privmbox, pubmbox)