~futatuki/mailman/2.1-forbid-subscription

« back to all changes in this revision

Viewing changes to bin/update

  • Committer:
  • Date: 2003-01-02 05:25:50 UTC
  • Revision ID: vcs-imports@canonical.com-20030102052550-qqbl1i96tzg3bach
This commit was manufactured by cvs2svn to create branch
'Release_2_1-maint'.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#! @PYTHON@
 
2
#
 
3
# Copyright (C) 1998,1999,2000,2001,2002 by the Free Software Foundation, Inc.
 
4
#
 
5
# This program is free software; you can redistribute it and/or
 
6
# modify it under the terms of the GNU General Public License
 
7
# as published by the Free Software Foundation; either version 2
 
8
# of the License, or (at your option) any later version.
 
9
#
 
10
# This program is distributed in the hope that it will be useful,
 
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
# GNU General Public License for more details.
 
14
#
 
15
# You should have received a copy of the GNU General Public License
 
16
# along with this program; if not, write to the Free Software
 
17
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
18
 
 
19
"""Perform all necessary upgrades.
 
20
 
 
21
Usage: %(PROGRAM)s [options]
 
22
 
 
23
Options:
 
24
    -f/--force
 
25
        Force running the upgrade procedures.  Normally, if the version number
 
26
        of the installed Mailman matches the current version number (or a
 
27
        `downgrade' is detected), nothing will be done.
 
28
 
 
29
    -h/--help
 
30
        Print this text and exit.
 
31
 
 
32
Use this script to help you update to the latest release of Mailman from
 
33
some previous version.  It knows about versions back to 1.0b4 (?).
 
34
"""
 
35
 
 
36
import os
 
37
import md5
 
38
import sys
 
39
import time
 
40
import errno
 
41
import getopt
 
42
import shutil
 
43
import marshal
 
44
 
 
45
import paths
 
46
from Mailman import mm_cfg
 
47
from Mailman import Utils
 
48
from Mailman import MailList
 
49
from Mailman.LockFile import TimeOutError
 
50
from Mailman.i18n import _
 
51
from Mailman.Queue.Switchboard import Switchboard
 
52
from Mailman.OldStyleMemberships import OldStyleMemberships
 
53
from Mailman.MemberAdaptor import BYBOUNCE, ENABLED
 
54
 
 
55
FRESH = 0
 
56
NOTFRESH = -1
 
57
 
 
58
LMVFILE = os.path.join(mm_cfg.DATA_DIR, 'last_mailman_version')
 
59
PROGRAM = sys.argv[0]
 
60
 
 
61
 
 
62
 
 
63
def calcversions():
 
64
    # Returns a tuple of (lastversion, thisversion).  If the last version
 
65
    # could not be determined, lastversion will be FRESH or NOTFRESH,
 
66
    # depending on whether this installation appears to be fresh or not.  The
 
67
    # determining factor is whether there are files in the $var_prefix/logs
 
68
    # subdir or not.  The version numbers are HEX_VERSIONs.
 
69
    #
 
70
    # See if we stored the last updated version
 
71
    lastversion = None
 
72
    thisversion = mm_cfg.HEX_VERSION
 
73
    try:
 
74
        fp = open(LMVFILE)
 
75
        data = fp.read()
 
76
        fp.close()
 
77
        lastversion = int(data, 16)
 
78
    except (IOError, ValueError):
 
79
        pass
 
80
    #
 
81
    # try to figure out if this is a fresh install
 
82
    if lastversion is None:
 
83
        lastversion = FRESH
 
84
        try:
 
85
            if os.listdir(mm_cfg.LOG_DIR):
 
86
                lastversion = NOTFRESH
 
87
        except OSError:
 
88
            pass
 
89
    return (lastversion, thisversion)
 
90
 
 
91
 
 
92
 
 
93
def makeabs(relpath):
 
94
    return os.path.join(mm_cfg.PREFIX, relpath)
 
95
 
 
96
def make_varabs(relpath):
 
97
    return os.path.join(mm_cfg.VAR_PREFIX, relpath)
 
98
 
 
99
 
 
100
def move_language_templates(mlist):
 
101
    listname = mlist.internal_name()
 
102
    print _('Fixing language templates: %(listname)s')
 
103
    # Mailman 2.1 has a new cascading search for its templates, defined and
 
104
    # described in Utils.py:maketext().  Putting templates in the top level
 
105
    # templates/ subdir or the lists/<listname> subdir is deprecated and no
 
106
    # longer searched..
 
107
    #
 
108
    # What this means is that most templates can live in the global templates/
 
109
    # subdirectory, and only needs to be copied into the list-, vhost-, or
 
110
    # site-specific language directories when needed.
 
111
    #
 
112
    # Also, by default all standard (i.e. English) templates must now live in
 
113
    # the templates/en directory.  This update cleans up all the templates,
 
114
    # deleting more-specific duplicates (as calculated by md5 checksums) in
 
115
    # favor of more-global locations.
 
116
    #
 
117
    # First, get rid of any lists/<list> template or lists/<list>/en template
 
118
    # that is identical to the global templates/* default.
 
119
    for gtemplate in os.listdir(os.path.join(mm_cfg.TEMPLATE_DIR, 'en')):
 
120
        # BAW: get rid of old templates, e.g. admlogin.txt and
 
121
        # handle_opts.html
 
122
        try:
 
123
            fp = open(os.path.join(mm_cfg.TEMPLATE_DIR, gtemplate))
 
124
        except IOError, e:
 
125
            if e.errno <> errno.ENOENT: raise
 
126
            # No global template
 
127
            continue
 
128
 
 
129
        gcksum = md5.new(fp.read()).digest()
 
130
        fp.close()
 
131
        # Match against the lists/<list>/* template
 
132
        try:
 
133
            fp = open(os.path.join(mlist.fullpath(), gtemplate))
 
134
        except IOError, e:
 
135
            if e.errno <> errno.ENOENT: raise
 
136
        else:
 
137
            tcksum = md5.new(fp.read()).digest()
 
138
            fp.close()
 
139
            if gcksum == tcksum:
 
140
                os.unlink(os.path.join(mlist.fullpath(), gtemplate))
 
141
        # Match against the lists/<list>/*.prev template
 
142
        try:
 
143
            fp = open(os.path.join(mlist.fullpath(), gtemplate + '.prev'))
 
144
        except IOError, e:
 
145
            if e.errno <> errno.ENOENT: raise
 
146
        else:
 
147
            tcksum = md5.new(fp.read()).digest()
 
148
            fp.close()
 
149
            if gcksum == tcksum:
 
150
                os.unlink(os.path.join(mlist.fullpath(), gtemplate + '.prev'))
 
151
        # Match against the lists/<list>/en/* templates
 
152
        try:
 
153
            fp = open(os.path.join(mlist.fullpath(), 'en', gtemplate))
 
154
        except IOError, e:
 
155
            if e.errno <> errno.ENOENT: raise
 
156
        else:
 
157
            tcksum = md5.new(fp.read()).digest()
 
158
            fp.close()
 
159
            if gcksum == tcksum:
 
160
                os.unlink(os.path.join(mlist.fullpath(), 'en', gtemplate))
 
161
        # Match against the templates/* template
 
162
        try:
 
163
            fp = open(os.path.join(mm_cfg.TEMPLATE_DIR, gtemplate))
 
164
        except IOError, e:
 
165
            if e.errno <> errno.ENOENT: raise
 
166
        else:
 
167
            tcksum = md5.new(fp.read()).digest()
 
168
            fp.close()
 
169
            if gcksum == tcksum:
 
170
                os.unlink(os.path.join(mm_cfg.TEMPLATE_DIR, gtemplate))
 
171
        # Match against the templates/*.prev template
 
172
        try:
 
173
            fp = open(os.path.join(mm_cfg.TEMPLATE_DIR, gtemplate + '.prev'))
 
174
        except IOError, e:
 
175
            if e.errno <> errno.ENOENT: raise
 
176
        else:
 
177
            tcksum = md5.new(fp.read()).digest()
 
178
            fp.close()
 
179
            if gcksum == tcksum:
 
180
                os.unlink(os.path.join(mm_cfg.TEMPLATE_DIR,
 
181
                                       gtemplate + '.prev'))
 
182
 
 
183
 
 
184
 
 
185
def dolist(listname):
 
186
    errors = 0
 
187
    mlist = MailList.MailList(listname, lock=0)
 
188
    try:
 
189
        mlist.Lock(0.5)
 
190
    except TimeOutError:
 
191
        print >> sys.stderr, _('WARNING: could not acquire lock for list: '
 
192
                               '%(listname)s')
 
193
        return 1
 
194
 
 
195
    # Sanity check the invariant that every BYBOUNCE disabled member must have
 
196
    # bounce information.  Some earlier betas broke this.  BAW: we're
 
197
    # submerging below the MemberAdaptor interface, so skip this if we're not
 
198
    # using OldStyleMemberships.
 
199
    if isinstance(mlist._memberadaptor, OldStyleMemberships):
 
200
        noinfo = {}
 
201
        for addr, (reason, when) in mlist.delivery_status.items():
 
202
            if reason == BYBOUNCE and not mlist.bounce_info.has_key(addr):
 
203
                noinfo[addr] = reason, when
 
204
        # What to do about these folks with a BYBOUNCE delivery status and no
 
205
        # bounce info?  This number should be very small, and I think it's
 
206
        # fine to simple re-enable them and let the bounce machinery
 
207
        # re-disable them if necessary.
 
208
        n = len(noinfo)
 
209
        if n > 0:
 
210
            print _(
 
211
                'Resetting %(n)s BYBOUNCEs disabled addrs with no bounce info')
 
212
            for addr in noinfo.keys():
 
213
                mlist.setDeliveryStatus(addr, ENABLED)
 
214
 
 
215
    # Update the held requests database
 
216
    print _("""Updating the held requests database.""")
 
217
    mlist._UpdateRecords()
 
218
 
 
219
    mbox_dir = make_varabs('archives/private/%s.mbox' % (listname))
 
220
    mbox_file = make_varabs('archives/private/%s.mbox/%s' % (listname,
 
221
                                                             listname))
 
222
 
 
223
    o_pub_mbox_file = make_varabs('archives/public/%s' % (listname))
 
224
    o_pri_mbox_file = make_varabs('archives/private/%s' % (listname))
 
225
 
 
226
    html_dir = o_pri_mbox_file
 
227
    o_html_dir = makeabs('public_html/archives/%s' % (listname))
 
228
    #
 
229
    # make the mbox directory if it's not there.
 
230
    #
 
231
    if not os.path.exists(mbox_dir):
 
232
        ou = os.umask(0)
 
233
        os.mkdir(mbox_dir, 02775)
 
234
        os.umask(ou)
 
235
    else:
 
236
        # this shouldn't happen, but hey, just in case
 
237
        if not os.path.isdir(mbox_dir):
 
238
            print _("""\
 
239
For some reason, %(mbox_dir)s exists as a file.  This won't work with
 
240
b6, so I'm renaming it to %(mbox_dir)s.tmp and proceeding.""")
 
241
            os.rename(mbox_dir, "%s.tmp" % (mbox_dir))
 
242
            ou = os.umask(0)
 
243
            os.mkdir(mbox_dir, 02775)
 
244
            os.umask(ou)
 
245
 
 
246
    # Move any existing mboxes around, but watch out for both a public and a
 
247
    # private one existing
 
248
    if os.path.isfile(o_pri_mbox_file) and os.path.isfile(o_pub_mbox_file):
 
249
        if mlist.archive_private:
 
250
            print _("""\
 
251
 
 
252
%(listname)s has both public and private mbox archives.  Since this list
 
253
currently uses private archiving, I'm installing the private mbox archive
 
254
-- %(o_pri_mbox_file)s -- as the active archive, and renaming
 
255
        %(o_pub_mbox_file)s
 
256
to
 
257
        %(o_pub_mbox_file)s.preb6
 
258
 
 
259
You can integrate that into the archives if you want by using the 'arch'
 
260
script.
 
261
""") % (mlist._internal_name, o_pri_mbox_file, o_pub_mbox_file,
 
262
        o_pub_mbox_file)
 
263
            os.rename(o_pub_mbox_file, "%s.preb6" % (o_pub_mbox_file))
 
264
        else:
 
265
            print _("""\
 
266
%s has both public and private mbox archives.  Since this list
 
267
currently uses public archiving, I'm installing the public mbox file
 
268
archive file (%s) as the active one, and renaming
 
269
        %s
 
270
    to
 
271
        %s.preb6
 
272
 
 
273
You can integrate that into the archives if you want by using the 'arch'
 
274
script.
 
275
""") % (mlist._internal_name, o_pub_mbox_file, o_pri_mbox_file,
 
276
        o_pri_mbox_file)
 
277
            os.rename(o_pri_mbox_file, "%s.preb6" % (o_pri_mbox_file))
 
278
    #
 
279
    # move private archive mbox there if it's around
 
280
    # and take into account all sorts of absurdities
 
281
    #
 
282
    print _('- updating old private mbox file')
 
283
    if os.path.exists(o_pri_mbox_file):
 
284
        if os.path.isfile(o_pri_mbox_file):
 
285
            os.rename(o_pri_mbox_file, mbox_file)
 
286
        elif not os.path.isdir(o_pri_mbox_file):
 
287
            newname = "%s.mm_install-dunno_what_this_was_but_its_in_the_way" \
 
288
                      % o_pri_mbox_file
 
289
            os.rename(o_pri_mbox_file, newname)
 
290
            print _("""\
 
291
    unknown file in the way, moving
 
292
        %(o_pri_mbox_file)s
 
293
    to
 
294
        %(newname)s""")
 
295
        else:
 
296
            # directory
 
297
            print _("""\
 
298
    looks like you have a really recent CVS installation...
 
299
    you're either one brave soul, or you already ran me""")
 
300
 
 
301
 
 
302
    #
 
303
    # move public archive mbox there if it's around
 
304
    # and take into account all sorts of absurdities.
 
305
    #
 
306
    print _('- updating old public mbox file')
 
307
    if os.path.exists(o_pub_mbox_file):
 
308
        if os.path.isfile(o_pub_mbox_file):
 
309
            os.rename(o_pub_mbox_file, mbox_file)
 
310
        elif not os.path.isdir(o_pub_mbox_file):
 
311
            newname = "%s.mm_install-dunno_what_this_was_but_its_in_the_way" \
 
312
                      % o_pub_mbox_file
 
313
            os.rename(o_pub_mbox_file, newname)
 
314
            print _("""\
 
315
    unknown file in the way, moving
 
316
        %(o_pub_mbox_file)s
 
317
    to
 
318
        %(newname)s""")
 
319
        else: # directory
 
320
            print _("""\
 
321
    looks like you have a really recent CVS installation...
 
322
    you're either one brave soul, or you already ran me""")
 
323
 
 
324
    #
 
325
    # move the html archives there
 
326
    #
 
327
    if os.path.isdir(o_html_dir):
 
328
        os.rename(o_html_dir, html_dir)
 
329
        #
 
330
        # chmod the html archives
 
331
        #
 
332
        os.chmod(html_dir, 02775)
 
333
    # BAW: Is this still necessary?!
 
334
    mlist.Save()
 
335
    #
 
336
    # check to see if pre-b4 list-specific templates are around
 
337
    # and move them to the new place if there's not already
 
338
    # a new one there
 
339
    #
 
340
    tmpl_dir = os.path.join(mm_cfg.PREFIX, "templates")
 
341
    list_dir = os.path.join(mm_cfg.PREFIX, "lists")
 
342
    b4_tmpl_dir = os.path.join(tmpl_dir, mlist._internal_name)
 
343
    new_tmpl_dir = os.path.join(list_dir, mlist._internal_name)
 
344
    if os.path.exists(b4_tmpl_dir):
 
345
        print _("""\
 
346
- This list looks like it might have <= b4 list templates around""")
 
347
        for f in os.listdir(b4_tmpl_dir):
 
348
            o_tmpl = os.path.join(b4_tmpl_dir, f)
 
349
            n_tmpl = os.path.join(new_tmpl_dir, f)
 
350
            if not os.path.exists(n_tmpl):
 
351
                os.rename(o_tmpl, n_tmpl)
 
352
                print _('- moved %(o_tmpl)s to %(n_tmpl)s')
 
353
            else:
 
354
                print _("""\
 
355
- both %(o_tmpl)s and %(n_tmpl)s exist, leaving untouched""")
 
356
    #
 
357
    # Move all the templates to the en language subdirectory as required for
 
358
    # Mailman 2.1
 
359
    #
 
360
    move_language_templates(mlist)
 
361
    # Avoid eating filehandles with the list lockfiles
 
362
    mlist.Unlock()
 
363
    return 0
 
364
 
 
365
 
 
366
 
 
367
def archive_path_fixer(unused_arg, dir, files):
 
368
    # Passed to os.path.walk to fix the perms on old html archives.
 
369
    for f in files:
 
370
        abs = os.path.join(dir, f)
 
371
        if os.path.isdir(abs):
 
372
            if f == "database":
 
373
                os.chmod(abs, 02770)
 
374
            else:
 
375
                os.chmod(abs, 02775)
 
376
        elif os.path.isfile(abs):
 
377
            os.chmod(abs, 0664)
 
378
 
 
379
def remove_old_sources(module):
 
380
    # Also removes old directories.
 
381
    src = '%s/%s' % (mm_cfg.PREFIX, module)
 
382
    pyc = src + "c"
 
383
    if os.path.isdir(src):
 
384
        print _('removing directory %(src)s and everything underneath')
 
385
        shutil.rmtree(src)
 
386
    elif os.path.exists(src):
 
387
        print _('removing %(src)s')
 
388
        try:
 
389
            os.unlink(src)
 
390
        except os.error, rest:
 
391
            print _("Warning: couldn't remove %(src)s -- %(rest)s")
 
392
    if module.endswith('.py') and os.path.exists(pyc):
 
393
        try:
 
394
            os.unlink(pyc)
 
395
        except os.error, rest:
 
396
            print _("couldn't remove old file %(pyc)s -- %(rest)s")
 
397
 
 
398
 
 
399
def update_qfiles():
 
400
    print _('updating old qfiles')
 
401
    prefix = `time.time()` + '+'
 
402
    # Be sure the qfiles/in directory exists (we don't really need the
 
403
    # switchboard object, but it's convenient for creating the directory).
 
404
    sb = Switchboard(mm_cfg.INQUEUE_DIR)
 
405
    for file in os.listdir(mm_cfg.QUEUE_DIR):
 
406
        # Updating means just mokving the .db and .msg files to qfiles/in where
 
407
        # it should be dequeued, converted, and processed normally.
 
408
        if file.endswith('.msg'):
 
409
            oldmsgfile = os.path.join(mm_cfg.QUEUE_DIR, file)
 
410
            newmsgfile = os.path.join(mm_cfg.INQUEUE_DIR, prefix + file)
 
411
            os.rename(oldmsgfile, newmsgfile)
 
412
        elif file.endswith('.db'):
 
413
            olddbfile = os.path.join(mm_cfg.QUEUE_DIR, file)
 
414
            newdbfile = os.path.join(mm_cfg.INQUEUE_DIR, prefix + file)
 
415
            os.rename(olddbfile, newdbfile)
 
416
 
 
417
 
 
418
 
 
419
def main():
 
420
    errors = 0
 
421
    # get rid of old stuff
 
422
    print _('getting rid of old source files')
 
423
    for mod in ('Mailman/Archiver.py', 'Mailman/HyperArch.py',
 
424
                'Mailman/HyperDatabase.py', 'Mailman/pipermail.py',
 
425
                'Mailman/smtplib.py', 'Mailman/Cookie.py',
 
426
                'bin/update_to_10b6', 'scripts/mailcmd',
 
427
                'scripts/mailowner', 'mail/wrapper', 'Mailman/pythonlib',
 
428
                'cgi-bin/archives', 'Mailman/MailCommandHandler'):
 
429
        remove_old_sources(mod)
 
430
    listnames = Utils.list_names()
 
431
    if not listnames:
 
432
        print _('no lists == nothing to do, exiting')
 
433
        return
 
434
    #
 
435
    # for people with web archiving, make sure the directories
 
436
    # in the archiving are set with proper perms for b6.
 
437
    #
 
438
    if os.path.isdir("%s/public_html/archives" % mm_cfg.PREFIX):
 
439
        print _("""\
 
440
fixing all the perms on your old html archives to work with b6
 
441
If your archives are big, this could take a minute or two...""")
 
442
        os.path.walk("%s/public_html/archives" % mm_cfg.PREFIX,
 
443
                     archive_path_fixer, "")
 
444
        print _('done')
 
445
    for listname in listnames:
 
446
        print _('Updating mailing list: %(listname)s')
 
447
        errors = errors + dolist(listname)
 
448
        print
 
449
    print _('Updating Usenet watermarks')
 
450
    wmfile = os.path.join(mm_cfg.DATA_DIR, 'gate_watermarks')
 
451
    try:
 
452
        fp = open(wmfile)
 
453
    except IOError:
 
454
        print _('- nothing to update here')
 
455
    else:
 
456
        d = marshal.load(fp)
 
457
        fp.close()
 
458
        for listname in d.keys():
 
459
            if listname not in listnames:
 
460
                # this list no longer exists
 
461
                continue
 
462
            mlist = MailList.MailList(listname, lock=0)
 
463
            try:
 
464
                mlist.Lock(0.5)
 
465
            except TimeOutError:
 
466
                print >> sys.stderr, _(
 
467
                    'WARNING: could not acquire lock for list: %(listname)s')
 
468
                errors = errors + 1
 
469
            else:
 
470
                # Pre 1.0b7 stored 0 in the gate_watermarks file to indicate
 
471
                # that no gating had been done yet.  Without coercing this to
 
472
                # None, the list could now suddenly get flooded.
 
473
                mlist.usenet_watermark = d[listname] or None
 
474
                mlist.Save()
 
475
                mlist.Unlock()
 
476
        os.unlink(wmfile)
 
477
        print _('- usenet watermarks updated and gate_watermarks removed')
 
478
    #
 
479
    # In Mailman 2.1, the pending database format and file name has changed.
 
480
    #
 
481
    oldpendingfile = os.path.join(mm_cfg.DATA_DIR, 'pending_subscriptions.db')
 
482
    try:
 
483
        fp = open(oldpendingfile)
 
484
    except IOError, e:
 
485
        if e.errno <> errno.ENOENT: raise
 
486
    else:
 
487
        print _('Updating old pending_subscriptions.db database')
 
488
        from Mailman import Pending
 
489
        db = marshal.load(fp)
 
490
        Pending._update(db)
 
491
        fp.close()
 
492
        os.unlink(oldpendingfile)
 
493
    #
 
494
    # In Mailman 2.1, the qfiles directory has a different structure and a
 
495
    # different content.
 
496
    #
 
497
    update_qfiles()
 
498
    #
 
499
    # This warning was necessary for the upgrade from 1.0b9 to 1.0b10.
 
500
    # There's no good way of figuring this out for releases prior to 2.0beta2
 
501
    # :(
 
502
    #
 
503
    if lastversion == NOTFRESH:
 
504
        print _("""
 
505
 
 
506
NOTE NOTE NOTE NOTE NOTE
 
507
 
 
508
    You are upgrading an existing Mailman installation, but I can't tell what
 
509
    version you were previously running.
 
510
 
 
511
    If you are upgrading from Mailman 1.0b9 or earlier you will need to
 
512
    manually update your mailing lists.  For each mailing list you need to
 
513
    copy the file templates/options.html lists/<listname>/options.html.
 
514
 
 
515
    However, if you have edited this file via the Web interface, you will have
 
516
    to merge your changes into this file, otherwise you will lose your
 
517
    changes.
 
518
 
 
519
NOTE NOTE NOTE NOTE NOTE
 
520
 
 
521
""")
 
522
    return errors
 
523
 
 
524
 
 
525
 
 
526
def usage(code, msg=''):
 
527
    if code:
 
528
        fd = sys.stderr
 
529
    else:
 
530
        fd = sys.stdout
 
531
    print >> fd, _(__doc__) % globals()
 
532
    if msg:
 
533
        print >> sys.stderr, msg
 
534
    sys.exit(code)
 
535
 
 
536
 
 
537
 
 
538
if __name__ == '__main__':
 
539
    try:
 
540
        opts, args = getopt.getopt(sys.argv[1:], 'hf',
 
541
                                   ['help', 'force'])
 
542
    except getopt.error, msg:
 
543
        usage(1, msg)
 
544
 
 
545
    if args:
 
546
        usage(1, 'Unexpected arguments: %s' % args)
 
547
 
 
548
    force = 0
 
549
    for opt, arg in opts:
 
550
        if opt in ('-h', '--help'):
 
551
            usage(0)
 
552
        elif opt in ('-f', '--force'):
 
553
            force = 1
 
554
 
 
555
    # calculate the versions
 
556
    lastversion, thisversion = calcversions()
 
557
    hexlversion = hex(lastversion)
 
558
    hextversion = hex(thisversion)
 
559
    if lastversion == thisversion and not force:
 
560
        # nothing to do
 
561
        print _('No updates are necessary.')
 
562
        sys.exit(0)
 
563
    if lastversion > thisversion and not force:
 
564
        print _("""\
 
565
Downgrade detected, from version %(hexlversion)s to version %(hextversion)s
 
566
This is probably not safe.
 
567
Exiting.""")
 
568
        sys.exit(1)
 
569
    print _('Upgrading from version %(hexlversion)s to %(hextversion)s')
 
570
    errors = main()
 
571
    if not errors:
 
572
        # Record the version we just upgraded to
 
573
        fp = open(LMVFILE, 'w')
 
574
        fp.write(hex(mm_cfg.HEX_VERSION) + '\n')
 
575
        fp.close()
 
576
    else:
 
577
        lockdir = mm_cfg.LOCK_DIR
 
578
        print _('''\
 
579
 
 
580
ERROR:
 
581
 
 
582
The locks for some lists could not be acquired.  This means that either
 
583
Mailman was still active when you upgraded, or there were stale locks in the
 
584
%(lockdir)s directory.
 
585
 
 
586
You must put Mailman into a quiescent state and remove all stale locks, then
 
587
re-run "make update" manually.  See the INSTALL and UPGRADE files for details.
 
588
''')