~maxb/debian/sid/mailman/red-bean

« back to all changes in this revision

Viewing changes to .pc/79_archiver_slash.patch/Mailman/Archiver/Archiver.py

  • Committer: Bazaar Package Importer
  • Author(s): Thijs Kinkhorst
  • Date: 2010-07-13 21:35:40 UTC
  • mto: This revision was merged to the branch mainline in revision 14.
  • Revision ID: james.westby@ubuntu.com-20100713213540-g9zrs50cua0o8f5h
Tags: 1:2.1.13-3
* Drop unneeded Indexes option from shipped apache.conf.
* Eliminate update_rc.d warning by not passing runlevel 1 at stop.
* Add 25_site_logo patch by Paul Wise (closes: #267243).
* Do not compress PDF's under /u/s/d/mailman (closes: #582259).
* Back up ./configure before running autoconf, so it can be restored
  in clean as not to generate irrelevant diff.gz content.
* Switch to dpkg-source 3.0 (quilt) format.
* Checked for policy 3.9.0, no changes needed.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 1998-2003 by the Free Software Foundation, Inc.
 
2
#
 
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.
 
7
#
 
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.
 
12
#
 
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.
 
16
 
 
17
 
 
18
"""Mixin class for putting new messages in the right place for archival.
 
19
 
 
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
 
22
archival.
 
23
"""
 
24
 
 
25
import os
 
26
import errno
 
27
import traceback
 
28
import re
 
29
from cStringIO import StringIO
 
30
 
 
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 _
 
38
 
 
39
try:
 
40
    True, False
 
41
except NameError:
 
42
    True = 1
 
43
    False = 0
 
44
 
 
45
 
 
46
 
 
47
def makelink(old, new):
 
48
    try:
 
49
        os.symlink(old, new)
 
50
    except OSError, e:
 
51
        if e.errno <> errno.EEXIST:
 
52
            raise
 
53
 
 
54
def breaklink(link):
 
55
    try:
 
56
        os.unlink(link)
 
57
    except OSError, e:
 
58
        if e.errno <> errno.ENOENT:
 
59
            raise
 
60
 
 
61
 
 
62
 
 
63
class Archiver:
 
64
    #
 
65
    # Interface to Pipermail.  HyperArch.py uses this method to get the
 
66
    # archive directory for the mailing list
 
67
    #
 
68
    def InitVars(self):
 
69
        # Configurable
 
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:
 
76
        #
 
77
        # archives/
 
78
        #     private/
 
79
        #         listname.mbox/
 
80
        #             listname.mbox
 
81
        #         listname/
 
82
        #             lots-of-pipermail-stuff
 
83
        #     public/
 
84
        #         listname.mbox@ -> ../private/listname.mbox
 
85
        #         listname@ -> ../private/listname
 
86
        #
 
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
 
92
        # symbolic links.
 
93
        omask = os.umask(0)
 
94
        try:
 
95
            try:
 
96
                os.mkdir(self.archive_dir()+'.mbox', 02775)
 
97
            except OSError, e:
 
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.
 
103
            try:
 
104
                os.mkdir(self.archive_dir(), 02775)
 
105
            except OSError, e:
 
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')
 
110
            fp = None
 
111
            try:
 
112
                fp = open(indexfile)
 
113
            except IOError, e:
 
114
                if e.errno <> errno.ENOENT: raise
 
115
                omask = os.umask(002)
 
116
                try:
 
117
                    fp = open(indexfile, 'w')
 
118
                finally:
 
119
                    os.umask(omask)
 
120
                fp.write(Utils.maketext(
 
121
                    'emptyarchive.html',
 
122
                    {'listname': self.real_name,
 
123
                     'listinfo': self.GetScriptURL('listinfo', absolute=1),
 
124
                     }, mlist=self))
 
125
            if fp:
 
126
                fp.close()
 
127
        finally:
 
128
            os.umask(omask)
 
129
 
 
130
    def archive_dir(self):
 
131
        return Site.get_archpath(self.internal_name())
 
132
 
 
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')
 
137
 
 
138
    def GetBaseArchiveURL(self):
 
139
        url = self.GetScriptURL('private', absolute=1) + '/'
 
140
        if self.archive_private:
 
141
            return url
 
142
        else:
 
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(),
 
147
                'hostname': hostname
 
148
                }
 
149
            if not url.endswith('/'):
 
150
                url += '/'
 
151
            return url
 
152
 
 
153
    def __archive_file(self, afn):
 
154
        """Open (creating, if necessary) the named archive file."""
 
155
        omask = os.umask(002)
 
156
        try:
 
157
            return Mailbox.Mailbox(open(afn, 'a+'))
 
158
        finally:
 
159
            os.umask(omask)
 
160
 
 
161
    #
 
162
    # old ArchiveMail function, retained under a new name
 
163
    # for optional archiving to an mbox
 
164
    #
 
165
    def __archive_to_mbox(self, post):
 
166
        """Retain a text copy of the message in an mbox file."""
 
167
        try:
 
168
            afn = self.ArchiveFileName()
 
169
            mbox = self.__archive_file(afn)
 
170
            mbox.AppendMessage(post)
 
171
            mbox.fp.close()
 
172
        except IOError, msg:
 
173
            syslog('error', 'Archive file access failure:\n\t%s %s', afn, msg)
 
174
            raise
 
175
 
 
176
    def ExternalArchive(self, ar, txt):
 
177
        d = SafeDict({'listname': self.internal_name(),
 
178
                      'hostname': self.host_name,
 
179
                      })
 
180
        cmd = ar % d
 
181
        extarch = os.popen(cmd, 'w')
 
182
        extarch.write(txt)
 
183
        status = extarch.close()
 
184
        if status:
 
185
            syslog('error', 'external archiver non-zero exit status: %d\n',
 
186
                   (status & 0xff00) >> 8)
 
187
 
 
188
    #
 
189
    # archiving in real time  this is called from list.post(msg)
 
190
    #
 
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:
 
195
            return
 
196
        #
 
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.
 
203
                return
 
204
        txt = str(msg)
 
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)
 
211
        else:
 
212
            # use the internal archiver
 
213
            f = StringIO(txt)
 
214
            import HyperArch
 
215
            h = HyperArch.HyperArchive(self)
 
216
            h.processUnixMailbox(f)
 
217
            h.close()
 
218
            f.close()
 
219
 
 
220
    #
 
221
    # called from MailList.MailList.Save()
 
222
    #
 
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.
 
230
            return
 
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:
 
236
            breaklink(pubdir)
 
237
            breaklink(pubmbox)
 
238
        else:
 
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)