Status: | Rejected |
---|---|
Rejected by: | Barry Warsaw |
Proposed branch: | lp:~mmlmtp/mailman/mm3lmtp |
Merge into: | lp:mailman |
Diff against target: |
568979 lines (+565598/-0) (has conflicts) 627 files modified
PKG-INFO (+14/-0) README.txt (+197/-0) TODO.txt (+15/-0) bin/clone_member (+219/-0) bin/discard (+120/-0) bin/fix_url.py (+93/-0) bin/list_admins (+101/-0) bin/msgfmt.py (+203/-0) bin/po2templ.py (+90/-0) bin/pygettext.py (+545/-0) bin/remove_members (+186/-0) bin/reset_pw.py (+83/-0) bin/sync_members (+286/-0) bin/templ2pot.py (+120/-0) bin/transcheck (+412/-0) contrib/README (+4/-0) contrib/README.check_perms_grsecurity (+14/-0) contrib/README.mm-handler (+215/-0) contrib/README.mmdsr (+45/-0) contrib/auto (+116/-0) contrib/check_perms_grsecurity.py (+182/-0) contrib/mailman.mc (+143/-0) contrib/majordomo2mailman.pl (+691/-0) contrib/mm-handler (+236/-0) contrib/mmdsr (+572/-0) contrib/qmail-to-mailman.py (+116/-0) contrib/rotatelogs.py (+104/-0) contrib/virtusertable (+37/-0) cron/crontab.in.in (+21/-0) data/coverage.py (+952/-0) data/mailman.in (+54/-0) data/paths.py.in (+90/-0) docs/ACKNOWLEDGMENTS.txt (+251/-0) docs/ALPHA.txt (+59/-0) docs/NEWS.txt (+181/-0) docs/OLD-NEWS.txt (+2835/-0) docs/STYLEGUIDE.txt (+162/-0) docs/gnu-COPYING-GPL (+340/-0) docs/man/add_members.1 (+60/-0) docs/man/check_db.1 (+60/-0) docs/man/check_perms.1 (+46/-0) docs/man/clone_member.1 (+71/-0) docs/man/find_member.1 (+64/-0) docs/man/list_members.1 (+78/-0) docs/man/remove_members.1 (+63/-0) docs/man/sync_members.1 (+81/-0) docs/man/transcheck.1 (+41/-0) docs/posting-flow-chart.ps (+735/-0) ez_setup.py (+228/-0) foo.members (+2/-0) mailman.egg-info/PKG-INFO (+14/-0) mailman.egg-info/SOURCES.txt (+598/-0) mailman.egg-info/dependency_links.txt (+1/-0) mailman.egg-info/entry_points.txt (+63/-0) mailman.egg-info/requires.txt (+5/-0) mailman.egg-info/top_level.txt (+1/-0) mailman/Archiver/Archiver.py (+231/-0) mailman/Archiver/HyperArch.py (+1245/-0) mailman/Archiver/HyperDatabase.py (+339/-0) mailman/Archiver/__init__.py (+18/-0) mailman/Archiver/pipermail.py (+874/-0) mailman/Bouncer.py (+250/-0) mailman/Bouncers/BouncerAPI.py (+64/-0) mailman/Bouncers/Caiwireless.py (+46/-0) mailman/Bouncers/Compuserve.py (+46/-0) mailman/Bouncers/DSN.py (+100/-0) mailman/Bouncers/Exchange.py (+47/-0) mailman/Bouncers/Exim.py (+31/-0) mailman/Bouncers/GroupWise.py (+71/-0) mailman/Bouncers/LLNL.py (+32/-0) mailman/Bouncers/Microsoft.py (+53/-0) mailman/Bouncers/Netscape.py (+89/-0) mailman/Bouncers/Postfix.py (+85/-0) mailman/Bouncers/Qmail.py (+72/-0) mailman/Bouncers/SMTP32.py (+60/-0) mailman/Bouncers/SimpleMatch.py (+204/-0) mailman/Bouncers/SimpleWarning.py (+62/-0) mailman/Bouncers/Sina.py (+47/-0) mailman/Bouncers/Yahoo.py (+54/-0) mailman/Bouncers/Yale.py (+80/-0) mailman/Cgi/Auth.py (+60/-0) mailman/Cgi/admin.py (+1433/-0) mailman/Cgi/admindb.py (+813/-0) mailman/Cgi/confirm.py (+834/-0) mailman/Cgi/create.py (+396/-0) mailman/Cgi/edithtml.py (+175/-0) mailman/Cgi/listinfo.py (+207/-0) mailman/Cgi/options.py (+1000/-0) mailman/Cgi/private.py (+190/-0) mailman/Cgi/rmlist.py (+243/-0) mailman/Cgi/roster.py (+129/-0) mailman/Cgi/subscribe.py (+252/-0) mailman/Cgi/wsgi_app.py (+286/-0) mailman/Commands/cmd_confirm.py (+98/-0) mailman/Commands/cmd_echo.py (+26/-0) mailman/Commands/cmd_end.py (+33/-0) mailman/Commands/cmd_help.py (+92/-0) mailman/Commands/cmd_info.py (+49/-0) mailman/Commands/cmd_join.py (+20/-0) mailman/Commands/cmd_leave.py (+20/-0) mailman/Commands/cmd_lists.py (+65/-0) mailman/Commands/cmd_password.py (+122/-0) mailman/Commands/cmd_remove.py (+20/-0) mailman/Commands/cmd_set.py (+360/-0) mailman/Commands/cmd_stop.py (+20/-0) mailman/Commands/cmd_subscribe.py (+133/-0) mailman/Commands/cmd_unsubscribe.py (+87/-0) mailman/Commands/cmd_who.py (+152/-0) mailman/Defaults.py (+1347/-0) mailman/Deliverer.py (+174/-0) mailman/Digester.py (+57/-0) mailman/Errors.py (+195/-0) mailman/Gui/Archive.py (+45/-0) mailman/Gui/Autoresponse.py (+99/-0) mailman/Gui/Bounce.py (+195/-0) mailman/Gui/ContentFilter.py (+199/-0) mailman/Gui/Digest.py (+161/-0) mailman/Gui/GUIBase.py (+209/-0) mailman/Gui/General.py (+464/-0) mailman/Gui/Language.py (+128/-0) mailman/Gui/Membership.py (+34/-0) mailman/Gui/NonDigest.py (+158/-0) mailman/Gui/Passwords.py (+31/-0) mailman/Gui/Privacy.py (+537/-0) mailman/Gui/Topics.py (+162/-0) mailman/Gui/Usenet.py (+140/-0) mailman/Gui/__init__.py (+33/-0) mailman/HTMLFormatter.py (+437/-0) mailman/MTA/Manual.py (+139/-0) mailman/MTA/Postfix.py (+413/-0) mailman/MTA/Utils.py (+87/-0) mailman/MailList.py (+731/-0) mailman/Mailbox.py (+105/-0) mailman/Message.py (+309/-0) mailman/SafeDict.py (+55/-0) mailman/SecurityManager.py (+306/-0) mailman/UserDesc.py (+65/-0) mailman/Utils.py (+918/-0) mailman/app/archiving.py (+110/-0) mailman/app/bounces.py (+62/-0) mailman/app/chains.py (+115/-0) mailman/app/lifecycle.py (+117/-0) mailman/app/membership.py (+199/-0) mailman/app/moderator.py (+340/-0) mailman/app/pipelines.py (+122/-0) mailman/app/plugins.py (+65/-0) mailman/app/registrar.py (+144/-0) mailman/app/replybot.py (+123/-0) mailman/app/rules.py (+42/-0) mailman/app/styles.py (+294/-0) mailman/bin/__init__.py (+62/-0) mailman/bin/add_members.py (+187/-0) mailman/bin/arch.py (+152/-0) mailman/bin/bounces.py (+61/-0) mailman/bin/bumpdigests.py (+74/-0) mailman/bin/change_pw.py (+177/-0) mailman/bin/check_perms.py (+407/-0) mailman/bin/checkdbs.py (+199/-0) mailman/bin/cleanarch.py (+133/-0) mailman/bin/config_list.py (+332/-0) mailman/bin/confirm.py (+63/-0) mailman/bin/create_list.py (+134/-0) mailman/bin/disabled.py (+201/-0) mailman/bin/docs/master.txt (+44/-0) mailman/bin/dumpdb.py (+90/-0) mailman/bin/export.py (+314/-0) mailman/bin/find_member.py (+135/-0) mailman/bin/gate_news.py (+245/-0) mailman/bin/genaliases.py (+85/-0) mailman/bin/import.py (+315/-0) mailman/bin/inject.py (+91/-0) mailman/bin/join.py (+63/-0) mailman/bin/leave.py (+62/-0) mailman/bin/list_lists.py (+105/-0) mailman/bin/list_members.py (+201/-0) mailman/bin/list_owners.py (+88/-0) mailman/bin/mailmanctl.py (+234/-0) mailman/bin/make_instance.py (+173/-0) mailman/bin/master.py (+432/-0) mailman/bin/mmsitepass.py (+113/-0) mailman/bin/nightly_gzip.py (+117/-0) mailman/bin/owner.py (+68/-0) mailman/bin/post.py (+71/-0) mailman/bin/qrunner.py (+271/-0) mailman/bin/remove_list.py (+87/-0) mailman/bin/request.py (+65/-0) mailman/bin/senddigests.py (+83/-0) mailman/bin/set_members.py (+185/-0) mailman/bin/show_config.py (+97/-0) mailman/bin/show_qfiles.py (+65/-0) mailman/bin/testall.py (+283/-0) mailman/bin/unshunt.py (+75/-0) mailman/bin/update.py (+659/-0) mailman/bin/version.py (+46/-0) mailman/bin/withlist.py (+218/-0) mailman/chains/accept.py (+55/-0) mailman/chains/base.py (+119/-0) mailman/chains/builtin.py (+84/-0) mailman/chains/discard.py (+43/-0) mailman/chains/headers.py (+151/-0) mailman/chains/hold.py (+176/-0) mailman/chains/reject.py (+55/-0) mailman/configuration.py (+261/-0) mailman/constants.py (+39/-0) mailman/database/__init__.py (+153/-0) mailman/database/address.py (+91/-0) mailman/database/language.py (+30/-0) mailman/database/listmanager.py (+76/-0) mailman/database/mailinglist.py (+259/-0) mailman/database/mailman.sql (+209/-0) mailman/database/member.py (+97/-0) mailman/database/message.py (+42/-0) mailman/database/messagestore.py (+132/-0) mailman/database/model.py (+54/-0) mailman/database/pending.py (+167/-0) mailman/database/preferences.py (+40/-0) mailman/database/requests.py (+134/-0) mailman/database/roster.py (+266/-0) mailman/database/transaction.py (+51/-0) mailman/database/types.py (+57/-0) mailman/database/user.py (+85/-0) mailman/database/usermanager.py (+99/-0) mailman/database/version.py (+30/-0) mailman/docs/addresses.txt (+232/-0) mailman/docs/bounces.txt (+109/-0) mailman/docs/chains.txt (+341/-0) mailman/docs/languages.txt (+104/-0) mailman/docs/lifecycle.txt (+138/-0) mailman/docs/listmanager.txt (+89/-0) mailman/docs/membership.txt (+231/-0) mailman/docs/message.txt (+50/-0) mailman/docs/messagestore.txt (+114/-0) mailman/docs/mlist-addresses.txt (+75/-0) mailman/docs/pending.txt (+95/-0) mailman/docs/pipelines.txt (+174/-0) mailman/docs/registration.txt (+364/-0) mailman/docs/requests.txt (+856/-0) mailman/docs/styles.txt (+162/-0) mailman/docs/usermanager.txt (+125/-0) mailman/docs/users.txt (+196/-0) mailman/extras/mailman.cfg.in (+46/-0) mailman/htmlformat.py (+670/-0) mailman/i18n.py (+185/-0) mailman/initialize.py (+78/-0) mailman/inject.py (+78/-0) mailman/interact.py (+68/-0) mailman/interfaces/__init__.py (+78/-0) mailman/interfaces/address.py (+90/-0) mailman/interfaces/archiver.py (+58/-0) mailman/interfaces/chain.py (+98/-0) mailman/interfaces/database.py (+87/-0) mailman/interfaces/domain.py (+53/-0) mailman/interfaces/errors.py (+28/-0) mailman/interfaces/handler.py (+37/-0) mailman/interfaces/languages.py (+80/-0) mailman/interfaces/listmanager.py (+82/-0) mailman/interfaces/mailinglist.py (+269/-0) mailman/interfaces/member.py (+185/-0) mailman/interfaces/messages.py (+102/-0) mailman/interfaces/mlistrequest.py (+29/-0) mailman/interfaces/pending.py (+88/-0) mailman/interfaces/permissions.py (+28/-0) mailman/interfaces/pipeline.py (+32/-0) mailman/interfaces/preferences.py (+69/-0) mailman/interfaces/registrar.py (+75/-0) mailman/interfaces/requests.py (+105/-0) mailman/interfaces/roster.py (+53/-0) mailman/interfaces/rules.py (+45/-0) mailman/interfaces/runner.py (+31/-0) mailman/interfaces/styles.py (+111/-0) mailman/interfaces/switchboard.py (+82/-0) mailman/interfaces/user.py (+76/-0) mailman/interfaces/usermanager.py (+85/-0) mailman/languages.py (+60/-0) mailman/loginit.py (+182/-0) mailman/messages/ar/LC_MESSAGES/mailman.po (+15702/-0) mailman/messages/ca/LC_MESSAGES/mailman.po (+15421/-0) mailman/messages/cs/LC_MESSAGES/mailman.po (+13393/-0) mailman/messages/da/LC_MESSAGES/mailman.po (+15972/-0) mailman/messages/de/LC_MESSAGES/mailman.po (+15318/-0) mailman/messages/de/README.de (+21/-0) mailman/messages/docstring.files (+59/-0) mailman/messages/es/LC_MESSAGES/mailman.po (+16349/-0) mailman/messages/es/README.es (+82/-0) mailman/messages/et/LC_MESSAGES/mailman.po (+14338/-0) mailman/messages/eu/LC_MESSAGES/mailman.po (+14145/-0) mailman/messages/eu/README.eu (+103/-0) mailman/messages/fi/LC_MESSAGES/mailman.po (+13862/-0) mailman/messages/fi/README.fi (+13/-0) mailman/messages/fr/LC_MESSAGES/mailman.po (+15420/-0) mailman/messages/fr/README.fr (+7/-0) mailman/messages/hr/LC_MESSAGES/mailman.po (+13737/-0) mailman/messages/hu/FAQ.hu (+464/-0) mailman/messages/hu/INSTALL.hu (+640/-0) mailman/messages/hu/LC_MESSAGES/mailman.po (+14882/-0) mailman/messages/hu/README.BSD.hu (+28/-0) mailman/messages/hu/README.CONTRIB.hu (+17/-0) mailman/messages/hu/README.EXIM.hu (+359/-0) mailman/messages/hu/README.LINUX.hu (+59/-0) mailman/messages/hu/README.MACOSX.hu (+31/-0) mailman/messages/hu/README.NETSCAPE.hu (+57/-0) mailman/messages/hu/README.POSTFIX.hu (+239/-0) mailman/messages/hu/README.QMAIL.hu (+186/-0) mailman/messages/hu/README.SENDMAIL.hu (+86/-0) mailman/messages/hu/README.USERAGENT.hu (+49/-0) mailman/messages/hu/README.hu (+271/-0) mailman/messages/hu/UPGRADING.hu (+391/-0) mailman/messages/ia/LC_MESSAGES/mailman.po (+14426/-0) mailman/messages/it/LC_MESSAGES/mailman.po (+15623/-0) mailman/messages/it/README.it (+32/-0) mailman/messages/ja/INSTALL (+615/-0) mailman/messages/ja/LC_MESSAGES/mailman.po (+14328/-0) mailman/messages/ja/README (+214/-0) mailman/messages/ja/README.ja (+109/-0) mailman/messages/ja/UPGRADING (+215/-0) mailman/messages/ja/doc/Defaults.py.in (+1442/-0) mailman/messages/ja/doc/mailman-install.tex (+1933/-0) mailman/messages/ja/doc/mailman-member.tex (+1787/-0) mailman/messages/ko/LC_MESSAGES/mailman.po (+12970/-0) mailman/messages/ko/README.ko (+26/-0) mailman/messages/lt/LC_MESSAGES/mailman.po (+12116/-0) mailman/messages/mailman.pot (+10031/-0) mailman/messages/marked.files (+130/-0) mailman/messages/nl/LC_MESSAGES/mailman.po (+13629/-0) mailman/messages/no/LC_MESSAGES/mailman.po (+15503/-0) mailman/messages/pl/LC_MESSAGES/mailman.po (+13120/-0) mailman/messages/pl/README.pl (+28/-0) mailman/messages/pt/LC_MESSAGES/mailman.po (+14924/-0) mailman/messages/pt_BR/LC_MESSAGES/mailman.po (+15033/-0) mailman/messages/ro/LC_MESSAGES/mailman.po (+14633/-0) mailman/messages/ru/LC_MESSAGES/mailman.po (+14862/-0) mailman/messages/ru/README.ru (+17/-0) mailman/messages/sl/LC_MESSAGES/mailman.po (+17650/-0) mailman/messages/sr/LC_MESSAGES/mailman.po (+11873/-0) mailman/messages/sr/readme.sr (+6/-0) mailman/messages/sv/LC_MESSAGES/mailman.po (+18097/-0) mailman/messages/sv/README.sv (+30/-0) mailman/messages/tr/LC_MESSAGES/mailman.po (+13641/-0) mailman/messages/uk/LC_MESSAGES/mailman.po (+14897/-0) mailman/messages/vi/LC_MESSAGES/mailman.po (+14584/-0) mailman/messages/zh_CN/LC_MESSAGES/mailman.po (+14142/-0) mailman/messages/zh_TW/LC_MESSAGES/mailman.po (+12964/-0) mailman/options.py (+133/-0) mailman/passwords.py (+249/-0) mailman/pipeline/__init__.py (+50/-0) mailman/pipeline/acknowledge.py (+78/-0) mailman/pipeline/after_delivery.py (+44/-0) mailman/pipeline/avoid_duplicates.py (+113/-0) mailman/pipeline/calculate_recipients.py (+144/-0) mailman/pipeline/cleanse.py (+71/-0) mailman/pipeline/cleanse_dkim.py (+53/-0) mailman/pipeline/cook_headers.py (+358/-0) mailman/pipeline/decorate.py (+228/-0) mailman/pipeline/docs/ack-headers.txt (+41/-0) mailman/pipeline/docs/acknowledge.txt (+162/-0) mailman/pipeline/docs/after-delivery.txt (+28/-0) mailman/pipeline/docs/archives.txt (+133/-0) mailman/pipeline/docs/avoid-duplicates.txt (+169/-0) mailman/pipeline/docs/calc-recips.txt (+101/-0) mailman/pipeline/docs/cleanse.txt (+95/-0) mailman/pipeline/docs/cook-headers.txt (+328/-0) mailman/pipeline/docs/decorate.txt (+318/-0) mailman/pipeline/docs/digests.txt (+536/-0) mailman/pipeline/docs/file-recips.txt (+97/-0) mailman/pipeline/docs/filtering.txt (+341/-0) mailman/pipeline/docs/nntp.txt (+68/-0) mailman/pipeline/docs/reply-to.txt (+128/-0) mailman/pipeline/docs/replybot.txt (+216/-0) mailman/pipeline/docs/scrubber.txt (+214/-0) mailman/pipeline/docs/subject-munging.txt (+245/-0) mailman/pipeline/docs/tagger.txt (+237/-0) mailman/pipeline/docs/to-outgoing.txt (+155/-0) mailman/pipeline/file_recipients.py (+64/-0) mailman/pipeline/mime_delete.py (+280/-0) mailman/pipeline/moderate.py (+167/-0) mailman/pipeline/owner_recipients.py (+27/-0) mailman/pipeline/replybot.py (+130/-0) mailman/pipeline/scrubber.py (+520/-0) mailman/pipeline/smtp_direct.py (+433/-0) mailman/pipeline/tagger.py (+181/-0) mailman/pipeline/to_archive.py (+54/-0) mailman/pipeline/to_digest.py (+439/-0) mailman/pipeline/to_outgoing.py (+73/-0) mailman/pipeline/to_usenet.py (+66/-0) mailman/queue/__init__.py (+435/-0) mailman/queue/archive.py (+85/-0) mailman/queue/bounce.py (+316/-0) mailman/queue/command.py (+230/-0) mailman/queue/docs/OVERVIEW.txt (+78/-0) mailman/queue/docs/archiver.txt (+37/-0) mailman/queue/docs/incoming.txt (+203/-0) mailman/queue/docs/lmtp.txt (+166/-0) mailman/queue/docs/news.txt (+158/-0) mailman/queue/docs/outgoing.txt (+96/-0) mailman/queue/docs/runner.txt (+70/-0) mailman/queue/docs/switchboard.txt (+149/-0) mailman/queue/http.py (+73/-0) mailman/queue/incoming.py (+44/-0) mailman/queue/lmtp.py (+488/-0) mailman/queue/maildir.py (+189/-0) mailman/queue/news.py (+166/-0) mailman/queue/outgoing.py (+130/-0) mailman/queue/pipeline.py (+38/-0) mailman/queue/retry.py (+40/-0) mailman/queue/virgin.py (+42/-0) mailman/rules/__init__.py (+50/-0) mailman/rules/administrivia.py (+98/-0) mailman/rules/any.py (+41/-0) mailman/rules/approved.py (+117/-0) mailman/rules/docs/administrivia.txt (+100/-0) mailman/rules/docs/approve.txt (+473/-0) mailman/rules/docs/emergency.txt (+75/-0) mailman/rules/docs/header-matching.txt (+145/-0) mailman/rules/docs/implicit-dest.txt (+76/-0) mailman/rules/docs/loop.txt (+49/-0) mailman/rules/docs/max-size.txt (+40/-0) mailman/rules/docs/moderation.txt (+70/-0) mailman/rules/docs/news-moderation.txt (+37/-0) mailman/rules/docs/no-subject.txt (+34/-0) mailman/rules/docs/recipients.txt (+41/-0) mailman/rules/docs/rules.txt (+70/-0) mailman/rules/docs/suspicious.txt (+36/-0) mailman/rules/docs/truth.txt (+10/-0) mailman/rules/emergency.py (+44/-0) mailman/rules/implicit_dest.py (+95/-0) mailman/rules/loop.py (+44/-0) mailman/rules/max_recipients.py (+48/-0) mailman/rules/max_size.py (+46/-0) mailman/rules/moderation.py (+66/-0) mailman/rules/news_moderation.py (+44/-0) mailman/rules/no_subject.py (+42/-0) mailman/rules/suspicious.py (+94/-0) mailman/rules/truth.py (+41/-0) mailman/templates/en/adminaddrchgack.txt (+4/-0) mailman/templates/en/admindbdetails.html (+65/-0) mailman/templates/en/admindbpreamble.html (+10/-0) mailman/templates/en/admindbsummary.html (+14/-0) mailman/templates/en/adminsubscribeack.txt (+1/-0) mailman/templates/en/adminunsubscribeack.txt (+1/-0) mailman/templates/en/admlogin.html (+39/-0) mailman/templates/en/approve.txt (+15/-0) mailman/templates/en/archidxentry.html (+4/-0) mailman/templates/en/archidxfoot.html (+21/-0) mailman/templates/en/archidxhead.html (+24/-0) mailman/templates/en/archlistend.html (+1/-0) mailman/templates/en/archliststart.html (+4/-0) mailman/templates/en/archtoc.html (+20/-0) mailman/templates/en/archtocentry.html (+12/-0) mailman/templates/en/archtocnombox.html (+18/-0) mailman/templates/en/article.html (+50/-0) mailman/templates/en/bounce.txt (+13/-0) mailman/templates/en/checkdbs.txt (+7/-0) mailman/templates/en/convert.txt (+34/-0) mailman/templates/en/cronpass.txt (+19/-0) mailman/templates/en/disabled.txt (+25/-0) mailman/templates/en/emptyarchive.html (+15/-0) mailman/templates/en/headfoot.html (+28/-0) mailman/templates/en/help.txt (+33/-0) mailman/templates/en/invite.txt (+20/-0) mailman/templates/en/listinfo.html (+143/-0) mailman/templates/en/masthead.txt (+13/-0) mailman/templates/en/newlist.txt (+35/-0) mailman/templates/en/nomoretoday.txt (+8/-0) mailman/templates/en/options.html (+316/-0) mailman/templates/en/postack.txt (+8/-0) mailman/templates/en/postauth.txt (+13/-0) mailman/templates/en/postheld.txt (+15/-0) mailman/templates/en/private.html (+43/-0) mailman/templates/en/probe.txt (+25/-0) mailman/templates/en/refuse.txt (+13/-0) mailman/templates/en/roster.html (+52/-0) mailman/templates/en/subauth.txt (+11/-0) mailman/templates/en/subscribe.html (+8/-0) mailman/templates/en/subscribeack.txt (+25/-0) mailman/templates/en/unsub.txt (+23/-0) mailman/templates/en/unsubauth.txt (+11/-0) mailman/templates/en/userpass.txt (+24/-0) mailman/templates/en/verify.txt (+19/-0) mailman/tests/bounces/bounce_01.txt (+95/-0) mailman/tests/bounces/bounce_02.txt (+36/-0) mailman/tests/bounces/bounce_03.txt (+109/-0) mailman/tests/bounces/dsn_01.txt (+217/-0) mailman/tests/bounces/dsn_02.txt (+187/-0) mailman/tests/bounces/dsn_03.txt (+144/-0) mailman/tests/bounces/dsn_04.txt (+202/-0) mailman/tests/bounces/dsn_05.txt (+125/-0) mailman/tests/bounces/dsn_06.txt (+122/-0) mailman/tests/bounces/dsn_07.txt (+121/-0) mailman/tests/bounces/dsn_08.txt (+131/-0) mailman/tests/bounces/dsn_09.txt (+85/-0) mailman/tests/bounces/dsn_10.txt (+66/-0) mailman/tests/bounces/dsn_11.txt (+176/-0) mailman/tests/bounces/dsn_12.txt (+40/-0) mailman/tests/bounces/dsn_13.txt (+311/-0) mailman/tests/bounces/dsn_14.txt (+149/-0) mailman/tests/bounces/dsn_15.txt (+278/-0) mailman/tests/bounces/dumbass_01.txt (+109/-0) mailman/tests/bounces/exim_01.txt (+58/-0) mailman/tests/bounces/groupwise_01.txt (+151/-0) mailman/tests/bounces/groupwise_02.txt (+186/-0) mailman/tests/bounces/hotpop_01.txt (+180/-0) mailman/tests/bounces/llnl_01.txt (+203/-0) mailman/tests/bounces/microsoft_01.txt (+108/-0) mailman/tests/bounces/microsoft_02.txt (+119/-0) mailman/tests/bounces/microsoft_03.txt (+65/-0) mailman/tests/bounces/netscape_01.txt (+123/-0) mailman/tests/bounces/newmailru_01.txt (+112/-0) mailman/tests/bounces/postfix_01.txt (+123/-0) mailman/tests/bounces/postfix_02.txt (+60/-0) mailman/tests/bounces/postfix_03.txt (+145/-0) mailman/tests/bounces/postfix_04.txt (+240/-0) mailman/tests/bounces/postfix_05.txt (+231/-0) mailman/tests/bounces/qmail_01.txt (+103/-0) mailman/tests/bounces/qmail_02.txt (+73/-0) mailman/tests/bounces/qmail_03.txt (+245/-0) mailman/tests/bounces/qmail_04.txt (+81/-0) mailman/tests/bounces/qmail_05.txt (+121/-0) mailman/tests/bounces/sendmail_01.txt (+146/-0) mailman/tests/bounces/simple_01.txt (+153/-0) mailman/tests/bounces/simple_02.txt (+118/-0) mailman/tests/bounces/simple_03.txt (+68/-0) mailman/tests/bounces/simple_04.txt (+105/-0) mailman/tests/bounces/simple_05.txt (+81/-0) mailman/tests/bounces/simple_06.txt (+77/-0) mailman/tests/bounces/simple_07.txt (+21/-0) mailman/tests/bounces/simple_08.txt (+81/-0) mailman/tests/bounces/simple_09.txt (+27/-0) mailman/tests/bounces/simple_10.txt (+45/-0) mailman/tests/bounces/simple_11.txt (+68/-0) mailman/tests/bounces/simple_12.txt (+81/-0) mailman/tests/bounces/simple_13.txt (+60/-0) mailman/tests/bounces/simple_14.txt (+122/-0) mailman/tests/bounces/simple_15.txt (+259/-0) mailman/tests/bounces/simple_16.txt (+78/-0) mailman/tests/bounces/simple_17.txt (+76/-0) mailman/tests/bounces/simple_18.txt (+73/-0) mailman/tests/bounces/simple_19.txt (+77/-0) mailman/tests/bounces/simple_20.txt (+27/-0) mailman/tests/bounces/simple_21.txt (+46/-0) mailman/tests/bounces/simple_22.txt (+25/-0) mailman/tests/bounces/simple_23.txt (+152/-0) mailman/tests/bounces/simple_24.txt (+33/-0) mailman/tests/bounces/simple_25.txt (+379/-0) mailman/tests/bounces/simple_26.txt (+74/-0) mailman/tests/bounces/simple_27.txt (+279/-0) mailman/tests/bounces/sina_01.txt (+128/-0) mailman/tests/bounces/smtp32_01.txt (+97/-0) mailman/tests/bounces/smtp32_02.txt (+96/-0) mailman/tests/bounces/smtp32_03.txt (+92/-0) mailman/tests/bounces/smtp32_04.txt (+47/-0) mailman/tests/bounces/smtp32_05.txt (+63/-0) mailman/tests/bounces/smtp32_06.txt (+41/-0) mailman/tests/bounces/smtp32_07.txt (+81/-0) mailman/tests/bounces/yahoo_01.txt (+47/-0) mailman/tests/bounces/yahoo_02.txt (+57/-0) mailman/tests/bounces/yahoo_03.txt (+98/-0) mailman/tests/bounces/yahoo_04.txt (+150/-0) mailman/tests/bounces/yahoo_05.txt (+150/-0) mailman/tests/bounces/yahoo_06.txt (+105/-0) mailman/tests/bounces/yahoo_07.txt (+112/-0) mailman/tests/bounces/yahoo_08.txt (+129/-0) mailman/tests/bounces/yahoo_09.txt (+165/-0) mailman/tests/bounces/yahoo_10.txt (+83/-0) mailman/tests/bounces/yale_01.txt (+422/-0) mailman/tests/helpers.py (+237/-0) mailman/tests/smtplistener.py (+86/-0) mailman/tests/test_bounces.py (+223/-0) mailman/tests/test_documentation.py (+121/-0) mailman/tests/test_membership.py (+386/-0) mailman/tests/test_passwords.py (+161/-0) mailman/tests/test_safedict.py (+48/-0) mailman/tests/test_security_mgr.py (+233/-0) mailman/tests/testing.cfg.in (+14/-0) mailman/version.py (+48/-0) scripts/driver (+314/-0) setup.cfg (+5/-0) setup.py (+104/-0) staging/bin/add_members (+9/-0) staging/bin/arch (+9/-0) staging/bin/bounces (+9/-0) staging/bin/bumpdigests (+9/-0) staging/bin/change_pw (+9/-0) staging/bin/check_perms (+9/-0) staging/bin/checkdbs (+9/-0) staging/bin/cleanarch (+9/-0) staging/bin/config_list (+9/-0) staging/bin/confirm (+9/-0) staging/bin/create_list (+9/-0) staging/bin/disabled (+9/-0) staging/bin/dumpdb (+9/-0) staging/bin/export (+9/-0) staging/bin/find_member (+9/-0) staging/bin/gate_news (+9/-0) staging/bin/genaliases (+9/-0) staging/bin/import (+9/-0) staging/bin/inject (+9/-0) staging/bin/join (+9/-0) staging/bin/leave (+9/-0) staging/bin/list_lists (+9/-0) staging/bin/list_members (+9/-0) staging/bin/list_owners (+9/-0) staging/bin/mailmanctl (+9/-0) staging/bin/make_instance (+9/-0) staging/bin/master (+9/-0) staging/bin/mmsitepass (+9/-0) staging/bin/nightly_gzip (+9/-0) staging/bin/owner (+9/-0) staging/bin/post (+9/-0) staging/bin/qrunner (+9/-0) staging/bin/remove_list (+9/-0) staging/bin/request (+9/-0) staging/bin/senddigests (+9/-0) staging/bin/set_members (+9/-0) staging/bin/show_config (+9/-0) staging/bin/show_qfiles (+9/-0) staging/bin/testall (+9/-0) staging/bin/unshunt (+9/-0) staging/bin/update (+9/-0) staging/bin/version (+9/-0) staging/bin/withlist (+9/-0) staging/easy-install.pth (+3/-0) staging/mailman.egg-link (+2/-0) staging/site.py (+82/-0) tests/fblast.py (+60/-0) tests/msgs/bad_01.txt (+62/-0) tests/onebounce.py (+94/-0) var/etc/mailman.cfg (+54/-0) Conflict adding file README.txt. Moved existing file to README.txt.moved. Conflict adding file contrib. Moved existing file to contrib.moved. Conflict adding file cron. Moved existing file to cron.moved. Conflict adding file data. Moved existing file to data.moved. Conflict adding file setup.py. Moved existing file to setup.py.moved. |
To merge this branch: | bzr merge lp:~mmlmtp/mailman/mm3lmtp |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Barry Warsaw | Disapprove | ||
Review via email: mp+38413@code.launchpad.net |
Commit message
Description of the change
Gives LMTP server the ability to reject mail to non-existent lists at LMTP time, and to reject mail from people who don't have permission to post to a list.
Barry Warsaw (barry) wrote : | # |
I'm going to close this as rejected since there are so many conflicts. Please do consider resubmitting against our gitlab project: https:/
Unmerged revisions
- 11. By iane <email address hidden>
-
Removed extraneous log files
- 10. By William Mead <email address hidden>
-
rev10: Added a method to Utils, Added some tests to the lmtp Doctest
- 9. By William Mead <email address hidden>
-
rev9: Added PIPELINING supported message at LHLO
- 8. By William Mead <email address hidden>
-
rev8:fixed Doctest, code respects rfcs
- 7. By William Mead <email address hidden>
-
rev7: Complete LMTP channel, Fully uses enhanced error codes, LHLO command is required before MAIL command
- 6. By William Mead <email address hidden>
-
rev6: Fixed Doctest
- 5. By William Mead <email address hidden>
-
rev5: Added two new enhanced error codes
- 4. By William Mead <email address hidden>
-
rev4: Created three new methods for getting and parsing listnames, accept_
these_nonmember s will be checked - 3. By William Mead <email address hidden>
-
rev3 : Added enhanced error codes, Sender address can be rejected at RCPT TO
- 2. By William Mead <email address hidden>
-
rev2
Preview Diff
1 | === added file 'PKG-INFO' |
2 | --- PKG-INFO 1970-01-01 00:00:00 +0000 |
3 | +++ PKG-INFO 2010-10-14 12:15:59 +0000 |
4 | @@ -0,0 +1,14 @@ |
5 | +Metadata-Version: 1.0 |
6 | +Name: mailman |
7 | +Version: 3.0.0a1 |
8 | +Summary: Mailman -- the GNU mailing list manager |
9 | +Home-page: http://www.list.org |
10 | +Author: The Mailman Developers |
11 | +Author-email: mailman-developers@python.org |
12 | +License: GPL |
13 | +Description: This is GNU Mailman, a mailing list management system distributed under the |
14 | + terms of the GNU General Public License (GPL). The name of this software is |
15 | + spelled 'Mailman' with a leading capital 'M' but with a lower case second `m'. |
16 | + Any other spelling is incorrect. |
17 | +Keywords: email |
18 | +Platform: UNKNOWN |
19 | |
20 | === added file 'README.txt' |
21 | --- README.txt 1970-01-01 00:00:00 +0000 |
22 | +++ README.txt 2010-10-14 12:15:59 +0000 |
23 | @@ -0,0 +1,197 @@ |
24 | +Mailman - The GNU Mailing List Management System |
25 | +Copyright (C) 1998-2008 by the Free Software Foundation, Inc. |
26 | +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
27 | + |
28 | +INTRODUCTION |
29 | + |
30 | + This is GNU Mailman, a mailing list management system distributed under |
31 | + the terms of the GNU General Public License (GPL). The name of this |
32 | + software is spelled "Mailman" with a leading capital `M' but with a lower |
33 | + case second `m'. Any other spelling is incorrect. |
34 | + |
35 | + Mailman is written in Python, a free object-oriented scripting language. |
36 | + Python is available for all platforms that Mailman is supported on, which |
37 | + includes GNU/Linux and most other Unix-like operating systems |
38 | + (e.g. Solaris, *BSD, MacOSX, etc.). It does not run on Windows, although |
39 | + web and mail clients on any platform should be able to interact with |
40 | + Mailman just fine. |
41 | + |
42 | + Mailman was originally developed by John Viega. Subsequent development |
43 | + (through version 1.0b3) was by Ken Manheimer. Further work towards the |
44 | + 1.0 final release was a group effort, with the core contributors being: |
45 | + Barry Warsaw, Ken Manheimer, Scott Cotton, Harald Meland, and John Viega. |
46 | + Version 1.0 and beyond have been primarily maintained by Barry Warsaw with |
47 | + contributions from many; see the ACKNOWLEDGMENTS file for details. Jeremy |
48 | + Hylton helped considerably with the Pipermail code in Mailman 2.0. |
49 | + Mailman 2.1 is now being primarily maintained by Mark Sapiro and Tokio |
50 | + Kikuchi. Barry Warsaw is the lead developer on Mailman 3. |
51 | + |
52 | + The Mailman home page is: |
53 | + |
54 | + http://www.list.org |
55 | + |
56 | + with mirrors at: |
57 | + |
58 | + http://www.gnu.org/software/mailman |
59 | + http://mailman.sf.net |
60 | + |
61 | + You might also be interested in the Mailman wiki at: |
62 | + |
63 | + http://wiki.list.org |
64 | + |
65 | + Mailman 3.0 requires Python 2.5 or greater, which can be downloaded from: |
66 | + |
67 | + http://www.python.org |
68 | + |
69 | + It is recommended that you use at least Python 2.5.2, the latest release |
70 | + as of this writing (31-Mar-2008). |
71 | + |
72 | + |
73 | +FEATURES |
74 | + |
75 | + **Mailman 3.0 is alpha software and some of this information may be out of |
76 | + date or not currently working. This will improve as the alpha releases |
77 | + are developed.** |
78 | + |
79 | + Mailman has most of the standard features you'd expect in a mailing list |
80 | + manager, and more: |
81 | + |
82 | + - Web based list administration for nearly all tasks. Web based |
83 | + subscriptions and user configuration management. A customizable "home |
84 | + page" for each mailing list. |
85 | + |
86 | + - Privacy features such as moderation, open and closed list subscription |
87 | + policies, private membership rosters, and sender-based filters. |
88 | + |
89 | + - Automatic web based archiving built-in with support for private and |
90 | + public archives, and hooks for external archivers. |
91 | + |
92 | + - Per-user configuration optional digest delivery for either |
93 | + MIME-compliant or RFC 1153 style "plain text" digests. |
94 | + |
95 | + - Integrated mail/Usenet gateways. |
96 | + |
97 | + - Integrated auto-replies. |
98 | + |
99 | + - Email commands. |
100 | + |
101 | + - Integrated bounce detection within an extensible framework. |
102 | + |
103 | + - Integrated spam detection, and MIME-based content filtering. |
104 | + |
105 | + - An extensible mail delivery pipeline. |
106 | + |
107 | + - Support for virtual domains. |
108 | + |
109 | + |
110 | +REQUIREMENTS |
111 | + |
112 | + The default mail delivery mechanism uses a direct SMTP connection to |
113 | + whatever mail transport agent you have running on port 25. You can thus |
114 | + use Mailman with any such MTA, however with certain MTAs (e.g. Exim and |
115 | + Postfix), Mailman will support thru-the-web creation and removal of |
116 | + mailing lists. |
117 | + |
118 | + Mailman works with any web server that supports CGI/1.1. The HTML it |
119 | + generates should be friendly to most web browsers and network connections. |
120 | + |
121 | + You will need root access on the machine hosting your Mailman installation |
122 | + in order to complete some of the configuration steps. See the INSTALL.txt |
123 | + file for details. |
124 | + |
125 | + Mailman's web and email user interface should be compatible with just |
126 | + about any mail reader or web browser, although a mail reader that is MIME |
127 | + aware will be a big help. You do not need Java, JavaScript, or any other |
128 | + fancy plugins. |
129 | + |
130 | + |
131 | +FOR MORE INFORMATION |
132 | + |
133 | + For information on this alpha release, see docs/ALPHA.txt |
134 | + |
135 | + More documentation is available in the docs directory, and on-line (see |
136 | + below). Installation instructions are contained in the |
137 | + docs/readmes/INSTALL.txt file. Upgrading information is available in the |
138 | + docs/readmes/UPGRADING.txt file. See the docs/NEWS.txt file for a list of |
139 | + changes since version 0.9. |
140 | + |
141 | + The online documentation can be found in |
142 | + |
143 | + file:admin/www/index.html |
144 | + |
145 | + in the directory in which you unpacked Mailman. |
146 | + |
147 | + There is an online FAQ maintained by the Mailman community, which contains |
148 | + a vast amount of information: |
149 | + |
150 | + http://www.python.org/cgi-bin/faqw-mm.py |
151 | + |
152 | + There is also a wiki for more community-driven information: |
153 | + |
154 | + http://wiki.list.org |
155 | + |
156 | + Chris Kolar has made a list owner-oriented manual available from |
157 | + the following URL |
158 | + |
159 | + http://www.imsa.edu/~ckolar/mailman/ |
160 | + |
161 | + There are also several mailing lists that can be used as resources |
162 | + to help you get going with Mailman. |
163 | + |
164 | + Mailman-Users |
165 | + An list for users of Mailman, for posting questions or problems |
166 | + related to installation, use, etc. We'll try to keep the deep |
167 | + technical discussions off this list. |
168 | + |
169 | + http://mail.python.org/mailman/listinfo/mailman-users |
170 | + |
171 | + Listowners |
172 | + This mailing list with a non-technical focus, specifically for |
173 | + discussions from the perspective of listowners and moderators who do |
174 | + not have "shell access" to the mailing list server where the Mailman |
175 | + software runs. |
176 | + |
177 | + http://listowner.org |
178 | + |
179 | + Mailman-Announce |
180 | + A read-only list for release announcements an other important news. |
181 | + |
182 | + http://mail.python.org/mailman/listinfo/mailman-announce |
183 | + |
184 | + Mailman-Developers |
185 | + A list for those of you interested in helping develop Mailman 2's |
186 | + future direction. This list will contain in-depth technical |
187 | + discussions. |
188 | + |
189 | + http://mail.python.org/mailman/listinfo/mailman-developers |
190 | + |
191 | + Mailman3-Dev |
192 | + Get involved now in the development of Mailman 3! |
193 | + |
194 | + http://mail.python.org/mailman/listinfo/mailman3-dev |
195 | + |
196 | + Mailman-I18N |
197 | + A list for the discussion of the Mailman internationalization |
198 | + effort. Mailman 2.1 is fully multi-lingual. |
199 | + |
200 | + http://mail.python.org/mailman/listinfo/mailman-i18n |
201 | + |
202 | + Mailman-Checkins |
203 | + A read-only list which is an adjunct to the public anonymous CVS |
204 | + repository. You can stay on the bleeding edge of Mailman development |
205 | + by subscribing to this list. |
206 | + |
207 | + http://mail.python.org/mailman/listinfo/mailman-checkins |
208 | + |
209 | + The Mailman project is coordinated on SourceForge at |
210 | + |
211 | + http://sf.net/projects/mailman |
212 | + |
213 | + You should use SourceForge to report bugs and to upload patches. |
214 | + |
215 | + |
216 | + |
217 | |
218 | +Local Variables: |
219 | +mode: indented-text |
220 | +indent-tabs-mode: nil |
221 | +End: |
222 | |
223 | === renamed file 'README.txt' => 'README.txt.moved' |
224 | === added file 'TODO.txt' |
225 | --- TODO.txt 1970-01-01 00:00:00 +0000 |
226 | +++ TODO.txt 2010-10-14 12:15:59 +0000 |
227 | @@ -0,0 +1,15 @@ |
228 | +This list is by no means complete. I'm just using it to track short term |
229 | +things that I need to do. |
230 | + |
231 | +Get rid of PickleTypes |
232 | +Get rid of MailList class! (done for test suite!) |
233 | +Add tests for bin/newlist and bin/rmlist |
234 | +Add tests for plugins |
235 | +Rework MTA plugins and add tests |
236 | +Address XXX and FIXME |
237 | +Fix the roster creation cruft for mailing lists |
238 | +Suss out the IDomain stuff |
239 | +Remove Date: header from messagestore requirements (see list thread) |
240 | +Handle moderation flag (see Mailman.app.membership) |
241 | +Eradicate MailList.Lock() and friends. |
242 | +Eradicate MemberAdapter and friends. |
243 | |
244 | === added directory 'bin' |
245 | === added file 'bin/clone_member' |
246 | --- bin/clone_member 1970-01-01 00:00:00 +0000 |
247 | +++ bin/clone_member 2010-10-14 12:15:59 +0000 |
248 | @@ -0,0 +1,219 @@ |
249 | +#! @PYTHON@ |
250 | +# |
251 | +# Copyright (C) 1998,1999,2000,2001,2002 by the Free Software Foundation, Inc. |
252 | +# |
253 | +# This program is free software; you can redistribute it and/or |
254 | +# modify it under the terms of the GNU General Public License |
255 | +# as published by the Free Software Foundation; either version 2 |
256 | +# of the License, or (at your option) any later version. |
257 | +# |
258 | +# This program is distributed in the hope that it will be useful, |
259 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
260 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
261 | +# GNU General Public License for more details. |
262 | +# |
263 | +# You should have received a copy of the GNU General Public License |
264 | +# along with this program; if not, write to the Free Software |
265 | +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
266 | + |
267 | +"""Clone a member address. |
268 | + |
269 | +Cloning a member address means that a new member will be added who has all the |
270 | +same options and passwords as the original member address. Note that this |
271 | +operation is fairly trusting of the user who runs it -- it does no |
272 | +verification to the new address, it does not send out a welcome message, etc. |
273 | + |
274 | +The existing member's subscription is usually not modified in any way. If you |
275 | +want to remove the old address, use the -r flag. If you also want to change |
276 | +any list admin addresses, use the -a flag. |
277 | + |
278 | +Usage: |
279 | + clone_member [options] fromoldaddr tonewaddr |
280 | + |
281 | +Where: |
282 | + |
283 | + --listname=listname |
284 | + -l listname |
285 | + Check and modify only the named mailing lists. If -l is not given, |
286 | + then all mailing lists are scanned from the address. Multiple -l |
287 | + options can be supplied. |
288 | + |
289 | + --remove |
290 | + -r |
291 | + Remove the old address from the mailing list after it's been cloned. |
292 | + |
293 | + --admin |
294 | + -a |
295 | + Scan the list admin addresses for the old address, and clone or change |
296 | + them too. |
297 | + |
298 | + --quiet |
299 | + -q |
300 | + Do the modifications quietly. |
301 | + |
302 | + --nomodify |
303 | + -n |
304 | + Print what would be done, but don't actually do it. Inhibits the |
305 | + --quiet flag. |
306 | + |
307 | + --help |
308 | + -h |
309 | + Print this help message and exit. |
310 | + |
311 | + fromoldaddr (`from old address') is the old address of the user. tonewaddr |
312 | + (`to new address') is the new address of the user. |
313 | + |
314 | +""" |
315 | + |
316 | +import sys |
317 | +import getopt |
318 | + |
319 | +import paths |
320 | +from Mailman import MailList |
321 | +from Mailman import Utils |
322 | +from Mailman import Errors |
323 | +from Mailman.i18n import _ |
324 | + |
325 | + |
326 | + |
327 | |
328 | +def usage(code, msg=''): |
329 | + if code: |
330 | + fd = sys.stderr |
331 | + else: |
332 | + fd = sys.stdout |
333 | + print >> fd, _(__doc__) |
334 | + if msg: |
335 | + print >> fd, msg |
336 | + sys.exit(code) |
337 | + |
338 | + |
339 | + |
340 | |
341 | +def dolist(mlist, options): |
342 | + SPACE = ' ' |
343 | + if not options.quiet: |
344 | + print _('processing mailing list:'), mlist.internal_name() |
345 | + |
346 | + # scan the list owners. TBD: mlist.owner keys should be lowercase? |
347 | + oldowners = mlist.owner[:] |
348 | + oldowners.sort() |
349 | + if options.admintoo: |
350 | + if not options.quiet: |
351 | + print _(' scanning list owners:'), SPACE.join(oldowners) |
352 | + newowners = {} |
353 | + foundp = 0 |
354 | + for owner in mlist.owner: |
355 | + if options.lfromaddr == owner.lower(): |
356 | + foundp = 1 |
357 | + if options.remove: |
358 | + continue |
359 | + newowners[owner] = 1 |
360 | + if foundp: |
361 | + newowners[options.toaddr] = 1 |
362 | + newowners = newowners.keys() |
363 | + newowners.sort() |
364 | + if options.modify: |
365 | + mlist.owner = newowners |
366 | + if not options.quiet: |
367 | + if newowners <> oldowners: |
368 | |
369 | + print _(' new list owners:'), SPACE.join(newowners) |
370 | + else: |
371 | + print _('(no change)') |
372 | + |
373 | + # see if the fromaddr is a digest member or regular member |
374 | + if options.lfromaddr in mlist.getDigestMemberKeys(): |
375 | + digest = 1 |
376 | + elif options.lfromaddr in mlist.getRegularMemberKeys(): |
377 | + digest = 0 |
378 | + else: |
379 | + if not options.quiet: |
380 | + print _(' address not found:'), options.fromaddr |
381 | + return |
382 | + |
383 | + # Now change the membership address |
384 | + try: |
385 | + if options.modify: |
386 | + mlist.changeMemberAddress(options.fromaddr, options.toaddr, |
387 | + not options.remove) |
388 | + if not options.quiet: |
389 | + print _(' clone address added:'), options.toaddr |
390 | + except Errors.MMAlreadyAMember: |
391 | + if not options.quiet: |
392 | + print _(' clone address is already a member:'), options.toaddr |
393 | + |
394 | + if options.remove: |
395 | + print _(' original address removed:'), options.fromaddr |
396 | + |
397 | + |
398 | + |
399 | |
400 | +def main(): |
401 | + # default options |
402 | + class Options: |
403 | + listnames = None |
404 | + remove = 0 |
405 | + admintoo = 0 |
406 | + quiet = 0 |
407 | + modify = 1 |
408 | + |
409 | + # scan sysargs |
410 | + try: |
411 | + opts, args = getopt.getopt( |
412 | + sys.argv[1:], 'arl:qnh', |
413 | + ['admin', 'remove', 'listname=', 'quiet', 'nomodify', 'help']) |
414 | + except getopt.error, msg: |
415 | + usage(1, msg) |
416 | + |
417 | + options = Options() |
418 | + for opt, arg in opts: |
419 | + if opt in ('-h', '--help'): |
420 | + usage(0) |
421 | + elif opt in ('-q', '--quiet'): |
422 | + options.quiet = 1 |
423 | + elif opt in ('-n', '--nomodify'): |
424 | + options.modify = 0 |
425 | + elif opt in ('-a', '--admin'): |
426 | + options.admintoo = 1 |
427 | + elif opt in ('-r', '--remove'): |
428 | + options.remove = 1 |
429 | + elif opt in ('-l', '--listname'): |
430 | + if options.listnames is None: |
431 | + options.listnames = [] |
432 | + options.listnames.append(arg.lower()) |
433 | + |
434 | + # further options and argument processing |
435 | + if not options.modify: |
436 | + options.quiet = 0 |
437 | + |
438 | + if len(args) <> 2: |
439 | + usage(1) |
440 | + fromaddr = args[0] |
441 | + toaddr = args[1] |
442 | + |
443 | + # validate and normalize the target address |
444 | + try: |
445 | + Utils.ValidateEmail(toaddr) |
446 | + except Errors.EmailAddressError: |
447 | + usage(1, _('Not a valid email address: %(toaddr)s')) |
448 | + lfromaddr = fromaddr.lower() |
449 | + options.toaddr = toaddr |
450 | + options.fromaddr = fromaddr |
451 | + options.lfromaddr = lfromaddr |
452 | + |
453 | + if options.listnames is None: |
454 | + options.listnames = Utils.list_names() |
455 | + |
456 | + for listname in options.listnames: |
457 | + try: |
458 | + mlist = MailList.MailList(listname) |
459 | + except Errors.MMListError, e: |
460 | + print _('Error opening list "%(listname)s", skipping.\n%(e)s') |
461 | + continue |
462 | + try: |
463 | + dolist(mlist, options) |
464 | + finally: |
465 | + mlist.Save() |
466 | + mlist.Unlock() |
467 | + |
468 | + |
469 | |
470 | +if __name__ == '__main__': |
471 | + main() |
472 | |
473 | === added file 'bin/discard' |
474 | --- bin/discard 1970-01-01 00:00:00 +0000 |
475 | +++ bin/discard 2010-10-14 12:15:59 +0000 |
476 | @@ -0,0 +1,120 @@ |
477 | +#! @PYTHON@ |
478 | +# |
479 | +# Copyright (C) 2003 by the Free Software Foundation, Inc. |
480 | +# |
481 | +# This program is free software; you can redistribute it and/or |
482 | +# modify it under the terms of the GNU General Public License |
483 | +# as published by the Free Software Foundation; either version 2 |
484 | +# of the License, or (at your option) any later version. |
485 | +# |
486 | +# This program is distributed in the hope that it will be useful, |
487 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
488 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
489 | +# GNU General Public License for more details. |
490 | +# |
491 | +# You should have received a copy of the GNU General Public License |
492 | +# along with this program; if not, write to the Free Software |
493 | +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
494 | + |
495 | +"""Discard held messages. |
496 | + |
497 | +Usage: |
498 | + discard [options] file ... |
499 | + |
500 | +Options: |
501 | + --help / -h |
502 | + Print this help message and exit. |
503 | + |
504 | + --quiet / -q |
505 | + Don't print status messages. |
506 | +""" |
507 | + |
508 | +# TODO: add command line arguments for specifying other actions than DISCARD, |
509 | +# and also for specifying other __handlepost() arguments, i.e. comment, |
510 | +# preserve, forward, addr |
511 | + |
512 | +import os |
513 | +import re |
514 | +import sys |
515 | +import getopt |
516 | + |
517 | +import paths |
518 | +from Mailman import mm_cfg |
519 | +from Mailman.MailList import MailList |
520 | +from Mailman.i18n import _ |
521 | + |
522 | +try: |
523 | + True, False |
524 | +except NameError: |
525 | + True = 1 |
526 | + False = 0 |
527 | + |
528 | +cre = re.compile(r'heldmsg-(?P<listname>.*)-(?P<id>[0-9]+)\.(pck|txt)$') |
529 | + |
530 | + |
531 | + |
532 | |
533 | +def usage(code, msg=''): |
534 | + if code: |
535 | + fd = sys.stderr |
536 | + else: |
537 | + fd = sys.stdout |
538 | + print >> fd, _(__doc__) |
539 | + if msg: |
540 | + print >> fd, msg |
541 | + sys.exit(code) |
542 | + |
543 | + |
544 | + |
545 | |
546 | +def main(): |
547 | + try: |
548 | + opts, args = getopt.getopt(sys.argv[1:], 'hq', ['help', 'quiet']) |
549 | + except getopt.error, msg: |
550 | + usage(1, msg) |
551 | + |
552 | + quiet = False |
553 | + for opt, arg in opts: |
554 | + if opt in ('-h', '--help'): |
555 | + usage(0) |
556 | + elif opt in ('-q', '--quiet'): |
557 | + quiet = True |
558 | + |
559 | + files = args |
560 | + if not files: |
561 | + print _('Nothing to do.') |
562 | + |
563 | + # Mapping from listnames to sequence of request ids |
564 | + discards = {} |
565 | + |
566 | + # Cruise through all the named files, collating by mailing list. We'll |
567 | + # lock the list once, process all holds for that list and move on. |
568 | + for f in files: |
569 | + basename = os.path.basename(f) |
570 | + mo = cre.match(basename) |
571 | + if not mo: |
572 | + print >> sys.stderr, _('Ignoring non-held message: %(f)s') |
573 | + continue |
574 | + listname, id = mo.group('listname', 'id') |
575 | + try: |
576 | + id = int(id) |
577 | + except (ValueError, TypeError): |
578 | + print >> sys.stderr, _('Ignoring held msg w/bad id: %(f)s') |
579 | + continue |
580 | + discards.setdefault(listname, []).append(id) |
581 | + |
582 | + # Now do the discards |
583 | + for listname, ids in discards.items(): |
584 | + mlist = MailList(listname) |
585 | + try: |
586 | + for id in ids: |
587 | + # No comment, no preserve, no forward, no forwarding address |
588 | + mlist.HandleRequest(id, mm_cfg.DISCARD, '', False, False, '') |
589 | + if not quiet: |
590 | + print _('Discarded held msg #%(id)s for list %(listname)s') |
591 | + mlist.Save() |
592 | + finally: |
593 | + mlist.Unlock() |
594 | + |
595 | + |
596 | + |
597 | |
598 | +if __name__ == '__main__': |
599 | + main() |
600 | |
601 | === added file 'bin/fix_url.py' |
602 | --- bin/fix_url.py 1970-01-01 00:00:00 +0000 |
603 | +++ bin/fix_url.py 2010-10-14 12:15:59 +0000 |
604 | @@ -0,0 +1,93 @@ |
605 | +#! @PYTHON@ |
606 | +# |
607 | +# Copyright (C) 2001-2007 by the Free Software Foundation, Inc. |
608 | +# |
609 | +# This program is free software; you can redistribute it and/or |
610 | +# modify it under the terms of the GNU General Public License |
611 | +# as published by the Free Software Foundation; either version 2 |
612 | +# of the License, or (at your option) any later version. |
613 | +# |
614 | +# This program is distributed in the hope that it will be useful, |
615 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
616 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
617 | +# GNU General Public License for more details. |
618 | +# |
619 | +# You should have received a copy of the GNU General Public License |
620 | +# along with this program; if not, write to the Free Software |
621 | +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, |
622 | +# USA. |
623 | + |
624 | +"""Reset a list's web_page_url attribute to the default setting. |
625 | + |
626 | +This script is intended to be run as a bin/withlist script, i.e. |
627 | + |
628 | +% bin/withlist -l -r fix_url listname [options] |
629 | + |
630 | +Options: |
631 | + -u urlhost |
632 | + --urlhost=urlhost |
633 | + Look up urlhost in the virtual host table and set the web_page_url and |
634 | + host_name attributes of the list to the values found. This |
635 | + essentially moves the list from one virtual domain to another. |
636 | + |
637 | + Without this option, the default web_page_url and host_name values are |
638 | + used. |
639 | + |
640 | + -v / --verbose |
641 | + Print what the script is doing. |
642 | + |
643 | +If run standalone, it prints this help text and exits. |
644 | +""" |
645 | + |
646 | +import sys |
647 | +import getopt |
648 | + |
649 | +import paths |
650 | +from Mailman.configuration import config |
651 | +from Mailman.i18n import _ |
652 | + |
653 | + |
654 | + |
655 | |
656 | +def usage(code, msg=''): |
657 | + print _(__doc__.replace('%', '%%')) |
658 | + if msg: |
659 | + print msg |
660 | + sys.exit(code) |
661 | + |
662 | + |
663 | + |
664 | |
665 | +def fix_url(mlist, *args): |
666 | + try: |
667 | + opts, args = getopt.getopt(args, 'u:v', ['urlhost=', 'verbose']) |
668 | + except getopt.error, msg: |
669 | + usage(1, msg) |
670 | + |
671 | + verbose = 0 |
672 | + urlhost = mailhost = None |
673 | + for opt, arg in opts: |
674 | + if opt in ('-u', '--urlhost'): |
675 | + urlhost = arg |
676 | + elif opt in ('-v', '--verbose'): |
677 | + verbose = 1 |
678 | + |
679 | + if urlhost: |
680 | + web_page_url = config.DEFAULT_URL_PATTERN % urlhost |
681 | + mailhost = config.VIRTUAL_HOSTS.get(urlhost.lower(), urlhost) |
682 | + else: |
683 | + web_page_url = config.DEFAULT_URL_PATTERN % config.DEFAULT_URL_HOST |
684 | + mailhost = config.DEFAULT_EMAIL_HOST |
685 | + |
686 | + if verbose: |
687 | + print _('Setting web_page_url to: %(web_page_url)s') |
688 | + mlist.web_page_url = web_page_url |
689 | + if verbose: |
690 | + print _('Setting host_name to: %(mailhost)s') |
691 | + mlist.host_name = mailhost |
692 | + print _('Saving list') |
693 | + mlist.Save() |
694 | + mlist.Unlock() |
695 | + |
696 | + |
697 | + |
698 | |
699 | +if __name__ == '__main__': |
700 | + usage(0) |
701 | |
702 | === added file 'bin/list_admins' |
703 | --- bin/list_admins 1970-01-01 00:00:00 +0000 |
704 | +++ bin/list_admins 2010-10-14 12:15:59 +0000 |
705 | @@ -0,0 +1,101 @@ |
706 | +#! @PYTHON@ |
707 | +# |
708 | +# Copyright (C) 2001,2002 by the Free Software Foundation, Inc. |
709 | +# |
710 | +# This program is free software; you can redistribute it and/or |
711 | +# modify it under the terms of the GNU General Public License |
712 | +# as published by the Free Software Foundation; either version 2 |
713 | +# of the License, or (at your option) any later version. |
714 | +# |
715 | +# This program is distributed in the hope that it will be useful, |
716 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
717 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
718 | +# GNU General Public License for more details. |
719 | +# |
720 | +# You should have received a copy of the GNU General Public License |
721 | +# along with this program; if not, write to the Free Software |
722 | +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
723 | + |
724 | +"""List all the owners of a mailing list. |
725 | + |
726 | +Usage: %(program)s [options] listname ... |
727 | + |
728 | +Where: |
729 | + |
730 | + --all-vhost=vhost |
731 | + -v=vhost |
732 | + List the owners of all the mailing lists for the given virtual host. |
733 | + |
734 | + --all |
735 | + -a |
736 | + List the owners of all the mailing lists on this system. |
737 | + |
738 | + --help |
739 | + -h |
740 | + Print this help message and exit. |
741 | + |
742 | +`listname' is the name of the mailing list to print the owners of. You can |
743 | +have more than one named list on the command line. |
744 | +""" |
745 | + |
746 | +import sys |
747 | +import getopt |
748 | + |
749 | +import paths |
750 | +from Mailman import MailList, Utils |
751 | +from Mailman import Errors |
752 | +from Mailman.i18n import _ |
753 | + |
754 | +COMMASPACE = ', ' |
755 | + |
756 | +program = sys.argv[0] |
757 | + |
758 | + |
759 | + |
760 | |
761 | +def usage(code, msg=''): |
762 | + if code: |
763 | + fd = sys.stderr |
764 | + else: |
765 | + fd = sys.stdout |
766 | + print >> fd, _(__doc__) |
767 | + if msg: |
768 | + print >> fd, msg |
769 | + sys.exit(code) |
770 | + |
771 | + |
772 | + |
773 | |
774 | +def main(): |
775 | + try: |
776 | + opts, args = getopt.getopt(sys.argv[1:], 'hv:a', |
777 | + ['help', 'all-vhost=', 'all']) |
778 | + except getopt.error, msg: |
779 | + usage(1, msg) |
780 | + |
781 | + listnames = args |
782 | + vhost = None |
783 | + for opt, arg in opts: |
784 | + if opt in ('-h', '--help'): |
785 | + usage(0) |
786 | + elif opt in ('-a', '--all'): |
787 | + listnames = Utils.list_names() |
788 | + elif opt in ('-v', '--all-vhost'): |
789 | + listnames = Utils.list_names() |
790 | + vhost = arg |
791 | + |
792 | + for listname in listnames: |
793 | + try: |
794 | + mlist = MailList.MailList(listname, lock=0) |
795 | + except Errors.MMListError, e: |
796 | + print _('No such list: %(listname)s') |
797 | + continue |
798 | + |
799 | + if vhost and vhost <> mlist.host_name: |
800 | + continue |
801 | + |
802 | + owners = COMMASPACE.join(mlist.owner) |
803 | + print _('List: %(listname)s, \tOwners: %(owners)s') |
804 | + |
805 | + |
806 | + |
807 | |
808 | +if __name__ == '__main__': |
809 | + main() |
810 | |
811 | === added file 'bin/msgfmt.py' |
812 | --- bin/msgfmt.py 1970-01-01 00:00:00 +0000 |
813 | +++ bin/msgfmt.py 2010-10-14 12:15:59 +0000 |
814 | @@ -0,0 +1,203 @@ |
815 | +#! /usr/bin/env python |
816 | +# -*- coding: iso-8859-1 -*- |
817 | +# Written by Martin v. Löwis <loewis@informatik.hu-berlin.de> |
818 | + |
819 | +"""Generate binary message catalog from textual translation description. |
820 | + |
821 | +This program converts a textual Uniforum-style message catalog (.po file) into |
822 | +a binary GNU catalog (.mo file). This is essentially the same function as the |
823 | +GNU msgfmt program, however, it is a simpler implementation. |
824 | + |
825 | +Usage: msgfmt.py [OPTIONS] filename.po |
826 | + |
827 | +Options: |
828 | + -o file |
829 | + --output-file=file |
830 | + Specify the output file to write to. If omitted, output will go to a |
831 | + file named filename.mo (based off the input file name). |
832 | + |
833 | + -h |
834 | + --help |
835 | + Print this message and exit. |
836 | + |
837 | + -V |
838 | + --version |
839 | + Display version information and exit. |
840 | +""" |
841 | + |
842 | +import sys |
843 | +import os |
844 | +import getopt |
845 | +import struct |
846 | +import array |
847 | + |
848 | +__version__ = "1.1" |
849 | + |
850 | +MESSAGES = {} |
851 | + |
852 | + |
853 | + |
854 | |
855 | +def usage(code, msg=''): |
856 | + print >> sys.stderr, __doc__ |
857 | + if msg: |
858 | + print >> sys.stderr, msg |
859 | + sys.exit(code) |
860 | + |
861 | + |
862 | + |
863 | |
864 | +def add(id, str, fuzzy): |
865 | + "Add a non-fuzzy translation to the dictionary." |
866 | + global MESSAGES |
867 | + if not fuzzy and str: |
868 | + MESSAGES[id] = str |
869 | + |
870 | + |
871 | + |
872 | |
873 | +def generate(): |
874 | + "Return the generated output." |
875 | + global MESSAGES |
876 | + keys = MESSAGES.keys() |
877 | + # the keys are sorted in the .mo file |
878 | + keys.sort() |
879 | + offsets = [] |
880 | + ids = strs = '' |
881 | + for id in keys: |
882 | + # For each string, we need size and file offset. Each string is NUL |
883 | + # terminated; the NUL does not count into the size. |
884 | + offsets.append((len(ids), len(id), len(strs), len(MESSAGES[id]))) |
885 | + ids += id + '\0' |
886 | + strs += MESSAGES[id] + '\0' |
887 | + output = '' |
888 | + # The header is 7 32-bit unsigned integers. We don't use hash tables, so |
889 | + # the keys start right after the index tables. |
890 | + # translated string. |
891 | + keystart = 7*4+16*len(keys) |
892 | + # and the values start after the keys |
893 | + valuestart = keystart + len(ids) |
894 | + koffsets = [] |
895 | + voffsets = [] |
896 | + # The string table first has the list of keys, then the list of values. |
897 | + # Each entry has first the size of the string, then the file offset. |
898 | + for o1, l1, o2, l2 in offsets: |
899 | + koffsets += [l1, o1+keystart] |
900 | + voffsets += [l2, o2+valuestart] |
901 | + offsets = koffsets + voffsets |
902 | + output = struct.pack("Iiiiiii", |
903 | + 0x950412deL, # Magic |
904 | + 0, # Version |
905 | + len(keys), # # of entries |
906 | + 7*4, # start of key index |
907 | + 7*4+len(keys)*8, # start of value index |
908 | + 0, 0) # size and offset of hash table |
909 | + output += array.array("i", offsets).tostring() |
910 | + output += ids |
911 | + output += strs |
912 | + return output |
913 | + |
914 | + |
915 | + |
916 | |
917 | +def make(filename, outfile): |
918 | + ID = 1 |
919 | + STR = 2 |
920 | + |
921 | + # Compute .mo name from .po name and arguments |
922 | + if filename.endswith('.po'): |
923 | + infile = filename |
924 | + else: |
925 | + infile = filename + '.po' |
926 | + if outfile is None: |
927 | + outfile = os.path.splitext(infile)[0] + '.mo' |
928 | + |
929 | + try: |
930 | + lines = open(infile).readlines() |
931 | + except IOError, msg: |
932 | + print >> sys.stderr, msg |
933 | + sys.exit(1) |
934 | + |
935 | + section = None |
936 | + fuzzy = 0 |
937 | + |
938 | + # Parse the catalog |
939 | + lno = 0 |
940 | + for l in lines: |
941 | + lno += 1 |
942 | + # If we get a comment line after a msgstr, this is a new entry |
943 | + if l[0] == '#' and section == STR: |
944 | + add(msgid, msgstr, fuzzy) |
945 | + section = None |
946 | + fuzzy = 0 |
947 | + # Record a fuzzy mark |
948 | + if l[:2] == '#,' and l.find('fuzzy'): |
949 | + fuzzy = 1 |
950 | + # Skip comments |
951 | + if l[0] == '#': |
952 | + continue |
953 | + # Now we are in a msgid section, output previous section |
954 | + if l.startswith('msgid'): |
955 | + if section == STR: |
956 | + add(msgid, msgstr, fuzzy) |
957 | + section = ID |
958 | + l = l[5:] |
959 | + msgid = msgstr = '' |
960 | + # Now we are in a msgstr section |
961 | + elif l.startswith('msgstr'): |
962 | + section = STR |
963 | + l = l[6:] |
964 | + # Skip empty lines |
965 | + l = l.strip() |
966 | + if not l: |
967 | + continue |
968 | + # XXX: Does this always follow Python escape semantics? |
969 | + l = eval(l) |
970 | + if section == ID: |
971 | + msgid += l |
972 | + elif section == STR: |
973 | + msgstr += l |
974 | + else: |
975 | + print >> sys.stderr, 'Syntax error on %s:%d' % (infile, lno), \ |
976 | + 'before:' |
977 | + print >> sys.stderr, l |
978 | + sys.exit(1) |
979 | + # Add last entry |
980 | + if section == STR: |
981 | + add(msgid, msgstr, fuzzy) |
982 | + |
983 | + # Compute output |
984 | + output = generate() |
985 | + |
986 | + try: |
987 | + open(outfile,"wb").write(output) |
988 | + except IOError,msg: |
989 | + print >> sys.stderr, msg |
990 | + |
991 | + |
992 | + |
993 | |
994 | +def main(): |
995 | + try: |
996 | + opts, args = getopt.getopt(sys.argv[1:], 'hVo:', |
997 | + ['help', 'version', 'output-file=']) |
998 | + except getopt.error, msg: |
999 | + usage(1, msg) |
1000 | + |
1001 | + outfile = None |
1002 | + # parse options |
1003 | + for opt, arg in opts: |
1004 | + if opt in ('-h', '--help'): |
1005 | + usage(0) |
1006 | + elif opt in ('-V', '--version'): |
1007 | + print >> sys.stderr, "msgfmt.py", __version__ |
1008 | + sys.exit(0) |
1009 | + elif opt in ('-o', '--output-file'): |
1010 | + outfile = arg |
1011 | + # do it |
1012 | + if not args: |
1013 | + print >> sys.stderr, 'No input file given' |
1014 | + print >> sys.stderr, "Try `msgfmt --help' for more information." |
1015 | + return |
1016 | + |
1017 | + for filename in args: |
1018 | + make(filename, outfile) |
1019 | + |
1020 | + |
1021 | +if __name__ == '__main__': |
1022 | + main() |
1023 | |
1024 | === added file 'bin/po2templ.py' |
1025 | --- bin/po2templ.py 1970-01-01 00:00:00 +0000 |
1026 | +++ bin/po2templ.py 2010-10-14 12:15:59 +0000 |
1027 | @@ -0,0 +1,90 @@ |
1028 | +#! @PYTHON@ |
1029 | +# |
1030 | +# Copyright (C) 2005-2007 by the Free Software Foundation, Inc. |
1031 | +# |
1032 | +# This program is free software; you can redistribute it and/or |
1033 | +# modify it under the terms of the GNU General Public License |
1034 | +# as published by the Free Software Foundation; either version 2 |
1035 | +# of the License, or (at your option) any later version. |
1036 | +# |
1037 | +# This program is distributed in the hope that it will be useful, |
1038 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1039 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1040 | +# GNU General Public License for more details. |
1041 | +# |
1042 | +# You should have received a copy of the GNU General Public License |
1043 | +# along with this program; if not, write to the Free Software |
1044 | +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, |
1045 | +# USA. |
1046 | + |
1047 | +# Author: Tokio Kikuchi <tkikuchi@is.kochi-u.ac.jp> |
1048 | + |
1049 | + |
1050 | +"""po2templ.py |
1051 | + |
1052 | +Extract templates from language po file. |
1053 | + |
1054 | +Usage: po2templ.py languages |
1055 | +""" |
1056 | + |
1057 | +import re |
1058 | +import sys |
1059 | + |
1060 | +cre = re.compile('^#:\s*templates/en/(?P<filename>.*?):1') |
1061 | + |
1062 | + |
1063 | + |
1064 | |
1065 | +def do_lang(lang): |
1066 | + in_template = False |
1067 | + in_msg = False |
1068 | + msgstr = '' |
1069 | + fp = file('messages/%s/LC_MESSAGES/mailman.po' % lang) |
1070 | + try: |
1071 | + for line in fp: |
1072 | + m = cre.search(line) |
1073 | + if m: |
1074 | + in_template = True |
1075 | + in_msg = False |
1076 | + filename = m.group('filename') |
1077 | + outfilename = 'templates/%s/%s' % (lang, filename) |
1078 | + continue |
1079 | + if in_template and line.startswith('#,'): |
1080 | + if line.strip() == '#, fuzzy': |
1081 | + in_template = False |
1082 | + continue |
1083 | + if in_template and line.startswith('msgstr'): |
1084 | + line = line[7:] |
1085 | + in_msg = True |
1086 | + if in_msg: |
1087 | + if not line.strip(): |
1088 | + in_template = False |
1089 | + in_msg = False |
1090 | + if len(msgstr) > 1 and outfilename: |
1091 | + # exclude no translation ... 1 is for LF only |
1092 | + outfile = file(outfilename, 'w') |
1093 | + try: |
1094 | + outfile.write(msgstr) |
1095 | + outfile.write('\n') |
1096 | + finally: |
1097 | + outfile.close() |
1098 | + outfilename = '' |
1099 | + msgstr = '' |
1100 | + continue |
1101 | + msgstr += eval(line) |
1102 | + finally: |
1103 | + fp.close() |
1104 | + if len(msgstr) > 1 and outfilename: |
1105 | + # flush remaining msgstr (last template file) |
1106 | + outfile = file(outfilename, 'w') |
1107 | + try: |
1108 | + outfile.write(msgstr) |
1109 | + outfile.write('\n') |
1110 | + finally: |
1111 | + outfile.close() |
1112 | + |
1113 | + |
1114 | + |
1115 | |
1116 | +if __name__ == '__main__': |
1117 | + langs = sys.argv[1:] |
1118 | + for lang in langs: |
1119 | + do_lang(lang) |
1120 | |
1121 | === added file 'bin/pygettext.py' |
1122 | --- bin/pygettext.py 1970-01-01 00:00:00 +0000 |
1123 | +++ bin/pygettext.py 2010-10-14 12:15:59 +0000 |
1124 | @@ -0,0 +1,545 @@ |
1125 | +#! @PYTHON@ |
1126 | +# Originally written by Barry Warsaw <barry@zope.com> |
1127 | +# |
1128 | +# Minimally patched to make it even more xgettext compatible |
1129 | +# by Peter Funk <pf@artcom-gmbh.de> |
1130 | + |
1131 | +"""pygettext -- Python equivalent of xgettext(1) |
1132 | + |
1133 | +Many systems (Solaris, Linux, Gnu) provide extensive tools that ease the |
1134 | +internationalization of C programs. Most of these tools are independent of |
1135 | +the programming language and can be used from within Python programs. Martin |
1136 | +von Loewis' work[1] helps considerably in this regard. |
1137 | + |
1138 | +There's one problem though; xgettext is the program that scans source code |
1139 | +looking for message strings, but it groks only C (or C++). Python introduces |
1140 | +a few wrinkles, such as dual quoting characters, triple quoted strings, and |
1141 | +raw strings. xgettext understands none of this. |
1142 | + |
1143 | +Enter pygettext, which uses Python's standard tokenize module to scan Python |
1144 | +source code, generating .pot files identical to what GNU xgettext[2] generates |
1145 | +for C and C++ code. From there, the standard GNU tools can be used. |
1146 | + |
1147 | +A word about marking Python strings as candidates for translation. GNU |
1148 | +xgettext recognizes the following keywords: gettext, dgettext, dcgettext, and |
1149 | +gettext_noop. But those can be a lot of text to include all over your code. |
1150 | +C and C++ have a trick: they use the C preprocessor. Most internationalized C |
1151 | +source includes a #define for gettext() to _() so that what has to be written |
1152 | +in the source is much less. Thus these are both translatable strings: |
1153 | + |
1154 | + gettext("Translatable String") |
1155 | + _("Translatable String") |
1156 | + |
1157 | +Python of course has no preprocessor so this doesn't work so well. Thus, |
1158 | +pygettext searches only for _() by default, but see the -k/--keyword flag |
1159 | +below for how to augment this. |
1160 | + |
1161 | + [1] http://www.python.org/workshops/1997-10/proceedings/loewis.html |
1162 | + [2] http://www.gnu.org/software/gettext/gettext.html |
1163 | + |
1164 | +NOTE: pygettext attempts to be option and feature compatible with GNU xgettext |
1165 | +where ever possible. However some options are still missing or are not fully |
1166 | +implemented. Also, xgettext's use of command line switches with option |
1167 | +arguments is broken, and in these cases, pygettext just defines additional |
1168 | +switches. |
1169 | + |
1170 | +Usage: pygettext [options] inputfile ... |
1171 | + |
1172 | +Options: |
1173 | + |
1174 | + -a |
1175 | + --extract-all |
1176 | + Extract all strings. |
1177 | + |
1178 | + -d name |
1179 | + --default-domain=name |
1180 | + Rename the default output file from messages.pot to name.pot. |
1181 | + |
1182 | + -E |
1183 | + --escape |
1184 | + Replace non-ASCII characters with octal escape sequences. |
1185 | + |
1186 | + -D |
1187 | + --docstrings |
1188 | + Extract module, class, method, and function docstrings. These do not |
1189 | + need to be wrapped in _() markers, and in fact cannot be for Python to |
1190 | + consider them docstrings. (See also the -X option). |
1191 | + |
1192 | + -h |
1193 | + --help |
1194 | + Print this help message and exit. |
1195 | + |
1196 | + -k word |
1197 | + --keyword=word |
1198 | + Keywords to look for in addition to the default set, which are: |
1199 | + %(DEFAULTKEYWORDS)s |
1200 | + |
1201 | + You can have multiple -k flags on the command line. |
1202 | + |
1203 | + -K |
1204 | + --no-default-keywords |
1205 | + Disable the default set of keywords (see above). Any keywords |
1206 | + explicitly added with the -k/--keyword option are still recognized. |
1207 | + |
1208 | + --no-location |
1209 | + Do not write filename/lineno location comments. |
1210 | + |
1211 | + -n |
1212 | + --add-location |
1213 | + Write filename/lineno location comments indicating where each |
1214 | + extracted string is found in the source. These lines appear before |
1215 | + each msgid. The style of comments is controlled by the -S/--style |
1216 | + option. This is the default. |
1217 | + |
1218 | + -o filename |
1219 | + --output=filename |
1220 | + Rename the default output file from messages.pot to filename. If |
1221 | + filename is `-' then the output is sent to standard out. |
1222 | + |
1223 | + -p dir |
1224 | + --output-dir=dir |
1225 | + Output files will be placed in directory dir. |
1226 | + |
1227 | + -S stylename |
1228 | + --style stylename |
1229 | + Specify which style to use for location comments. Two styles are |
1230 | + supported: |
1231 | + |
1232 | + Solaris # File: filename, line: line-number |
1233 | + GNU #: filename:line |
1234 | + |
1235 | + The style name is case insensitive. GNU style is the default. |
1236 | + |
1237 | + -v |
1238 | + --verbose |
1239 | + Print the names of the files being processed. |
1240 | + |
1241 | + -V |
1242 | + --version |
1243 | + Print the version of pygettext and exit. |
1244 | + |
1245 | + -w columns |
1246 | + --width=columns |
1247 | + Set width of output to columns. |
1248 | + |
1249 | + -x filename |
1250 | + --exclude-file=filename |
1251 | + Specify a file that contains a list of strings that are not be |
1252 | + extracted from the input files. Each string to be excluded must |
1253 | + appear on a line by itself in the file. |
1254 | + |
1255 | + -X filename |
1256 | + --no-docstrings=filename |
1257 | + Specify a file that contains a list of files (one per line) that |
1258 | + should not have their docstrings extracted. This is only useful in |
1259 | + conjunction with the -D option above. |
1260 | + |
1261 | +If `inputfile' is -, standard input is read. |
1262 | +""" |
1263 | + |
1264 | +import os |
1265 | +import sys |
1266 | +import time |
1267 | +import getopt |
1268 | +import tokenize |
1269 | +import operator |
1270 | + |
1271 | +# for selftesting |
1272 | +try: |
1273 | + import fintl |
1274 | + _ = fintl.gettext |
1275 | +except ImportError: |
1276 | + def _(s): return s |
1277 | + |
1278 | +__version__ = '1.4' |
1279 | + |
1280 | +default_keywords = ['_'] |
1281 | +DEFAULTKEYWORDS = ', '.join(default_keywords) |
1282 | + |
1283 | +EMPTYSTRING = '' |
1284 | + |
1285 | + |
1286 | + |
1287 | |
1288 | +# The normal pot-file header. msgmerge and Emacs's po-mode work better if it's |
1289 | +# there. |
1290 | +pot_header = _('''\ |
1291 | +# SOME DESCRIPTIVE TITLE. |
1292 | +# Copyright (C) YEAR ORGANIZATION |
1293 | +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. |
1294 | +# |
1295 | +msgid "" |
1296 | +msgstr "" |
1297 | +"Project-Id-Version: PACKAGE VERSION\\n" |
1298 | +"POT-Creation-Date: %(time)s\\n" |
1299 | +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n" |
1300 | +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n" |
1301 | +"Language-Team: LANGUAGE <LL@li.org>\\n" |
1302 | +"MIME-Version: 1.0\\n" |
1303 | +"Content-Type: text/plain; charset=CHARSET\\n" |
1304 | +"Content-Transfer-Encoding: ENCODING\\n" |
1305 | +"Generated-By: pygettext.py %(version)s\\n" |
1306 | + |
1307 | +''') |
1308 | + |
1309 | + |
1310 | |
1311 | +def usage(code, msg=''): |
1312 | + if code: |
1313 | + fd = sys.stderr |
1314 | + else: |
1315 | + fd = sys.stdout |
1316 | + print >> fd, _(__doc__) % globals() |
1317 | + if msg: |
1318 | + print >> fd, msg |
1319 | + sys.exit(code) |
1320 | + |
1321 | + |
1322 | + |
1323 | |
1324 | +escapes = [] |
1325 | + |
1326 | +def make_escapes(pass_iso8859): |
1327 | + global escapes |
1328 | + if pass_iso8859: |
1329 | + # Allow iso-8859 characters to pass through so that e.g. 'msgid |
1330 | + # "H[o-umlaut]he"' would result not result in 'msgid "H\366he"'. |
1331 | + # Otherwise we escape any character outside the 32..126 range. |
1332 | + mod = 128 |
1333 | + else: |
1334 | + mod = 256 |
1335 | + for i in range(256): |
1336 | + if 32 <= (i % mod) <= 126: |
1337 | + escapes.append(chr(i)) |
1338 | + else: |
1339 | + escapes.append("\\%03o" % i) |
1340 | + escapes[ord('\\')] = '\\\\' |
1341 | + escapes[ord('\t')] = '\\t' |
1342 | + escapes[ord('\r')] = '\\r' |
1343 | + escapes[ord('\n')] = '\\n' |
1344 | + escapes[ord('\"')] = '\\"' |
1345 | + |
1346 | + |
1347 | +def escape(s): |
1348 | + global escapes |
1349 | + s = list(s) |
1350 | + for i in range(len(s)): |
1351 | + s[i] = escapes[ord(s[i])] |
1352 | + return EMPTYSTRING.join(s) |
1353 | + |
1354 | + |
1355 | +def safe_eval(s): |
1356 | + # unwrap quotes, safely |
1357 | + return eval(s, {'__builtins__':{}}, {}) |
1358 | + |
1359 | + |
1360 | +def normalize(s): |
1361 | + # This converts the various Python string types into a format that is |
1362 | + # appropriate for .po files, namely much closer to C style. |
1363 | + lines = s.split('\n') |
1364 | + if len(lines) == 1: |
1365 | + s = '"' + escape(s) + '"' |
1366 | + else: |
1367 | + if not lines[-1]: |
1368 | + del lines[-1] |
1369 | + lines[-1] = lines[-1] + '\n' |
1370 | + for i in range(len(lines)): |
1371 | + lines[i] = escape(lines[i]) |
1372 | + lineterm = '\\n"\n"' |
1373 | + s = '""\n"' + lineterm.join(lines) + '"' |
1374 | + return s |
1375 | + |
1376 | + |
1377 | + |
1378 | |
1379 | +class TokenEater: |
1380 | + def __init__(self, options): |
1381 | + self.__options = options |
1382 | + self.__messages = {} |
1383 | + self.__state = self.__waiting |
1384 | + self.__data = [] |
1385 | + self.__lineno = -1 |
1386 | + self.__freshmodule = 1 |
1387 | + self.__curfile = None |
1388 | + |
1389 | + def __call__(self, ttype, tstring, stup, etup, line): |
1390 | + # dispatch |
1391 | +## import token |
1392 | +## print >> sys.stderr, 'ttype:', token.tok_name[ttype], \ |
1393 | +## 'tstring:', tstring |
1394 | + self.__state(ttype, tstring, stup[0]) |
1395 | + |
1396 | + def __waiting(self, ttype, tstring, lineno): |
1397 | + opts = self.__options |
1398 | + # Do docstring extractions, if enabled |
1399 | + if opts.docstrings and not opts.nodocstrings.get(self.__curfile): |
1400 | + # module docstring? |
1401 | + if self.__freshmodule: |
1402 | + if ttype == tokenize.STRING: |
1403 | + self.__addentry(safe_eval(tstring), lineno, isdocstring=1) |
1404 | + self.__freshmodule = 0 |
1405 | + elif ttype not in (tokenize.COMMENT, tokenize.NL): |
1406 | + self.__freshmodule = 0 |
1407 | + return |
1408 | + # class docstring? |
1409 | + if ttype == tokenize.NAME and tstring in ('class', 'def'): |
1410 | + self.__state = self.__suiteseen |
1411 | + return |
1412 | + if ttype == tokenize.NAME and tstring in opts.keywords: |
1413 | + self.__state = self.__keywordseen |
1414 | + |
1415 | + def __suiteseen(self, ttype, tstring, lineno): |
1416 | + # ignore anything until we see the colon |
1417 | + if ttype == tokenize.OP and tstring == ':': |
1418 | + self.__state = self.__suitedocstring |
1419 | + |
1420 | + def __suitedocstring(self, ttype, tstring, lineno): |
1421 | + # ignore any intervening noise |
1422 | + if ttype == tokenize.STRING: |
1423 | + self.__addentry(safe_eval(tstring), lineno, isdocstring=1) |
1424 | + self.__state = self.__waiting |
1425 | + elif ttype not in (tokenize.NEWLINE, tokenize.INDENT, |
1426 | + tokenize.COMMENT): |
1427 | + # there was no class docstring |
1428 | + self.__state = self.__waiting |
1429 | + |
1430 | + def __keywordseen(self, ttype, tstring, lineno): |
1431 | + if ttype == tokenize.OP and tstring == '(': |
1432 | + self.__data = [] |
1433 | + self.__lineno = lineno |
1434 | + self.__state = self.__openseen |
1435 | + else: |
1436 | + self.__state = self.__waiting |
1437 | + |
1438 | + def __openseen(self, ttype, tstring, lineno): |
1439 | + if ttype == tokenize.OP and tstring == ')': |
1440 | + # We've seen the last of the translatable strings. Record the |
1441 | + # line number of the first line of the strings and update the list |
1442 | + # of messages seen. Reset state for the next batch. If there |
1443 | + # were no strings inside _(), then just ignore this entry. |
1444 | + if self.__data: |
1445 | + self.__addentry(EMPTYSTRING.join(self.__data)) |
1446 | + self.__state = self.__waiting |
1447 | + elif ttype == tokenize.STRING: |
1448 | + self.__data.append(safe_eval(tstring)) |
1449 | + # TBD: should we warn if we seen anything else? |
1450 | + |
1451 | + def __addentry(self, msg, lineno=None, isdocstring=0): |
1452 | + if lineno is None: |
1453 | + lineno = self.__lineno |
1454 | + if not msg in self.__options.toexclude: |
1455 | + entry = (self.__curfile, lineno) |
1456 | + self.__messages.setdefault(msg, {})[entry] = isdocstring |
1457 | + |
1458 | + def set_filename(self, filename): |
1459 | + self.__curfile = filename |
1460 | + self.__freshmodule = 1 |
1461 | + |
1462 | + def write(self, fp): |
1463 | + options = self.__options |
1464 | + timestamp = time.ctime(time.time()) |
1465 | + # The time stamp in the header doesn't have the same format as that |
1466 | + # generated by xgettext... |
1467 | + print >> fp, pot_header % {'time': timestamp, 'version': __version__} |
1468 | + # Sort the entries. First sort each particular entry's keys, then |
1469 | + # sort all the entries by their first item. |
1470 | + reverse = {} |
1471 | + for k, v in self.__messages.items(): |
1472 | + keys = v.keys() |
1473 | + keys.sort() |
1474 | + reverse.setdefault(tuple(keys), []).append((k, v)) |
1475 | + rkeys = reverse.keys() |
1476 | + rkeys.sort() |
1477 | + for rkey in rkeys: |
1478 | + rentries = reverse[rkey] |
1479 | + rentries.sort() |
1480 | + for k, v in rentries: |
1481 | + isdocstring = 0 |
1482 | + # If the entry was gleaned out of a docstring, then add a |
1483 | + # comment stating so. This is to aid translators who may wish |
1484 | + # to skip translating some unimportant docstrings. |
1485 | + if reduce(operator.__add__, v.values()): |
1486 | + isdocstring = 1 |
1487 | + # k is the message string, v is a dictionary-set of (filename, |
1488 | + # lineno) tuples. We want to sort the entries in v first by |
1489 | + # file name and then by line number. |
1490 | + v = v.keys() |
1491 | + v.sort() |
1492 | + if not options.writelocations: |
1493 | + pass |
1494 | + # location comments are different b/w Solaris and GNU: |
1495 | + elif options.locationstyle == options.SOLARIS: |
1496 | + for filename, lineno in v: |
1497 | + d = {'filename': filename, 'lineno': lineno} |
1498 | + print >>fp, _( |
1499 | + '# File: %(filename)s, line: %(lineno)d') % d |
1500 | + elif options.locationstyle == options.GNU: |
1501 | + # fit as many locations on one line, as long as the |
1502 | + # resulting line length doesn't exceeds 'options.width' |
1503 | + locline = '#:' |
1504 | + for filename, lineno in v: |
1505 | + d = {'filename': filename, 'lineno': lineno} |
1506 | + s = _(' %(filename)s:%(lineno)d') % d |
1507 | + if len(locline) + len(s) <= options.width: |
1508 | + locline = locline + s |
1509 | + else: |
1510 | + print >> fp, locline |
1511 | + locline = "#:" + s |
1512 | + if len(locline) > 2: |
1513 | + print >> fp, locline |
1514 | + if isdocstring: |
1515 | + print >> fp, '#, docstring' |
1516 | + print >> fp, 'msgid', normalize(k) |
1517 | + print >> fp, 'msgstr ""\n' |
1518 | + |
1519 | + |
1520 | + |
1521 | |
1522 | +def main(): |
1523 | + global default_keywords |
1524 | + try: |
1525 | + opts, args = getopt.getopt( |
1526 | + sys.argv[1:], |
1527 | + 'ad:DEhk:Kno:p:S:Vvw:x:X:', |
1528 | + ['extract-all', 'default-domain=', 'escape', 'help', |
1529 | + 'keyword=', 'no-default-keywords', |
1530 | + 'add-location', 'no-location', 'output=', 'output-dir=', |
1531 | + 'style=', 'verbose', 'version', 'width=', 'exclude-file=', |
1532 | + 'docstrings', 'no-docstrings', |
1533 | + ]) |
1534 | + except getopt.error, msg: |
1535 | + usage(1, msg) |
1536 | + |
1537 | + # for holding option values |
1538 | + class Options: |
1539 | + # constants |
1540 | + GNU = 1 |
1541 | + SOLARIS = 2 |
1542 | + # defaults |
1543 | + extractall = 0 # FIXME: currently this option has no effect at all. |
1544 | + escape = 0 |
1545 | + keywords = [] |
1546 | + outpath = '' |
1547 | + outfile = 'messages.pot' |
1548 | + writelocations = 1 |
1549 | + locationstyle = GNU |
1550 | + verbose = 0 |
1551 | + width = 78 |
1552 | + excludefilename = '' |
1553 | + docstrings = 0 |
1554 | + nodocstrings = {} |
1555 | + |
1556 | + options = Options() |
1557 | + locations = {'gnu' : options.GNU, |
1558 | + 'solaris' : options.SOLARIS, |
1559 | + } |
1560 | + |
1561 | + # parse options |
1562 | + for opt, arg in opts: |
1563 | + if opt in ('-h', '--help'): |
1564 | + usage(0) |
1565 | + elif opt in ('-a', '--extract-all'): |
1566 | + options.extractall = 1 |
1567 | + elif opt in ('-d', '--default-domain'): |
1568 | + options.outfile = arg + '.pot' |
1569 | + elif opt in ('-E', '--escape'): |
1570 | + options.escape = 1 |
1571 | + elif opt in ('-D', '--docstrings'): |
1572 | + options.docstrings = 1 |
1573 | + elif opt in ('-k', '--keyword'): |
1574 | + options.keywords.append(arg) |
1575 | + elif opt in ('-K', '--no-default-keywords'): |
1576 | + default_keywords = [] |
1577 | + elif opt in ('-n', '--add-location'): |
1578 | + options.writelocations = 1 |
1579 | + elif opt in ('--no-location',): |
1580 | + options.writelocations = 0 |
1581 | + elif opt in ('-S', '--style'): |
1582 | + options.locationstyle = locations.get(arg.lower()) |
1583 | + if options.locationstyle is None: |
1584 | + usage(1, _('Invalid value for --style: %s') % arg) |
1585 | + elif opt in ('-o', '--output'): |
1586 | + options.outfile = arg |
1587 | + elif opt in ('-p', '--output-dir'): |
1588 | + options.outpath = arg |
1589 | + elif opt in ('-v', '--verbose'): |
1590 | + options.verbose = 1 |
1591 | + elif opt in ('-V', '--version'): |
1592 | + print _('pygettext.py (xgettext for Python) %s') % __version__ |
1593 | + sys.exit(0) |
1594 | + elif opt in ('-w', '--width'): |
1595 | + try: |
1596 | + options.width = int(arg) |
1597 | + except ValueError: |
1598 | + usage(1, _('--width argument must be an integer: %s') % arg) |
1599 | + elif opt in ('-x', '--exclude-file'): |
1600 | + options.excludefilename = arg |
1601 | + elif opt in ('-X', '--no-docstrings'): |
1602 | + fp = open(arg) |
1603 | + try: |
1604 | + while 1: |
1605 | + line = fp.readline() |
1606 | + if not line: |
1607 | + break |
1608 | + options.nodocstrings[line[:-1]] = 1 |
1609 | + finally: |
1610 | + fp.close() |
1611 | + |
1612 | + # calculate escapes |
1613 | + make_escapes(options.escape) |
1614 | + |
1615 | + # calculate all keywords |
1616 | + options.keywords.extend(default_keywords) |
1617 | + |
1618 | + # initialize list of strings to exclude |
1619 | + if options.excludefilename: |
1620 | + try: |
1621 | + fp = open(options.excludefilename) |
1622 | + options.toexclude = fp.readlines() |
1623 | + fp.close() |
1624 | + except IOError: |
1625 | + print >> sys.stderr, _( |
1626 | + "Can't read --exclude-file: %s") % options.excludefilename |
1627 | + sys.exit(1) |
1628 | + else: |
1629 | + options.toexclude = [] |
1630 | + |
1631 | + # slurp through all the files |
1632 | + eater = TokenEater(options) |
1633 | + for filename in args: |
1634 | + if filename == '-': |
1635 | + if options.verbose: |
1636 | + print _('Reading standard input') |
1637 | + fp = sys.stdin |
1638 | + closep = 0 |
1639 | + else: |
1640 | + if options.verbose: |
1641 | + print _('Working on %s') % filename |
1642 | + fp = open(filename) |
1643 | + closep = 1 |
1644 | + try: |
1645 | + eater.set_filename(filename) |
1646 | + try: |
1647 | + tokenize.tokenize(fp.readline, eater) |
1648 | + except tokenize.TokenError, e: |
1649 | + print >> sys.stderr, '%s: %s, line %d, column %d' % ( |
1650 | + e[0], filename, e[1][0], e[1][1]) |
1651 | + finally: |
1652 | + if closep: |
1653 | + fp.close() |
1654 | + |
1655 | + # write the output |
1656 | + if options.outfile == '-': |
1657 | + fp = sys.stdout |
1658 | + closep = 0 |
1659 | + else: |
1660 | + if options.outpath: |
1661 | + options.outfile = os.path.join(options.outpath, options.outfile) |
1662 | + fp = open(options.outfile, 'w') |
1663 | + closep = 1 |
1664 | + try: |
1665 | + eater.write(fp) |
1666 | + finally: |
1667 | + if closep: |
1668 | + fp.close() |
1669 | + |
1670 | + |
1671 | |
1672 | +if __name__ == '__main__': |
1673 | + main() |
1674 | + # some more test strings |
1675 | + _(u'a unicode string') |
1676 | |
1677 | === added file 'bin/remove_members' |
1678 | --- bin/remove_members 1970-01-01 00:00:00 +0000 |
1679 | +++ bin/remove_members 2010-10-14 12:15:59 +0000 |
1680 | @@ -0,0 +1,186 @@ |
1681 | +#! @PYTHON@ |
1682 | +# |
1683 | +# Copyright (C) 1998-2005 by the Free Software Foundation, Inc. |
1684 | +# |
1685 | +# This program is free software; you can redistribute it and/or |
1686 | +# modify it under the terms of the GNU General Public License |
1687 | +# as published by the Free Software Foundation; either version 2 |
1688 | +# of the License, or (at your option) any later version. |
1689 | +# |
1690 | +# This program is distributed in the hope that it will be useful, |
1691 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1692 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1693 | +# GNU General Public License for more details. |
1694 | +# |
1695 | +# You should have received a copy of the GNU General Public License |
1696 | +# along with this program; if not, write to the Free Software |
1697 | +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, |
1698 | +# USA. |
1699 | + |
1700 | +"""Remove members from a list. |
1701 | + |
1702 | +Usage: |
1703 | + remove_members [options] [listname] [addr1 ...] |
1704 | + |
1705 | +Options: |
1706 | + |
1707 | + --file=file |
1708 | + -f file |
1709 | + Remove member addresses found in the given file. If file is |
1710 | + `-', read stdin. |
1711 | + |
1712 | + --all |
1713 | + -a |
1714 | + Remove all members of the mailing list. |
1715 | + (mutually exclusive with --fromall) |
1716 | + |
1717 | + --fromall |
1718 | + Removes the given addresses from all the lists on this system |
1719 | + regardless of virtual domains if you have any. This option cannot be |
1720 | + used -a/--all. Also, you should not specify a listname when using |
1721 | + this option. |
1722 | + |
1723 | + --nouserack |
1724 | + -n |
1725 | + Don't send the user acknowledgements. If not specified, the list |
1726 | + default value is used. |
1727 | + |
1728 | + --noadminack |
1729 | + -N |
1730 | + Don't send the admin acknowledgements. If not specified, the list |
1731 | + default value is used. |
1732 | + |
1733 | + --help |
1734 | + -h |
1735 | + Print this help message and exit. |
1736 | + |
1737 | + listname is the name of the mailing list to use. |
1738 | + |
1739 | + addr1 ... are additional addresses to remove. |
1740 | +""" |
1741 | + |
1742 | +import sys |
1743 | +import getopt |
1744 | + |
1745 | +import paths |
1746 | +from Mailman import MailList |
1747 | +from Mailman import Utils |
1748 | +from Mailman import Errors |
1749 | +from Mailman.i18n import _ |
1750 | + |
1751 | +try: |
1752 | + True, False |
1753 | +except NameError: |
1754 | + True = 1 |
1755 | + False = 0 |
1756 | + |
1757 | + |
1758 | + |
1759 | |
1760 | +def usage(code, msg=''): |
1761 | + if code: |
1762 | + fd = sys.stderr |
1763 | + else: |
1764 | + fd = sys.stdout |
1765 | + print >> fd, _(__doc__) |
1766 | + if msg: |
1767 | + print >> fd, msg |
1768 | + sys.exit(code) |
1769 | + |
1770 | + |
1771 | +def ReadFile(filename): |
1772 | + lines = [] |
1773 | + if filename == "-": |
1774 | + fp = sys.stdin |
1775 | + closep = False |
1776 | + else: |
1777 | + fp = open(filename) |
1778 | + closep = True |
1779 | + lines = filter(None, [line.strip() for line in fp.readlines()]) |
1780 | + if closep: |
1781 | + fp.close() |
1782 | + return lines |
1783 | + |
1784 | + |
1785 | + |
1786 | |
1787 | +def main(): |
1788 | + try: |
1789 | + opts, args = getopt.getopt( |
1790 | + sys.argv[1:], 'naf:hN', |
1791 | + ['all', 'fromall', 'file=', 'help', 'nouserack', 'noadminack']) |
1792 | + except getopt.error, msg: |
1793 | + usage(1, msg) |
1794 | + |
1795 | + filename = None |
1796 | + all = False |
1797 | + alllists = False |
1798 | + # None means use list default |
1799 | + userack = None |
1800 | + admin_notif = None |
1801 | + |
1802 | + for opt, arg in opts: |
1803 | + if opt in ('-h', '--help'): |
1804 | + usage(0) |
1805 | + elif opt in ('-f', '--file'): |
1806 | + filename = arg |
1807 | + elif opt in ('-a', '--all'): |
1808 | + all = True |
1809 | + elif opt == '--fromall': |
1810 | + alllists = True |
1811 | + elif opt in ('-n', '--nouserack'): |
1812 | + userack = False |
1813 | + elif opt in ('-N', '--noadminack'): |
1814 | + admin_notif = False |
1815 | + |
1816 | + if len(args) < 1 and not (filename and alllists): |
1817 | + usage(1) |
1818 | + |
1819 | + # You probably don't want to delete all the users of all the lists -- Marc |
1820 | + if all and alllists: |
1821 | + usage(1) |
1822 | + |
1823 | + if alllists: |
1824 | + addresses = args |
1825 | + else: |
1826 | + listname = args[0].lower().strip() |
1827 | + addresses = args[1:] |
1828 | + |
1829 | + if alllists: |
1830 | + listnames = Utils.list_names() |
1831 | + else: |
1832 | + listnames = [listname] |
1833 | + |
1834 | + if filename: |
1835 | + try: |
1836 | + addresses = addresses + ReadFile(filename) |
1837 | + except IOError: |
1838 | + print _('Could not open file for reading: %(filename)s.') |
1839 | + |
1840 | + for listname in listnames: |
1841 | + try: |
1842 | + # open locked |
1843 | + mlist = MailList.MailList(listname) |
1844 | + except Errors.MMListError: |
1845 | + print _('Error opening list %(listname)s... skipping.') |
1846 | + continue |
1847 | + |
1848 | + if all: |
1849 | + addresses = mlist.getMembers() |
1850 | + |
1851 | + try: |
1852 | + for addr in addresses: |
1853 | + if not mlist.isMember(addr): |
1854 | + if not alllists: |
1855 | + print _('No such member: %(addr)s') |
1856 | + continue |
1857 | + mlist.ApprovedDeleteMember(addr, 'bin/remove_members', |
1858 | + admin_notif, userack) |
1859 | + if alllists: |
1860 | + print _("User `%(addr)s' removed from list: %(listname)s.") |
1861 | + mlist.Save() |
1862 | + finally: |
1863 | + mlist.Unlock() |
1864 | + |
1865 | + |
1866 | + |
1867 | |
1868 | +if __name__ == '__main__': |
1869 | + main() |
1870 | |
1871 | === added file 'bin/reset_pw.py' |
1872 | --- bin/reset_pw.py 1970-01-01 00:00:00 +0000 |
1873 | +++ bin/reset_pw.py 2010-10-14 12:15:59 +0000 |
1874 | @@ -0,0 +1,83 @@ |
1875 | +#! @PYTHON@ |
1876 | +# |
1877 | +# Copyright (C) 2004-2007 by the Free Software Foundation, Inc. |
1878 | +# |
1879 | +# This program is free software; you can redistribute it and/or |
1880 | +# modify it under the terms of the GNU General Public License |
1881 | +# as published by the Free Software Foundation; either version 2 |
1882 | +# of the License, or (at your option) any later version. |
1883 | +# |
1884 | +# This program is distributed in the hope that it will be useful, |
1885 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1886 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1887 | +# GNU General Public License for more details. |
1888 | +# |
1889 | +# You should have received a copy of the GNU General Public License |
1890 | +# along with this program; if not, write to the Free Software |
1891 | +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
1892 | + |
1893 | +# Inspired by Florian Weimer. |
1894 | + |
1895 | +"""Reset the passwords for members of a mailing list. |
1896 | + |
1897 | +This script resets all the passwords of a mailing list's members. It can also |
1898 | +be used to reset the lists of all members of all mailing lists, but it is your |
1899 | +responsibility to let the users know that their passwords have been changed. |
1900 | + |
1901 | +This script is intended to be run as a bin/withlist script, i.e. |
1902 | + |
1903 | +% bin/withlist -l -r reset_pw listname [options] |
1904 | + |
1905 | +Options: |
1906 | + -v / --verbose |
1907 | + Print what the script is doing. |
1908 | +""" |
1909 | + |
1910 | +import sys |
1911 | +import getopt |
1912 | + |
1913 | +import paths |
1914 | +from Mailman import Utils |
1915 | +from Mailman.i18n import _ |
1916 | + |
1917 | + |
1918 | + |
1919 | |
1920 | +def usage(code, msg=''): |
1921 | + if code: |
1922 | + fd = sys.stderr |
1923 | + else: |
1924 | + fd = sys.stdout |
1925 | + print >> fd, _(__doc__.replace('%', '%%')) |
1926 | + if msg: |
1927 | + print >> fd, msg |
1928 | + sys.exit(code) |
1929 | + |
1930 | + |
1931 | + |
1932 | |
1933 | +def reset_pw(mlist, *args): |
1934 | + try: |
1935 | + opts, args = getopt.getopt(args, 'v', ['verbose']) |
1936 | + except getopt.error, msg: |
1937 | + usage(1, msg) |
1938 | + |
1939 | + verbose = False |
1940 | + for opt, args in opts: |
1941 | + if opt in ('-v', '--verbose'): |
1942 | + verbose = True |
1943 | + |
1944 | + listname = mlist.internal_name() |
1945 | + if verbose: |
1946 | + print _('Changing passwords for list: %(listname)s') |
1947 | + |
1948 | + for member in mlist.getMembers(): |
1949 | + randompw = Utils.MakeRandomPassword() |
1950 | + mlist.setMemberPassword(member, randompw) |
1951 | + if verbose: |
1952 | + print _('New password for member %(member)40s: %(randompw)s') |
1953 | + |
1954 | + mlist.Save() |
1955 | + |
1956 | + |
1957 | + |
1958 | |
1959 | +if __name__ == '__main__': |
1960 | + usage(0) |
1961 | |
1962 | === added file 'bin/sync_members' |
1963 | --- bin/sync_members 1970-01-01 00:00:00 +0000 |
1964 | +++ bin/sync_members 2010-10-14 12:15:59 +0000 |
1965 | @@ -0,0 +1,286 @@ |
1966 | +#! @PYTHON@ |
1967 | +# |
1968 | +# Copyright (C) 1998-2003 by the Free Software Foundation, Inc. |
1969 | +# |
1970 | +# This program is free software; you can redistribute it and/or |
1971 | +# modify it under the terms of the GNU General Public License |
1972 | +# as published by the Free Software Foundation; either version 2 |
1973 | +# of the License, or (at your option) any later version. |
1974 | +# |
1975 | +# This program is distributed in the hope that it will be useful, |
1976 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1977 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1978 | +# GNU General Public License for more details. |
1979 | +# |
1980 | +# You should have received a copy of the GNU General Public License |
1981 | +# along with this program; if not, write to the Free Software |
1982 | +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
1983 | + |
1984 | +"""Synchronize a mailing list's membership with a flat file. |
1985 | + |
1986 | +This script is useful if you have a Mailman mailing list and a sendmail |
1987 | +:include: style list of addresses (also as is used in Majordomo). For every |
1988 | +address in the file that does not appear in the mailing list, the address is |
1989 | +added. For every address in the mailing list that does not appear in the |
1990 | +file, the address is removed. Other options control what happens when an |
1991 | +address is added or removed. |
1992 | + |
1993 | +Usage: %(PROGRAM)s [options] -f file listname |
1994 | + |
1995 | +Where `options' are: |
1996 | + |
1997 | + --no-change |
1998 | + -n |
1999 | + Don't actually make the changes. Instead, print out what would be |
2000 | + done to the list. |
2001 | + |
2002 | + --welcome-msg[=<yes|no>] |
2003 | + -w[=<yes|no>] |
2004 | + Sets whether or not to send the newly added members a welcome |
2005 | + message, overriding whatever the list's `send_welcome_msg' setting |
2006 | + is. With -w=yes or -w, the welcome message is sent. With -w=no, no |
2007 | + message is sent. |
2008 | + |
2009 | + --goodbye-msg[=<yes|no>] |
2010 | + -g[=<yes|no>] |
2011 | + Sets whether or not to send the goodbye message to removed members, |
2012 | + overriding whatever the list's `send_goodbye_msg' setting is. With |
2013 | + -g=yes or -g, the goodbye message is sent. With -g=no, no message is |
2014 | + sent. |
2015 | + |
2016 | + --digest[=<yes|no>] |
2017 | + -d[=<yes|no>] |
2018 | + Selects whether to make newly added members receive messages in |
2019 | + digests. With -d=yes or -d, they become digest members. With -d=no |
2020 | + (or if no -d option given) they are added as regular members. |
2021 | + |
2022 | + --notifyadmin[=<yes|no>] |
2023 | + -a[=<yes|no>] |
2024 | + Specifies whether the admin should be notified for each subscription |
2025 | + or unsubscription. If you're adding a lot of addresses, you |
2026 | + definitely want to turn this off! With -a=yes or -a, the admin is |
2027 | + notified. With -a=no, the admin is not notified. With no -a option, |
2028 | + the default for the list is used. |
2029 | + |
2030 | + --file <filename | -> |
2031 | + -f <filename | -> |
2032 | + This option is required. It specifies the flat file to synchronize |
2033 | + against. Email addresses must appear one per line. If filename is |
2034 | + `-' then stdin is used. |
2035 | + |
2036 | + --help |
2037 | + -h |
2038 | + Print this message. |
2039 | + |
2040 | + listname |
2041 | + Required. This specifies the list to synchronize. |
2042 | +""" |
2043 | + |
2044 | +import sys |
2045 | + |
2046 | +import paths |
2047 | +# Import this /after/ paths so that the sys.path is properly hacked |
2048 | +import email.Utils |
2049 | + |
2050 | +from Mailman import MailList |
2051 | +from Mailman import Errors |
2052 | +from Mailman import Utils |
2053 | +from Mailman.UserDesc import UserDesc |
2054 | +from Mailman.i18n import _ |
2055 | + |
2056 | + |
2057 | + |
2058 | |
2059 | +PROGRAM = sys.argv[0] |
2060 | + |
2061 | +def usage(code, msg=''): |
2062 | + if code: |
2063 | + fd = sys.stderr |
2064 | + else: |
2065 | + fd = sys.stdout |
2066 | + print >> fd, _(__doc__) |
2067 | + if msg: |
2068 | + print >> fd, msg |
2069 | + sys.exit(code) |
2070 | + |
2071 | + |
2072 | + |
2073 | |
2074 | +def yesno(opt): |
2075 | + i = opt.find('=') |
2076 | + yesno = opt[i+1:].lower() |
2077 | + if yesno in ('y', 'yes'): |
2078 | + return 1 |
2079 | + elif yesno in ('n', 'no'): |
2080 | + return 0 |
2081 | + else: |
2082 | + usage(1, _('Bad choice: %(yesno)s')) |
2083 | + # no return |
2084 | + |
2085 | + |
2086 | +def main(): |
2087 | + dryrun = 0 |
2088 | + digest = 0 |
2089 | + welcome = None |
2090 | + goodbye = None |
2091 | + filename = None |
2092 | + listname = None |
2093 | + notifyadmin = None |
2094 | + |
2095 | + # TBD: can't use getopt with this command line syntax, which is broken and |
2096 | + # should be changed to be getopt compatible. |
2097 | + i = 1 |
2098 | + while i < len(sys.argv): |
2099 | + opt = sys.argv[i] |
2100 | + if opt in ('-h', '--help'): |
2101 | + usage(0) |
2102 | + elif opt in ('-n', '--no-change'): |
2103 | + dryrun = 1 |
2104 | + i += 1 |
2105 | + print _('Dry run mode') |
2106 | + elif opt in ('-d', '--digest'): |
2107 | + digest = 1 |
2108 | + i += 1 |
2109 | + elif opt.startswith('-d=') or opt.startswith('--digest='): |
2110 | + digest = yesno(opt) |
2111 | + i += 1 |
2112 | + elif opt in ('-w', '--welcome-msg'): |
2113 | + welcome = 1 |
2114 | + i += 1 |
2115 | + elif opt.startswith('-w=') or opt.startswith('--welcome-msg='): |
2116 | + welcome = yesno(opt) |
2117 | + i += 1 |
2118 | + elif opt in ('-g', '--goodbye-msg'): |
2119 | + goodbye = 1 |
2120 | + i += 1 |
2121 | + elif opt.startswith('-g=') or opt.startswith('--goodbye-msg='): |
2122 | + goodbye = yesno(opt) |
2123 | + i += 1 |
2124 | + elif opt in ('-f', '--file'): |
2125 | + if filename is not None: |
2126 | + usage(1, _('Only one -f switch allowed')) |
2127 | + try: |
2128 | + filename = sys.argv[i+1] |
2129 | + except IndexError: |
2130 | + usage(1, _('No argument to -f given')) |
2131 | + i += 2 |
2132 | + elif opt in ('-a', '--notifyadmin'): |
2133 | + notifyadmin = 1 |
2134 | + i += 1 |
2135 | + elif opt.startswith('-a=') or opt.startswith('--notifyadmin='): |
2136 | + notifyadmin = yesno(opt) |
2137 | + i += 1 |
2138 | + elif opt[0] == '-': |
2139 | + usage(1, _('Illegal option: %(opt)s')) |
2140 | + else: |
2141 | + try: |
2142 | + listname = sys.argv[i].lower() |
2143 | + i += 1 |
2144 | + except IndexError: |
2145 | + usage(1, _('No listname given')) |
2146 | + break |
2147 | + |
2148 | + if listname is None or filename is None: |
2149 | + usage(1, _('Must have a listname and a filename')) |
2150 | + |
2151 | + # read the list of addresses to sync to from the file |
2152 | + if filename == '-': |
2153 | + filemembers = sys.stdin.readlines() |
2154 | + else: |
2155 | + try: |
2156 | + fp = open(filename) |
2157 | + except IOError, (code, msg): |
2158 | + usage(1, _('Cannot read address file: %(filename)s: %(msg)s')) |
2159 | + try: |
2160 | + filemembers = fp.readlines() |
2161 | + finally: |
2162 | + fp.close() |
2163 | + |
2164 | + # strip out lines we don't care about, they are comments (# in first |
2165 | + # non-whitespace) or are blank |
2166 | + for i in range(len(filemembers)-1, -1, -1): |
2167 | + addr = filemembers[i].strip() |
2168 | + if addr == '' or addr[:1] == '#': |
2169 | + del filemembers[i] |
2170 | + print _('Ignore : %(addr)30s') |
2171 | + |
2172 | + # first filter out any invalid addresses |
2173 | + filemembers = email.Utils.getaddresses(filemembers) |
2174 | + invalid = 0 |
2175 | + for name, addr in filemembers: |
2176 | + try: |
2177 | + Utils.ValidateEmail(addr) |
2178 | + except Errors.EmailAddressError: |
2179 | + print _('Invalid : %(addr)30s') |
2180 | + invalid = 1 |
2181 | + if invalid: |
2182 | + print _('You must fix the preceding invalid addresses first.') |
2183 | + sys.exit(1) |
2184 | + |
2185 | + # get the locked list object |
2186 | + try: |
2187 | + mlist = MailList.MailList(listname) |
2188 | + except Errors.MMListError, e: |
2189 | + print _('No such list: %(listname)s') |
2190 | + sys.exit(1) |
2191 | + |
2192 | + try: |
2193 | + # Get the list of addresses currently subscribed |
2194 | + addrs = {} |
2195 | + needsadding = {} |
2196 | + matches = {} |
2197 | + for addr in mlist.getMemberCPAddresses(mlist.getMembers()): |
2198 | + addrs[addr.lower()] = addr |
2199 | + |
2200 | + for name, addr in filemembers: |
2201 | + # Any address found in the file that is also in the list can be |
2202 | + # ignored. If not found in the list, it must be added later. |
2203 | + laddr = addr.lower() |
2204 | + if addrs.has_key(laddr): |
2205 | + del addrs[laddr] |
2206 | + matches[laddr] = 1 |
2207 | + elif not matches.has_key(laddr): |
2208 | + needsadding[laddr] = (name, addr) |
2209 | + |
2210 | + if not needsadding and not addrs: |
2211 | + print _('Nothing to do.') |
2212 | + sys.exit(0) |
2213 | + |
2214 | + enc = sys.getdefaultencoding() |
2215 | + # addrs contains now all the addresses that need removing |
2216 | + for laddr, (name, addr) in needsadding.items(): |
2217 | + pw = Utils.MakeRandomPassword() |
2218 | + # should not already be subscribed, otherwise our test above is |
2219 | + # broken. Bogosity is if the address is listed in the file more |
2220 | + # than once. Second and subsequent ones trigger an |
2221 | + # MMAlreadyAMember error. Just catch it and go on. |
2222 | + userdesc = UserDesc(addr, name, pw, digest) |
2223 | + try: |
2224 | + if not dryrun: |
2225 | + mlist.ApprovedAddMember(userdesc, welcome, notifyadmin) |
2226 | + s = email.Utils.formataddr((name, addr)).encode(enc, 'replace') |
2227 | + print _('Added : %(s)s') |
2228 | + except Errors.MMAlreadyAMember: |
2229 | + pass |
2230 | + |
2231 | + for laddr, addr in addrs.items(): |
2232 | + # Should be a member, otherwise our test above is broken |
2233 | + name = mlist.getMemberName(laddr) or '' |
2234 | + if not dryrun: |
2235 | + try: |
2236 | + mlist.ApprovedDeleteMember(addr, admin_notif=notifyadmin, |
2237 | + userack=goodbye) |
2238 | + except Errors.NotAMemberError: |
2239 | + # This can happen if the address is illegal (i.e. can't be |
2240 | + # parsed by email.Utils.parseaddr()) but for legacy |
2241 | + # reasons is in the database. Use a lower level remove to |
2242 | + # get rid of this member's entry |
2243 | + mlist.removeMember(addr) |
2244 | + s = email.Utils.formataddr((name, addr)).encode(enc, 'replace') |
2245 | + print _('Removed: %(s)s') |
2246 | + |
2247 | + mlist.Save() |
2248 | + finally: |
2249 | + mlist.Unlock() |
2250 | + |
2251 | + |
2252 | +if __name__ == '__main__': |
2253 | + main() |
2254 | |
2255 | === added file 'bin/templ2pot.py' |
2256 | --- bin/templ2pot.py 1970-01-01 00:00:00 +0000 |
2257 | +++ bin/templ2pot.py 2010-10-14 12:15:59 +0000 |
2258 | @@ -0,0 +1,120 @@ |
2259 | +#! @PYTHON@ |
2260 | +# Code stolen from pygettext.py |
2261 | +# by Tokio Kikuchi <tkikuchi@is.kochi-u.ac.jp> |
2262 | + |
2263 | +"""templ2pot.py -- convert mailman template (en) to pot format. |
2264 | + |
2265 | +Usage: templ2pot.py inputfile ... |
2266 | + |
2267 | +Options: |
2268 | + |
2269 | + -h, --help |
2270 | + |
2271 | +Inputfiles are english templates. Outputs are written to stdout. |
2272 | +""" |
2273 | + |
2274 | +import sys |
2275 | +import getopt |
2276 | + |
2277 | + |
2278 | + |
2279 | |
2280 | +try: |
2281 | + import paths |
2282 | + from Mailman.i18n import _ |
2283 | +except ImportError: |
2284 | + def _(s): return s |
2285 | + |
2286 | +EMPTYSTRING = '' |
2287 | + |
2288 | + |
2289 | + |
2290 | |
2291 | +def usage(code, msg=''): |
2292 | + if code: |
2293 | + fd = sys.stderr |
2294 | + else: |
2295 | + fd = sys.stdout |
2296 | + print >> fd, _(__doc__) % globals() |
2297 | + if msg: |
2298 | + print >> fd, msg |
2299 | + sys.exit(code) |
2300 | + |
2301 | + |
2302 | + |
2303 | |
2304 | +escapes = [] |
2305 | + |
2306 | +def make_escapes(pass_iso8859): |
2307 | + global escapes |
2308 | + if pass_iso8859: |
2309 | + # Allow iso-8859 characters to pass through so that e.g. 'msgid |
2310 | + # "H[o-umlaut]he"' would result not result in 'msgid "H\366he"'. |
2311 | + # Otherwise we escape any character outside the 32..126 range. |
2312 | + mod = 128 |
2313 | + else: |
2314 | + mod = 256 |
2315 | + for i in range(256): |
2316 | + if 32 <= (i % mod) <= 126: |
2317 | + escapes.append(chr(i)) |
2318 | + else: |
2319 | + escapes.append("\\%03o" % i) |
2320 | + escapes[ord('\\')] = '\\\\' |
2321 | + escapes[ord('\t')] = '\\t' |
2322 | + escapes[ord('\r')] = '\\r' |
2323 | + escapes[ord('\n')] = '\\n' |
2324 | + escapes[ord('\"')] = '\\"' |
2325 | + |
2326 | + |
2327 | +def escape(s): |
2328 | + global escapes |
2329 | + s = list(s) |
2330 | + for i in range(len(s)): |
2331 | + s[i] = escapes[ord(s[i])] |
2332 | + return EMPTYSTRING.join(s) |
2333 | + |
2334 | + |
2335 | +def normalize(s): |
2336 | + # This converts the various Python string types into a format that is |
2337 | + # appropriate for .po files, namely much closer to C style. |
2338 | + lines = s.splitlines() |
2339 | + if len(lines) == 1: |
2340 | + s = '"' + escape(s) + '"' |
2341 | + else: |
2342 | + if not lines[-1]: |
2343 | + del lines[-1] |
2344 | + lines[-1] = lines[-1] + '\n' |
2345 | + for i in range(len(lines)): |
2346 | + lines[i] = escape(lines[i]) |
2347 | + lineterm = '\\n"\n"' |
2348 | + s = '""\n"' + lineterm.join(lines) + '"' |
2349 | + return s |
2350 | + |
2351 | + |
2352 | + |
2353 | |
2354 | +def main(): |
2355 | + try: |
2356 | + opts, args = getopt.getopt( |
2357 | + sys.argv[1:], |
2358 | + 'h', |
2359 | + ['help',] |
2360 | + ) |
2361 | + except getopt.error, msg: |
2362 | + usage(1, msg) |
2363 | + |
2364 | + # parse options |
2365 | + for opt, arg in opts: |
2366 | + if opt in ('-h', '--help'): |
2367 | + usage(0) |
2368 | + |
2369 | + # calculate escapes |
2370 | + make_escapes(0) |
2371 | + |
2372 | + for filename in args: |
2373 | + print '#: %s:1' % filename |
2374 | + s = file(filename).read() |
2375 | + print '#, template' |
2376 | + print 'msgid', normalize(s) |
2377 | + print 'msgstr ""\n' |
2378 | + |
2379 | + |
2380 | + |
2381 | |
2382 | +if __name__ == '__main__': |
2383 | + main() |
2384 | |
2385 | === added file 'bin/transcheck' |
2386 | --- bin/transcheck 1970-01-01 00:00:00 +0000 |
2387 | +++ bin/transcheck 2010-10-14 12:15:59 +0000 |
2388 | @@ -0,0 +1,412 @@ |
2389 | +#! @PYTHON@ |
2390 | +# |
2391 | +# transcheck - (c) 2002 by Simone Piunno <pioppo@ferrara.linux.it> |
2392 | +# |
2393 | +# This program is free software; you can redistribute it and/or modify it |
2394 | +# under the terms of the version 2.0 of the GNU General Public License as |
2395 | +# published by the Free Software Foundation. |
2396 | +# |
2397 | +# This program is distributed in the hope that it will be useful, but |
2398 | +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
2399 | +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
2400 | +# for more details. |
2401 | +# |
2402 | +# You should have received a copy of the GNU General Public License along |
2403 | +# with this program; if not, write to the Free Software Foundation, Inc., |
2404 | +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
2405 | + |
2406 | +""" |
2407 | +Check a given Mailman translation, making sure that variables and |
2408 | +tags referenced in translation are the same variables and tags in |
2409 | +the original templates and catalog. |
2410 | + |
2411 | +Usage: |
2412 | + |
2413 | +cd $MAILMAN_DIR |
2414 | +%(program)s [-q] <lang> |
2415 | + |
2416 | +Where <lang> is your country code (e.g. 'it' for Italy) and -q is |
2417 | +to ask for a brief summary. |
2418 | +""" |
2419 | + |
2420 | +import sys |
2421 | +import re |
2422 | +import os |
2423 | +import getopt |
2424 | + |
2425 | +import paths |
2426 | +from Mailman.i18n import _ |
2427 | + |
2428 | +program = sys.argv[0] |
2429 | + |
2430 | + |
2431 | + |
2432 | |
2433 | +def usage(code, msg=''): |
2434 | + if code: |
2435 | + fd = sys.stderr |
2436 | + else: |
2437 | + fd = sys.stdout |
2438 | + print >> fd, _(__doc__) |
2439 | + if msg: |
2440 | + print >> fd, msg |
2441 | + sys.exit(code) |
2442 | + |
2443 | + |
2444 | + |
2445 | |
2446 | +class TransChecker: |
2447 | + "check a translation comparing with the original string" |
2448 | + def __init__(self, regexp, escaped=None): |
2449 | + self.dict = {} |
2450 | + self.errs = [] |
2451 | + self.regexp = re.compile(regexp) |
2452 | + self.escaped = None |
2453 | + if escaped: |
2454 | + self.escaped = re.compile(escaped) |
2455 | + |
2456 | + def checkin(self, string): |
2457 | + "scan a string from the original file" |
2458 | + for key in self.regexp.findall(string): |
2459 | + if self.escaped and self.escaped.match(key): |
2460 | + continue |
2461 | + if self.dict.has_key(key): |
2462 | + self.dict[key] += 1 |
2463 | + else: |
2464 | + self.dict[key] = 1 |
2465 | + |
2466 | + def checkout(self, string): |
2467 | + "scan a translated string" |
2468 | + for key in self.regexp.findall(string): |
2469 | + if self.escaped and self.escaped.match(key): |
2470 | + continue |
2471 | + if self.dict.has_key(key): |
2472 | + self.dict[key] -= 1 |
2473 | + else: |
2474 | + self.errs.append( |
2475 | + "%(key)s was not found" % |
2476 | + { 'key' : key } |
2477 | + ) |
2478 | + |
2479 | + def computeErrors(self): |
2480 | + "check for differences between checked in and checked out" |
2481 | + for key in self.dict.keys(): |
2482 | + if self.dict[key] < 0: |
2483 | + self.errs.append( |
2484 | + "Too much %(key)s" % |
2485 | + { 'key' : key } |
2486 | + ) |
2487 | + if self.dict[key] > 0: |
2488 | + self.errs.append( |
2489 | + "Too few %(key)s" % |
2490 | + { 'key' : key } |
2491 | + ) |
2492 | + return self.errs |
2493 | + |
2494 | + def status(self): |
2495 | + if self.errs: |
2496 | + return "FAILED" |
2497 | + else: |
2498 | + return "OK" |
2499 | + |
2500 | + def errorsAsString(self): |
2501 | + msg = "" |
2502 | + for err in self.errs: |
2503 | + msg += " - %(err)s" % { 'err': err } |
2504 | + return msg |
2505 | + |
2506 | + def reset(self): |
2507 | + self.dict = {} |
2508 | + self.errs = [] |
2509 | + |
2510 | + |
2511 | + |
2512 | |
2513 | +class POParser: |
2514 | + "parse a .po file extracting msgids and msgstrs" |
2515 | + def __init__(self, filename=""): |
2516 | + self.status = 0 |
2517 | + self.files = [] |
2518 | + self.msgid = "" |
2519 | + self.msgstr = "" |
2520 | + self.line = 1 |
2521 | + self.f = None |
2522 | + self.esc = { "n": "\n", "r": "\r", "t": "\t" } |
2523 | + if filename: |
2524 | + self.f = open(filename) |
2525 | + |
2526 | + def open(self, filename): |
2527 | + self.f = open(filename) |
2528 | + |
2529 | + def close(self): |
2530 | + self.f.close() |
2531 | + |
2532 | + def parse(self): |
2533 | + """States table for the finite-states-machine parser: |
2534 | + 0 idle |
2535 | + 1 filename-or-comment |
2536 | + 2 msgid |
2537 | + 3 msgstr |
2538 | + 4 end |
2539 | + """ |
2540 | + # each time we can safely re-initialize those vars |
2541 | + self.files = [] |
2542 | + self.msgid = "" |
2543 | + self.msgstr = "" |
2544 | + |
2545 | + |
2546 | + # can't continue if status == 4, this is a dead status |
2547 | + if self.status == 4: |
2548 | + return 0 |
2549 | + |
2550 | + while 1: |
2551 | + # continue scanning, char-by-char |
2552 | + c = self.f.read(1) |
2553 | + if not c: |
2554 | + # EOF -> maybe we have a msgstr to save? |
2555 | + self.status = 4 |
2556 | + if self.msgstr: |
2557 | + return 1 |
2558 | + else: |
2559 | + return 0 |
2560 | + |
2561 | + # keep the line count up-to-date |
2562 | + if c == "\n": |
2563 | + self.line += 1 |
2564 | + |
2565 | + # a pound was detected the previous char... |
2566 | + if self.status == 1: |
2567 | + if c == ":": |
2568 | + # was a line of filenames |
2569 | + row = self.f.readline() |
2570 | + self.files += row.split() |
2571 | + self.line += 1 |
2572 | + elif c == "\n": |
2573 | + # was a single pount on the line |
2574 | + pass |
2575 | + else: |
2576 | + # was a comment... discard |
2577 | + self.f.readline() |
2578 | + self.line += 1 |
2579 | + # in every case, we switch to idle status |
2580 | + self.status = 0; |
2581 | + continue |
2582 | + |
2583 | + # in idle status we search for a '#' or for a 'm' |
2584 | + if self.status == 0: |
2585 | + if c == "#": |
2586 | + # this could be a comment or a filename |
2587 | + self.status = 1; |
2588 | + continue |
2589 | + elif c == "m": |
2590 | + # this should be a msgid start... |
2591 | + s = self.f.read(4) |
2592 | + assert s == "sgid" |
2593 | + # so now we search for a '"' |
2594 | + self.status = 2 |
2595 | + continue |
2596 | + # in idle only those other chars are possibile |
2597 | + assert c in [ "\n", " ", "\t" ] |
2598 | + |
2599 | + # searching for the msgid string |
2600 | + if self.status == 2: |
2601 | + if c == "\n": |
2602 | + # a double LF is not possible here |
2603 | + c = self.f.read(1) |
2604 | + assert c != "\n" |
2605 | + if c == "\"": |
2606 | + # ok, this is the start of the string, |
2607 | + # now search for the end |
2608 | + while 1: |
2609 | + c = self.f.read(1) |
2610 | + if not c: |
2611 | + # EOF, bailout |
2612 | + self.status = 4 |
2613 | + return 0 |
2614 | + if c == "\\": |
2615 | + # a quoted char... |
2616 | + c = self.f.read(1) |
2617 | + if self.esc.has_key(c): |
2618 | + self.msgid += self.esc[c] |
2619 | + else: |
2620 | + self.msgid += c |
2621 | + continue |
2622 | + if c == "\"": |
2623 | + # end of string found |
2624 | + break |
2625 | + # a normal char, add it |
2626 | + self.msgid += c |
2627 | + if c == "m": |
2628 | + # this should be a msgstr identifier |
2629 | + s = self.f.read(5) |
2630 | + assert s == "sgstr" |
2631 | + # ok, now search for the msgstr string |
2632 | + self.status = 3 |
2633 | + |
2634 | + # searching for the msgstr string |
2635 | + if self.status == 3: |
2636 | + if c == "\n": |
2637 | + # a double LF is the end of the msgstr! |
2638 | + c = self.f.read(1) |
2639 | + if c == "\n": |
2640 | + # ok, time to go idle and return |
2641 | + self.status = 0 |
2642 | + self.line += 1 |
2643 | + return 1 |
2644 | + if c == "\"": |
2645 | + # start of string found |
2646 | + while 1: |
2647 | + c = self.f.read(1) |
2648 | + if not c: |
2649 | + # EOF, bail out |
2650 | + self.status = 4 |
2651 | + return 1 |
2652 | + if c == "\\": |
2653 | + # a quoted char... |
2654 | + c = self.f.read(1) |
2655 | + if self.esc.has_key(c): |
2656 | + self.msgid += self.esc[c] |
2657 | + else: |
2658 | + self.msgid += c |
2659 | + continue |
2660 | + if c == "\"": |
2661 | + # end of string |
2662 | + break |
2663 | + # a normal char, add it |
2664 | + self.msgstr += c |
2665 | + |
2666 | + |
2667 | + |
2668 | + |
2669 | |
2670 | +def check_file(translatedFile, originalFile, html=0, quiet=0): |
2671 | + """check a translated template against the original one |
2672 | + search also <MM-*> tags if html is not zero""" |
2673 | + |
2674 | + if html: |
2675 | + c = TransChecker("(%%|%\([^)]+\)[0-9]*[sd]|</?MM-[^>]+>)", "^%%$") |
2676 | + else: |
2677 | + c = TransChecker("(%%|%\([^)]+\)[0-9]*[sd])", "^%%$") |
2678 | + |
2679 | + try: |
2680 | + f = open(originalFile) |
2681 | + except IOError: |
2682 | + if not quiet: |
2683 | + print " - Can'open original file " + originalFile |
2684 | + return 1 |
2685 | + |
2686 | + while 1: |
2687 | + line = f.readline() |
2688 | + if not line: break |
2689 | + c.checkin(line) |
2690 | + |
2691 | + f.close() |
2692 | + |
2693 | + try: |
2694 | + f = open(translatedFile) |
2695 | + except IOError: |
2696 | + if not quiet: |
2697 | + print " - Can'open translated file " + translatedFile |
2698 | + return 1 |
2699 | + |
2700 | + while 1: |
2701 | + line = f.readline() |
2702 | + if not line: break |
2703 | + c.checkout(line) |
2704 | + |
2705 | + f.close() |
2706 | + |
2707 | + n = 0 |
2708 | + msg = "" |
2709 | + for desc in c.computeErrors(): |
2710 | + n +=1 |
2711 | + if not quiet: |
2712 | + print " - %(desc)s" % { 'desc': desc } |
2713 | + return n |
2714 | + |
2715 | + |
2716 | + |
2717 | |
2718 | +def check_po(file, quiet=0): |
2719 | + "scan the po file comparing msgids with msgstrs" |
2720 | + n = 0 |
2721 | + p = POParser(file) |
2722 | + c = TransChecker("(%%|%\([^)]+\)[0-9]*[sdu]|%[0-9]*[sdu])", "^%%$") |
2723 | + while p.parse(): |
2724 | + if p.msgstr: |
2725 | + c.reset() |
2726 | + c.checkin(p.msgid) |
2727 | + c.checkout(p.msgstr) |
2728 | + for desc in c.computeErrors(): |
2729 | + n += 1 |
2730 | + if not quiet: |
2731 | + print " - near line %(line)d %(file)s: %(desc)s" % { |
2732 | + 'line': p.line, |
2733 | + 'file': p.files, |
2734 | + 'desc': desc |
2735 | + } |
2736 | + p.close() |
2737 | + return n |
2738 | + |
2739 | + |
2740 | |
2741 | +def main(): |
2742 | + try: |
2743 | + opts, args = getopt.getopt(sys.argv[1:], 'qh', ['quiet', 'help']) |
2744 | + except getopt.error, msg: |
2745 | + usage(1, msg) |
2746 | + |
2747 | + quiet = 0 |
2748 | + for opt, arg in opts: |
2749 | + if opt in ('-h', '--help'): |
2750 | + usage(0) |
2751 | + elif opt in ('-q', '--quiet'): |
2752 | + quiet = 1 |
2753 | + |
2754 | + if len(args) <> 1: |
2755 | + usage(1) |
2756 | + |
2757 | + lang = args[0] |
2758 | + |
2759 | + isHtml = re.compile("\.html$"); |
2760 | + isTxt = re.compile("\.txt$"); |
2761 | + |
2762 | + numerrors = 0 |
2763 | + numfiles = 0 |
2764 | + try: |
2765 | + files = os.listdir("templates/" + lang + "/") |
2766 | + except: |
2767 | + print "can't open templates/%s/" % lang |
2768 | + for file in files: |
2769 | + fileEN = "templates/en/" + file |
2770 | + fileIT = "templates/" + lang + "/" + file |
2771 | + errlist = [] |
2772 | + if isHtml.search(file): |
2773 | + if not quiet: |
2774 | + print "HTML checking " + fileIT + "... " |
2775 | + n = check_file(fileIT, fileEN, html=1, quiet=quiet) |
2776 | + if n: |
2777 | + numerrors += n |
2778 | + numfiles += 1 |
2779 | + elif isTxt.search(file): |
2780 | + if not quiet: |
2781 | + print "TXT checking " + fileIT + "... " |
2782 | + n = check_file(fileIT, fileEN, html=0, quiet=quiet) |
2783 | + if n: |
2784 | + numerrors += n |
2785 | + numfiles += 1 |
2786 | + |
2787 | + else: |
2788 | + continue |
2789 | + |
2790 | + file = "messages/" + lang + "/LC_MESSAGES/mailman.po" |
2791 | + if not quiet: |
2792 | + print "PO checking " + file + "... " |
2793 | + n = check_po(file, quiet=quiet) |
2794 | + if n: |
2795 | + numerrors += n |
2796 | + numfiles += 1 |
2797 | + |
2798 | + if quiet: |
2799 | + print "%(errs)u warnings in %(files)u files" % { |
2800 | + 'errs': numerrors, |
2801 | + 'files': numfiles |
2802 | + } |
2803 | + |
2804 | + |
2805 | |
2806 | +if __name__ == '__main__': |
2807 | + main() |
2808 | |
2809 | === added directory 'contrib' |
2810 | === renamed directory 'contrib' => 'contrib.moved' |
2811 | === added file 'contrib/README' |
2812 | --- contrib/README 1970-01-01 00:00:00 +0000 |
2813 | +++ contrib/README 2010-10-14 12:15:59 +0000 |
2814 | @@ -0,0 +1,4 @@ |
2815 | +This directory contains unofficial contributed scripts and extensions |
2816 | +to Mailman. They are unsupported by the Mailman developers. If you |
2817 | +have questions or problems with them, please contact the contribution |
2818 | +author directly. |
2819 | |
2820 | === added file 'contrib/README.check_perms_grsecurity' |
2821 | --- contrib/README.check_perms_grsecurity 1970-01-01 00:00:00 +0000 |
2822 | +++ contrib/README.check_perms_grsecurity 2010-10-14 12:15:59 +0000 |
2823 | @@ -0,0 +1,14 @@ |
2824 | +The check_perms_grsecurity.py script, if copied in your installed |
2825 | +~mailman/bin/ directory and run from there will modify permissions of |
2826 | +files so that Mailman with extra restrictions imposed by linux kernel security |
2827 | +patches like securelinux/openwall in 2.2.x or grsecurity in 2.4.x |
2828 | + |
2829 | +The way it works is that it makes sure that the UID of any script that |
2830 | +touches config.pck is `mailman'. What this means however is that |
2831 | +scripts in ~mailman/bin will now only work if run as user mailman or |
2832 | +root (the script then changes its UID and GID to mailman). |
2833 | +To make grsecurity happy, we remove the group writeable bit on a directories |
2834 | +that contain binaries. |
2835 | + |
2836 | +Enjoy |
2837 | +Marc MERLIN <marc_soft@merlins.org>/<marc_bts@vasoftware.com> - 2001/12/10 |
2838 | |
2839 | === added file 'contrib/README.mm-handler' |
2840 | --- contrib/README.mm-handler 1970-01-01 00:00:00 +0000 |
2841 | +++ contrib/README.mm-handler 2010-10-14 12:15:59 +0000 |
2842 | @@ -0,0 +1,215 @@ |
2843 | +Contributed by David Champion <dgc@uchicago.edu> |
2844 | +See also ../README.SENDMAIL |
2845 | + |
2846 | +What? |
2847 | +----- |
2848 | +Mm-handler is a mail delivery agent (MDA) -- a "mailer", in Sendmail |
2849 | +lingo. Its function is to assume authority for messages destined to |
2850 | +Mailman lists, so that they're off sendmail's hands, and you (the site |
2851 | +administrator) don't need to maintain databases of aliases and such. It |
2852 | +takes a small bit of work to set up, but once that's done, you'll never |
2853 | +need to mess with aliases for Mailman's sake again. |
2854 | + |
2855 | +When? |
2856 | +----- |
2857 | +The only further catch is that mm-handler is only really useful when |
2858 | +it mostly "owns" its mail domain (the hostname part after an e-mail |
2859 | +address's "@" symbol) -- when you can dedicate the mail domain to |
2860 | +Mailman. If you have a limited set of exceptions -- a few users, for |
2861 | +example -- you can still use it, but for sites with a dynamic or even |
2862 | +mix of users (or forwarders) and lists, it might not gain you much. |
2863 | + |
2864 | +How do you know whether mm-handler is appropriate for you? Let's look |
2865 | +at some examples. If you're running lists off your primary DNS domain |
2866 | +name, you probably have a mix of lists and users in your namespace. Take |
2867 | +python.org, for example: it hosts Mailman lists, and it hosts users' |
2868 | +personal accounts. There aren't a whole bunch of either, but the ratio |
2869 | +is probably fairly near 1:1. Mm-handler is not very useful here, because |
2870 | +there's no simple way to separate user addresses from list addresses -- |
2871 | +not to mention that mm-handler is written in perl, so that's just bad |
2872 | +form. |
2873 | + |
2874 | +This begs two different, complementary situations. A hypothetical |
2875 | +domain, incidents.int, is used mostly for mailing lists. It's a |
2876 | +front-end site, and not a general user mail service. There might be |
2877 | +a couple of user addresses -- system administrators and such -- but |
2878 | +these are few in number and easily adjusted manually by the site |
2879 | +administrator. The 250 mailing lists at the site are much more dynamic, |
2880 | +and a much bigger pain to keep track of by editing an alias file. This |
2881 | +site can easily benefit from mm-handler. |
2882 | + |
2883 | +Inversely, mail.aero, another hypothetical domain, provides POP mail |
2884 | +service to employees of the aerospace industry. Its addresses are |
2885 | +almost entirely users, although it maintains a few mailing lists for |
2886 | +convenience. This site has nothing to gain from mm-handler. It's much |
2887 | +easier to maintain an alias file of 10 lists than to dedicate the domain |
2888 | +to Mailman, and put all 10,000 aerospace workers in a user table. |
2889 | + |
2890 | +Those are the trickier cases. The case where mm-handler really works |
2891 | +best is when you can dedicate a single hostname under your DNS domain |
2892 | +to mailing lists, and host no user accounts there. At the University of |
2893 | +Chicago, we do this with listhost.uchicago.edu. SourceForge does this, |
2894 | +too, although I don't believe they use Sendmail. |
2895 | + |
2896 | +If your site is like that, you should read on. |
2897 | + |
2898 | +How? |
2899 | +---- |
2900 | +Set-up isn't all that complicated. I've included a file here called |
2901 | +"mailman.mc". This is the m4 file that I use on my list server, and you |
2902 | +can likely use it with few changes at your site. It's well-annotated; |
2903 | +the rationale for each parameter (or set of parameters) is provided in |
2904 | +m4 (ahem) comments. |
2905 | + |
2906 | +So, the simple steps are as follows: |
2907 | + |
2908 | +1. Copy mailman.mc, and make any changes you need at your site. You |
2909 | + DEFINITELY need some changes. There are hostnames in there that you |
2910 | + need to adjust, and chances are that you'll need to change some other |
2911 | + parameters (like the host OS), too. [1] |
2912 | + |
2913 | +2. Install mm-handler. Because my server's sendmail-related files live |
2914 | + in /etc/mail, I keep mm-handler there, too. YMMV. |
2915 | + |
2916 | +3. Edit mm-handler, and make any changes you need at your site. You |
2917 | + probably want to change $MMWRAPPER and $MMLISTDIR at line 14, and you |
2918 | + *might* want to take a look at the helpful boilerplate text beginning |
2919 | + at line 64. (This text is sent whenever someone tries to send mail to |
2920 | + a nonexistent list address on your mail domain.) |
2921 | + |
2922 | +4. You should set up a virtusertable. (See mailman.mc for an |
2923 | + explanation.) There's an example of a good, minimal virtusertable |
2924 | + in this distribution. The virtusertable begins as a text file named |
2925 | + "virtusertable", stored in the same directory as all the other |
2926 | + Sendmail files, but it's converted to a map file for Sendmail's use. |
2927 | + Install the virtusertable, and (re)make the map file. [2] |
2928 | + |
2929 | +5. You absolutely must have a mailertable, or all of this goes nowhere. |
2930 | + Like virtusertable, the mailertable is a map that begins as text and |
2931 | + gets converted. It's named "mailertable", and it's probably pretty |
2932 | + simple. Mine looks like this: |
2933 | + |
2934 | + listtest.uchicago.edu mailman:listtest.uchicago.edu |
2935 | + |
2936 | + This says: assign all incoming mail (that was not intercepted by the |
2937 | + virtusertable) and that is in the listtest.uchicago.edu domain to the |
2938 | + "mailman" mailer, and tell the "mailman" mailer that the hostname |
2939 | + we're using is "listtest.uchicago.edu". You can support multiple |
2940 | + virtual hosts using mm-handler just by placing corresponding lines in |
2941 | + mailertable. |
2942 | + |
2943 | + Be sure to make this map, too! |
2944 | + |
2945 | +6. The mailer definition (see the end of mailman.mc, or your own .mc |
2946 | + file) for mm-handler sets the user/group that mm-handler will run |
2947 | + under. (I use mailman:other.) Be sure that mm-handler is executable |
2948 | + by this user or group. You almost certainly need the user to be the |
2949 | + same as the Mailman user, and this user is almost always called |
2950 | + "mailman", so you probably shouldn't change the defaults. |
2951 | + |
2952 | +7. Generate your new sendmail.cf file. See the sendmail documentation if |
2953 | + you're not familiar with this procedure. [1] |
2954 | + |
2955 | +8. Stop sendmail on your list server, if you haven't already. Install |
2956 | + the new sendmail.cf file wherever your sendmail.cf file belongs. |
2957 | + (This depends on how sendmail was compiled, but most systems support |
2958 | + using /etc/sendmail.cf.) |
2959 | + |
2960 | +9. Cross your fingers and restart sendmail. |
2961 | + |
2962 | +A. Barry warns that Mailman now needs you to modify your |
2963 | + Mailman/mm_cfg.py file, adding this line: |
2964 | + |
2965 | + MTA = None |
2966 | + |
2967 | + This tells Mailman that it doesn't need to do anything special when |
2968 | + it creates or deletes mailing lists through the web. For more |
2969 | + information, take a look at the comments for this variable in your |
2970 | + Defaults.py file (but never edit this file -- always edit mm_cfg.py |
2971 | + instead!). |
2972 | + |
2973 | +That's it! With any luck, you're fully functional. |
2974 | + |
2975 | +-- |
2976 | +[1] The .mc file is the standard, supported way of configuring sendmail. |
2977 | + I'm not going to get into this here, and I'm not going to tell |
2978 | + you how to write raw sendmail.cf stuff, because if you need to do |
2979 | + it this way then you need something more comprehensive than I can |
2980 | + provide. If you need help with the .mc -> .cf process, I recommend |
2981 | + these links: |
2982 | + |
2983 | + http://www.sendmail.org/~ca/email/setup1.html |
2984 | + http://www.sendmail.org/~ca/email/doc8.9/README.cf.html |
2985 | + http://www.hserus.net/sendmail.html |
2986 | + |
2987 | +[2] This is often done with something like "makemap hash |
2988 | + /etc/virtusertable </etc/virtusertable", but it could be different |
2989 | + on your server. Consult the sendmail documentation if you do not |
2990 | + know. |
2991 | + |
2992 | + |
2993 | +The following note is provided by Kevin McNamee <kevin.mcnamee(at)symsoft.se> |
2994 | +regarding solving a problem with mail to list addresses being rejected for |
2995 | +"user unknown". Reference: |
2996 | +<http://mail.python.org/pipermail/mailman-users/2006-February/049235.html> |
2997 | + |
2998 | + |
2999 | +"User unknown" analysis |
3000 | +======================= |
3001 | +If the "user unknown" problem arises, then sendmail is not |
3002 | +recognising your domain as a "mailman" domain. |
3003 | +The problem could be that your mailman.mydomain.com is defined as a |
3004 | +CNAME not a real DNS record. |
3005 | + |
3006 | +A hint from a tutorial about Masquerading: |
3007 | +http://www.feep.net/sendmail/tutorial/config/masquerading.html |
3008 | +"This address must be an address record in DNS, not simply |
3009 | +a CNAME, or the remote end will canonicalize the address back |
3010 | +to the original name." |
3011 | + |
3012 | +First confirm the problem |
3013 | +# sendmail -bv testlist<at>mailman.mydomain.com |
3014 | +testlist<at>mailman.mydomain.com... User unknown |
3015 | + |
3016 | +Then confirm that mailertable is operational |
3017 | +# sendmail -d -bv jbloggs<at>hotmail.com | egrep "map_rewrite|mailertable" |
3018 | +map_lookup(host, hotmail.com) => host_map_lookup(hotmail.com) => |
3019 | +map_rewrite(hotmail.com), av = |
3020 | +map_rewrite => hotmail.com. |
3021 | +map_lookup(mailertable, hotmail.com) => NOT FOUND (0) |
3022 | +map_lookup(mailertable, .com) => NOT FOUND (0) |
3023 | +map_lookup(mailertable, .) => NOT FOUND (0) |
3024 | + |
3025 | +Then confirm that your domain (CNAME) is being canonicalised: |
3026 | +# sendmail -d -bv testlist<at>mailman.mydomain.com | egrep |
3027 | +"map_rewrite|mailertable" |
3028 | +map_lookup(host, mailman.mydomain.com) => |
3029 | +host_map_lookup(mailman.mydomain.com) => map_rewrite(aserver.mydomain.com), |
3030 | +av = |
3031 | +map_rewrite => aserver.mydomain.com. |
3032 | + |
3033 | +Sendmail has done an nslookup and found the real name of your domain which |
3034 | +would not match your settings in mailertable (if sendmail got that far). |
3035 | + |
3036 | +If you remove the CNAME and create a real subdomain, then the problem will |
3037 | +go away: |
3038 | +# sendmail -bv testlist<at>mailman.mydomain.com |
3039 | +testlist<at>mailman.mydomain.com... deliverable: mailer mailman, host |
3040 | +testlist<at>mailman.mydomain.com, user testlist |
3041 | + |
3042 | +You will still need to create a new CNAME in your sub-domain for Apache to |
3043 | +work. |
3044 | + |
3045 | +Conclusion: |
3046 | +It is very important to make clear in the Mailman installation instructions |
3047 | +that a REAL subdomain is needed. Those of us not familiar with DNS (or |
3048 | +sendmail for that matter) can succeed in getting the whole Mailman |
3049 | +installation working including the (Apache) web-interface and subscription |
3050 | +management using just a CNAME and then wonder why we cannot send mail to our |
3051 | +list. Hope this is of use. |
3052 | + |
3053 | +Ed. note: the above "conclusion" applies in this mm-handler case, but it |
3054 | +normally does not apply if list mail is delivered via aliases. |
3055 | + |
3056 | +-- |
3057 | +$Id: README.mm-handler 7780 2006-02-20 03:33:19Z msapiro $ |
3058 | |
3059 | === added file 'contrib/README.mmdsr' |
3060 | --- contrib/README.mmdsr 1970-01-01 00:00:00 +0000 |
3061 | +++ contrib/README.mmdsr 2010-10-14 12:15:59 +0000 |
3062 | @@ -0,0 +1,45 @@ |
3063 | +Daily Status Report script... |
3064 | + |
3065 | +The mmdsr script was created by Brad Knowles to produce a daily status report |
3066 | +for mailman. It was initially posted at |
3067 | +<http://sourceforge.net/tracker/index.php?func=detail&aid=1123383&group_id=103&atid=300103> |
3068 | +which see for possible patches and other enhancements. |
3069 | + |
3070 | +Here goes the original mmdsr.readme by Brad ... |
3071 | + |
3072 | +======================================================================== |
3073 | +This is a basic Bourne shell script that I quickly hacked together for |
3074 | +my own purposes, designed to be fired off at 23:59 every night, going |
3075 | +through a variety of Mailman log files looking for entries specific |
3076 | +to that date, summarizing the activities, and indicating problems or |
3077 | +certain types of activity that might be of interest to someone trying |
3078 | +to administer the server. |
3079 | + |
3080 | +It also does an "ls -la" of /usr/local/mailman/qfiles/*, so that you |
3081 | +can see what is in the queue at the time of the execution of the script. |
3082 | + |
3083 | +This daily report will get e-mailed to the admin, or posted to a "reports" |
3084 | +mailing list, where they can be archived and kept for future reference. |
3085 | +If you don't define an address where the output e-mail should be sent, |
3086 | +it will instead be printed to the standard output (thus allowing you to |
3087 | +do something else with it). |
3088 | + |
3089 | + |
3090 | +Once I'd gone through a few revisions of my own on this tool, I |
3091 | +thought that I would release the code to the public and get comments |
3092 | +and suggestions from others in the Mailman community. This program is |
3093 | +currently being used actively on the mail servers for python.org (where |
3094 | +the mailman-users and other Mailman-related mailing lists are hosted), |
3095 | +as well as many others. |
3096 | + |
3097 | +Note that this script needs to be configured once to know where standard |
3098 | +commands are located, where log files are kept, etc... (see the top |
3099 | +500 lines or so of the script), but after that you don't need to feed |
3100 | +it any input, or capture the output to be sent anywhere. This script |
3101 | +takes care of all of that. All you should need to do is to call this |
3102 | +script from a cron job at 23:59 (local time) every night. |
3103 | + |
3104 | + |
3105 | +When looking at this script, perhaps during configuration, please keep |
3106 | +in mind that it is heavily commented at the top, and everything should |
3107 | +hopefully be self-evident. |
3108 | |
3109 | === added file 'contrib/auto' |
3110 | --- contrib/auto 1970-01-01 00:00:00 +0000 |
3111 | +++ contrib/auto 2010-10-14 12:15:59 +0000 |
3112 | @@ -0,0 +1,116 @@ |
3113 | +# -*- python -*- |
3114 | +# |
3115 | +# Copyright (C) 2000,2001,2002 by the Free Software Foundation, Inc. |
3116 | +# |
3117 | +# This program is free software; you can redistribute it and/or |
3118 | +# modify it under the terms of the GNU General Public License |
3119 | +# as published by the Free Software Foundation; either version 2 |
3120 | +# of the License, or (at your option) any later version. |
3121 | +# |
3122 | +# This program is distributed in the hope that it will be useful, |
3123 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3124 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3125 | +# GNU General Public License for more details. |
3126 | +# |
3127 | +# You should have received a copy of the GNU General Public License |
3128 | +# along with this program; if not, write to the Free Software |
3129 | +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
3130 | + |
3131 | +"""Automatically send a message to a mailing list. |
3132 | +""" |
3133 | + |
3134 | +# To use with Postfix, set the following in your main.cf file: |
3135 | +# |
3136 | +# recipient_delimiter = + |
3137 | +# luser_relay = mm+$user@yourdomain.com |
3138 | +# owner_request_special = no |
3139 | + |
3140 | +import sys |
3141 | +import os |
3142 | +import time |
3143 | + |
3144 | +import paths |
3145 | +from Mailman import mm_cfg |
3146 | +from Mailman import Utils |
3147 | +from Mailman import MailList |
3148 | +from Mailman import Errors |
3149 | +from Mailman.Queue.sbcache import get_switchboard |
3150 | +from Mailman.Logging.Utils import LogStdErr |
3151 | + |
3152 | +# Error code if it's really not a Mailman list addr destination |
3153 | +EX_NOUSER = 67 |
3154 | + |
3155 | +LogStdErr('auto', 'auto') |
3156 | + |
3157 | +DISPOSE_MAP = {None : 'tolist', |
3158 | + 'request': 'torequest', |
3159 | + 'admin' : 'toadmin', |
3160 | + 'owner' : 'toadmin', |
3161 | + } |
3162 | + |
3163 | + |
3164 | + |
3165 | |
3166 | +def fqdn_listname(listname, hostname): |
3167 | + return ('%s@%s' % (listname, hostname)).lower() |
3168 | + |
3169 | + |
3170 | + |
3171 | |
3172 | +def main(): |
3173 | + # Postfix sets some environment variables based on information gleaned |
3174 | + # from the original message. This is the most direct way to figure out |
3175 | + # which list the message was intended for. |
3176 | + extension = os.environ.get('EXTENSION', '').lower() |
3177 | + i = extension.rfind('-') |
3178 | + if i < 0: |
3179 | + listname = extension |
3180 | + subdest = 'tolist' |
3181 | + else: |
3182 | + missing = [] |
3183 | + listname = extension[:i] |
3184 | + subdest = DISPOSE_MAP.get(extension[i+1:], missing) |
3185 | + if not Utils.list_exists(listname) or subdest is missing: |
3186 | + # must be a list that has a `-' in it's name |
3187 | + listname = extension |
3188 | + subdest = 'tolist' |
3189 | + if not listname: |
3190 | + print >> sys.stderr, 'Empty list name (someone being subversive?)' |
3191 | + return EX_NOUSER |
3192 | + try: |
3193 | + mlist = MailList.MailList(listname, lock=0) |
3194 | + except Errors.MMListError: |
3195 | + print >> sys.stderr, 'List not found:', listname |
3196 | + return EX_NOUSER |
3197 | + |
3198 | + # Make sure that the domain part of the incoming address matches the |
3199 | + # domain of the mailing list. Actually, it's possible that one or the |
3200 | + # other is more fully qualified, and thus longer. So we split the domains |
3201 | + # by dots, reverse them and make sure that whatever parts /are/ defined |
3202 | + # for both are equivalent. |
3203 | + domain = os.environ.get('DOMAIN', '').lower() |
3204 | + domainp = domain.split('.') |
3205 | + hostname = mlist.host_name.split('.') |
3206 | + domainp.reverse() |
3207 | + hostname.reverse() |
3208 | + for ca, cb in zip(domainp, hostname): |
3209 | + if ca <> cb: |
3210 | + print >> sys.stderr, 'Domain mismatch: %s@%s (expected @%s)' \ |
3211 | + % (listname, domain, mlist.host_name) |
3212 | + return EX_NOUSER |
3213 | + |
3214 | + if subdest is None: |
3215 | + print >> sys.stderr, 'Bad sub-destination:', extension |
3216 | + return EX_NOUSER |
3217 | + |
3218 | + inq = get_switchboard(mm_cfg.INQUEUE_DIR) |
3219 | + inq.enqueue(sys.stdin.read(), |
3220 | + listname=listname, |
3221 | + received_time=time.time(), |
3222 | + _plaintext=1, |
3223 | + **{subdest: 1}) |
3224 | + return 0 |
3225 | + |
3226 | + |
3227 | + |
3228 | |
3229 | +if __name__ == '__main__': |
3230 | + code = main() |
3231 | + sys.exit(code) |
3232 | |
3233 | === added file 'contrib/check_perms_grsecurity.py' |
3234 | --- contrib/check_perms_grsecurity.py 1970-01-01 00:00:00 +0000 |
3235 | +++ contrib/check_perms_grsecurity.py 2010-10-14 12:15:59 +0000 |
3236 | @@ -0,0 +1,182 @@ |
3237 | +#! @PYTHON@ |
3238 | +# |
3239 | +# Copyright (C) 1998-2007 by the Free Software Foundation, Inc. |
3240 | +# |
3241 | +# This program is free software; you can redistribute it and/or |
3242 | +# modify it under the terms of the GNU General Public License |
3243 | +# as published by the Free Software Foundation; either version 2 |
3244 | +# of the License, or (at your option) any later version. |
3245 | +# |
3246 | +# This program is distributed in the hope that it will be useful, |
3247 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3248 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3249 | +# GNU General Public License for more details. |
3250 | +# |
3251 | +# You should have received a copy of the GNU General Public License |
3252 | +# along with this program; if not, write to the Free Software |
3253 | +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, |
3254 | +# USA. |
3255 | + |
3256 | +"""Fixes for running Mailman under the `secure-linux' patch or grsecurity. |
3257 | + |
3258 | +Run check_perms -f and only then check_perms_grsecurity.py -f |
3259 | +Note that you will have to re-run this script after a mailman upgrade and |
3260 | +that check_perms will undo part of what this script does |
3261 | + |
3262 | +If you use Solar Designer's secure-linux patch, it prevents a process from |
3263 | +linking (hard link) to a file it doesn't own. |
3264 | +Grsecurity (http://grsecurity.net/) can have the same restriction depending |
3265 | +on how it was built, including other restrictions like preventing you to run |
3266 | +a program if it is located in a directory writable by a non root user. |
3267 | + |
3268 | +As a result Mailman has to be changed so that the whole tree is owned by |
3269 | +Mailman, and the CGIs and some of the programs in the bin tree (the ones |
3270 | +that lock config.pck files) are SUID Mailman. The idea is that config.pck |
3271 | +files have to be owned by the mailman UID and only touched by programs that |
3272 | +are UID mailman. |
3273 | +At the same time, We have to make sure that at least 3 directories under |
3274 | +~mailman aren't writable by mailman: mail, cgi-bin, and bin |
3275 | + |
3276 | +Binary commands that are changed to be SUID mailman are also made unreadable |
3277 | +and unrunnable by people who aren't in the mailman group. This shouldn't |
3278 | +affect much since most of those commands would fail work if you weren't part |
3279 | +of the mailman group anyway. |
3280 | +Scripts in ~mailman/bin/ are not made suid or sgid, they need to be run by |
3281 | +user mailman or root to work. |
3282 | + |
3283 | +Marc <marc_soft@merlins.org>/<marc_bts@vasoftware.com> |
3284 | +2000/10/27 - Initial version for secure_linux/openwall and mailman 2.0 |
3285 | +2001/12/09 - Updated version for grsecurity and mailman 2.1 |
3286 | +""" |
3287 | + |
3288 | +import sys |
3289 | +import os |
3290 | +import paths |
3291 | +import re |
3292 | +import glob |
3293 | +import pwd |
3294 | +import grp |
3295 | +from Mailman import mm_cfg |
3296 | +from Mailman.mm_cfg import MAILMAN_USER, MAILMAN_GROUP |
3297 | +from stat import * |
3298 | + |
3299 | +# Directories that we don't want writable by mailman. |
3300 | +dirstochownroot= ( 'mail', 'cgi-bin', 'bin' ) |
3301 | + |
3302 | +# Those are the programs that we patch so that they insist being run under the |
3303 | +# mailman uid or as root. |
3304 | +binfilestopatch= ( 'add_members', 'change_pw', 'check_db', 'clone_member', |
3305 | + 'config_list', 'newlist', 'qrunner', 'remove_members', |
3306 | + 'rmlist', 'sync_members', 'update', 'withlist' ) |
3307 | + |
3308 | +def main(argv): |
3309 | + binpath = paths.prefix + '/bin/' |
3310 | + droplib = binpath + 'CheckFixUid.py' |
3311 | + |
3312 | + if len(argv) < 2 or argv[1] != "-f": |
3313 | + print __doc__ |
3314 | + sys.exit(1) |
3315 | + |
3316 | + print "Making select directories owned and writable by root only" |
3317 | + gid = grp.getgrnam(MAILMAN_GROUP)[2] |
3318 | + for dir in dirstochownroot: |
3319 | + dirpath = paths.prefix + '/' + dir |
3320 | + os.chown(dirpath, 0, gid) |
3321 | + os.chmod(dirpath, 02755) |
3322 | + print dirpath |
3323 | + |
3324 | |
3325 | + |
3326 | + file = paths.prefix + '/data/last_mailman_version' |
3327 | + print "Making" + file + "owned by mailman (not root)" |
3328 | + uid = pwd.getpwnam(MAILMAN_USER)[2] |
3329 | + gid = grp.getgrnam(MAILMAN_GROUP)[2] |
3330 | + os.chown(file, uid, gid) |
3331 | |
3332 | + |
3333 | + if not os.path.exists(droplib): |
3334 | + print "Creating " + droplib |
3335 | + fp = open(droplib, 'w', 0644) |
3336 | + fp.write("""import sys |
3337 | +import os |
3338 | +import grp, pwd |
3339 | +from Mailman.mm_cfg import MAILMAN_USER, MAILMAN_GROUP |
3340 | + |
3341 | +class CheckFixUid: |
3342 | + uid = pwd.getpwnam(MAILMAN_USER)[2] |
3343 | + gid = grp.getgrnam(MAILMAN_GROUP)[2] |
3344 | + if os.geteuid() == 0: |
3345 | + os.setgid(gid) |
3346 | + os.setuid(uid) |
3347 | + if os.geteuid() != uid: |
3348 | + print "You need to run this script as root or mailman because it was configured to run" |
3349 | + print "on a linux system with a security patch which restricts hard links" |
3350 | + sys.exit() |
3351 | +""") |
3352 | + fp.close() |
3353 | + else: |
3354 | + print "Skipping creation of " + droplib |
3355 | + |
3356 | + |
3357 | + print "\nMaking cgis setuid mailman" |
3358 | + cgis = glob.glob(paths.prefix + '/cgi-bin/*') |
3359 | + |
3360 | + for file in cgis: |
3361 | + print file |
3362 | + os.chown(file, uid, gid) |
3363 | + os.chmod(file, 06755) |
3364 | + |
3365 | + print "\nMaking mail wrapper setuid mailman" |
3366 | + file= paths.prefix + '/mail/mailman' |
3367 | + os.chown(file, uid, gid) |
3368 | + os.chmod(file, 06755) |
3369 | + print file |
3370 | + |
3371 | + print "\nEnsuring that all config.db/pck files are owned by Mailman" |
3372 | + cdbs = glob.glob(paths.prefix + '/lists/*/config.db*') |
3373 | + cpcks = glob.glob(paths.prefix + '/lists/*/config.pck*') |
3374 | + |
3375 | + for file in cdbs + cpcks: |
3376 | + stat = os.stat(file) |
3377 | + if (stat[ST_UID] != uid or stat[ST_GID] != gid): |
3378 | + print file |
3379 | + os.chown(file, uid, gid) |
3380 | + |
3381 | + print "\nPatching mailman scripts to change the uid to mailman" |
3382 | + |
3383 | + for script in binfilestopatch: |
3384 | + filefd = open(script, "r") |
3385 | + file = filefd.readlines() |
3386 | + filefd.close() |
3387 | + |
3388 | + patched = 0 |
3389 | + try: |
3390 | + file.index("import CheckFixUid\n") |
3391 | + print "Not patching " + script + ", already patched" |
3392 | + except ValueError: |
3393 | + file.insert(file.index("import paths\n")+1, "import CheckFixUid\n") |
3394 | + for i in range(len(file)-1, 0, -1): |
3395 | + object=re.compile("^([ ]*)main\(").search(file[i]) |
3396 | + # Special hack to support patching of update |
3397 | + object2=re.compile("^([ ]*).*=[ ]*main\(").search(file[i]) |
3398 | + if object: |
3399 | + print "Patching " + script |
3400 | + file.insert(i, |
3401 | + object.group(1) + "CheckFixUid.CheckFixUid()\n") |
3402 | + patched=1 |
3403 | + break |
3404 | + if object2: |
3405 | + print "Patching " + script |
3406 | + file.insert(i, |
3407 | + object2.group(1) + "CheckFixUid.CheckFixUid()\n") |
3408 | + patched=1 |
3409 | + break |
3410 | + |
3411 | + if patched==0: |
3412 | + print "Warning, file "+script+" couldn't be patched." |
3413 | + print "If you use it, mailman may not function properly" |
3414 | + else: |
3415 | + filefd=open(script, "w") |
3416 | + filefd.writelines(file) |
3417 | + |
3418 | +main(sys.argv) |
3419 | |
3420 | === added file 'contrib/mailman.mc' |
3421 | --- contrib/mailman.mc 1970-01-01 00:00:00 +0000 |
3422 | +++ contrib/mailman.mc 2010-10-14 12:15:59 +0000 |
3423 | @@ -0,0 +1,143 @@ |
3424 | +dnl |
3425 | +dnl *** EXAMPLE *** sendmail.mc file for a Mailman list server using |
3426 | +dnl mm-handler to deal with list operations (in place of aliases). |
3427 | +dnl This is what I actually use on my site. |
3428 | +dnl |
3429 | +dnl $Id: mailman.mc 4287 2001-10-27 02:30:51Z bwarsaw $ |
3430 | +dnl |
3431 | + |
3432 | + |
3433 | +dnl |
3434 | +dnl First you need to define your general characteristics. You |
3435 | +dnl should know what these settings should be at your site -- I |
3436 | +dnl only know what they should be at mine. |
3437 | +dnl |
3438 | +OSTYPE(solaris2)dnl |
3439 | +DOMAIN(generic)dnl |
3440 | + |
3441 | +dnl |
3442 | +dnl You can keep the old alias files for back-compatibility, but it's |
3443 | +dnl probably better not to as this can become a point of confusion |
3444 | +dnl later. |
3445 | +dnl |
3446 | +define(`ALIAS_FILE', `/etc/mail/aliases,/etc/mail/lists') |
3447 | + |
3448 | +dnl |
3449 | +dnl I use procmail for local delivery, because it's smart to have a |
3450 | +dnl local delivery mailer, even if you don't (ordinarily) do any local |
3451 | +dnl delivery. The Solaris local delivery mailer is part of its sendmail |
3452 | +dnl package. I pkgrmed the sendmail packages so that system upgrades |
3453 | +dnl don't kill my sendmail.com sendmail, so mail.local is unavailable, |
3454 | +dnl so I throw procmail in here even though it never gets used. |
3455 | +dnl |
3456 | +define(`PROCMAIL_MAILER_PATH', `/opt/bin/procmail') |
3457 | +FEATURE(`local_procmail') |
3458 | +MAILER(`local') |
3459 | + |
3460 | +dnl |
3461 | +dnl Miscellaneous tuning. Not relevant to Mailman. |
3462 | +dnl |
3463 | +define(`confCONNECTION_RATE_THROTTLE', 5) |
3464 | +define(`confMAX_MESSAGE_SIZE', `5000000') |
3465 | +define(`confNO_RCPT_ACTION', `add-to-undisclosed') |
3466 | +define(`confME_TOO', `True') |
3467 | +define(`confDOUBLE_BOUNCE_ADDRESS', `mailer-daemon') |
3468 | + |
3469 | +dnl |
3470 | +dnl Privacy options. Also not relevant. |
3471 | +dnl |
3472 | +define(`confPRIVACY_FLAGS', `authwarnings,needvrfyhelo,noexpn,noreceipts,restrictmailq') |
3473 | + |
3474 | + |
3475 | +dnl |
3476 | +dnl Mm-handler works by mailertabling all addresses on your list |
3477 | +dnl server hostname(s) through the mm-handler mailer. Mailertable |
3478 | +dnl maps mail domains to mailer types. I want a mailertable to map |
3479 | +dnl listtest.uchicago.edu to the mm-handler mailer, but we need to |
3480 | +dnl specifically request this functionality in the .mc file. |
3481 | +dnl |
3482 | +FEATURE(`mailertable', `hash -o /etc/mail/mailertable') |
3483 | + |
3484 | +dnl |
3485 | +dnl This leads to an immediate and important side-effect: "local" |
3486 | +dnl addresses, and notably RFC-specified addresses such as postmaster, |
3487 | +dnl are assumed by sendmail to be lists! Since aliases are not processed |
3488 | +dnl for domaintabled domains, we must use a virtusertable to reroute |
3489 | +dnl such addresses. |
3490 | +dnl |
3491 | +FEATURE(`virtusertable', `hash -o /etc/mail/virtusertable') |
3492 | + |
3493 | +dnl |
3494 | +dnl By default, sendmail applies virtusertable mapping, if at all, for |
3495 | +dnl all interfaces for which it accepts mail -- i.e., all domains in |
3496 | +dnl $=w. Mm-handler relies on your having a single domain (hostname) |
3497 | +dnl that serves only lists, with no users. To avoid potential namespace |
3498 | +dnl conflicts, you need not to have this list domain included in $=w. |
3499 | +dnl As a result, virtuser mapping does not apply for the Mailman |
3500 | +dnl list domain. However, you can pre-empt this rule by defining |
3501 | +dnl $={VirtHost}: if there are domains in this class, they will be |
3502 | +dnl mapped before $=w is mapped. |
3503 | +dnl |
3504 | +dnl VIRTUSER_DOMAIN() defines this class. |
3505 | +dnl |
3506 | +VIRTUSER_DOMAIN(`nospam.uchicago.edu listtest.uchicago.edu listhost.uchicago.edu') |
3507 | + |
3508 | +dnl |
3509 | +dnl On a related point: by default, Sendmail probes for open IP |
3510 | +dnl interfaces, and adds their hostnames to $=w. Although Sendmail does |
3511 | +dnl virtusertable mapping for members of $=w, it doesn't do mailertable |
3512 | +dnl mapping for them, because they're considered "local". This tells |
3513 | +dnl Sendmail not to probe interfaces for local hosts, and it's critical |
3514 | +dnl if your Mailman domain is actually an IP address (with an A record, |
3515 | +dnl not just CNAME or MX) on your server. |
3516 | +dnl |
3517 | +define(`confDONT_PROBE_INTERFACES', `True') |
3518 | + |
3519 | + |
3520 | +dnl |
3521 | +dnl Even though my actual hostname is foobar, tell the world that I'm |
3522 | +dnl listtest.uchicago.edu. |
3523 | +dnl |
3524 | +FEATURE(`limited_masquerade') |
3525 | +MASQUERADE_AS(`listtest.uchicago.edu') |
3526 | + |
3527 | + |
3528 | +dnl |
3529 | +dnl Access control is a useful feature for blocking abusers and relays |
3530 | +dnl and such. |
3531 | +dnl |
3532 | +FEATURE(`access_db') |
3533 | + |
3534 | + |
3535 | +dnl |
3536 | +dnl This allows you to block access for individual recipents through |
3537 | +dnl the same access database as is used for blocking sender hosts and |
3538 | +dnl addresses. |
3539 | +dnl |
3540 | +FEATURE(`blacklist_recipients') |
3541 | + |
3542 | + |
3543 | +dnl |
3544 | +dnl Other local mailers... |
3545 | +dnl |
3546 | +MAILER(`smtp') |
3547 | +MAILER(`procmail') |
3548 | + |
3549 | + |
3550 | +dnl |
3551 | +dnl Our Mailman-specific local mailer. |
3552 | +dnl |
3553 | +MAILER_DEFINITIONS |
3554 | +#################################### |
3555 | +### New Mailer specifications ### |
3556 | +#################################### |
3557 | + |
3558 | +## Special flags! See |
3559 | +## http://www.sendmail.org/~ca/email/doc8.10/op-sh-5.html#sh-5.4 |
3560 | +## Note especially the absence of the "m" and "n" flags. THIS IS |
3561 | +## IMPORTANT: mm-handler assumes this behavior to avoid having to know |
3562 | +## too much about address parsing and other RFC-2822 mail details. |
3563 | + |
3564 | +Mmailman, P=/etc/mail/mm-handler, F=rDFMhlqSu, U=mailman:other, |
3565 | + S=EnvFromL, R=EnvToL/HdrToL, |
3566 | + A=mm-handler $h $u |
3567 | |
3568 | === added file 'contrib/majordomo2mailman.pl' |
3569 | --- contrib/majordomo2mailman.pl 1970-01-01 00:00:00 +0000 |
3570 | +++ contrib/majordomo2mailman.pl 2010-10-14 12:15:59 +0000 |
3571 | @@ -0,0 +1,691 @@ |
3572 | +#!/usr/bin/perl -w |
3573 | + |
3574 | +# majordomo2mailman.pl - Migrate Majordomo mailing lists to Mailman 2.0 |
3575 | +# Copyright (C) 2002 Heiko Rommel (rommel@suse.de) |
3576 | + |
3577 | +# BAW: Note this probably needs to be upgraded to work with MM2.1 |
3578 | + |
3579 | +# |
3580 | +# License: |
3581 | +# |
3582 | +# This program is free software; you can redistribute it and/or modify |
3583 | +# it under the terms of the GNU General Public License as published by |
3584 | +# the Free Software Foundation; either version 1, or (at your option) |
3585 | +# any later version. |
3586 | + |
3587 | +# |
3588 | +# Warranty: |
3589 | +# |
3590 | +# There's absolutely no warranty. |
3591 | +# |
3592 | + |
3593 | +# comments on possible debug messages during the conversion: |
3594 | +# |
3595 | +# "not an valid email address" : those addresses are rejected, i.e. not imported into the Mailman list |
3596 | +# "not a numeric value" : such a value will be converted to 0 (z.B. maxlength) |
3597 | +# "already subscribed" : will only once be subscribed on the Mailman list |
3598 | +# "...umbrella..." or "...taboo..." -> Mailman-Admin-Guide |
3599 | + |
3600 | +use strict; |
3601 | +use Getopt::Long; |
3602 | +use Fcntl; |
3603 | +use POSIX qw (tmpnam); |
3604 | + |
3605 | +use vars qw ( |
3606 | + $majordomo $mydomain $myurl |
3607 | + $aliasin $listdir |
3608 | + $aliasout $mailmanbin |
3609 | + $umbrella_member_suffix $private |
3610 | + $newsserver $newsprefix |
3611 | + $susehack $susearchuser |
3612 | + $help $debug $update $all $usagemsg |
3613 | + *FH |
3614 | + %mlaliases %mlowners %mlapprovers |
3615 | + %defaultmlconf %mlconf |
3616 | + %defaultmmconf %mmconf |
3617 | +); |
3618 | + |
3619 | +# |
3620 | +# adjust your site-specific settings here |
3621 | +# |
3622 | + |
3623 | +$mydomain = "my.domain"; |
3624 | +$majordomo = "majordomo"; # the master Majordomo address for your site |
3625 | +$aliasin = "/var/lib/majordomo/aliases"; |
3626 | +$listdir = "/var/lib/majordomo/lists"; |
3627 | +$aliasout = "/tmp/aliases"; |
3628 | +$myurl = "http://my.domain/mailman/"; |
3629 | +$mailmanbin = "/usr/lib/mailman/bin"; |
3630 | +$umbrella_member_suffix = "-owner"; |
3631 | +$private = "yes"; # is this a private/Intranet site ? |
3632 | +$newsserver = "news.my.domain"; |
3633 | +$newsprefix = "intern."; |
3634 | + |
3635 | +$susehack = "no"; |
3636 | +$susearchuser = "archdummy"; |
3637 | + |
3638 | +# |
3639 | +# 0) |
3640 | +# parse the command line arguments |
3641 | +# |
3642 | + |
3643 | +$usagemsg = "usage: majordomo2mailman [-h|--help] [-d|--debug] [-u|--update] < (-a|--all) | list-of-mailinglists >"; |
3644 | + |
3645 | +GetOptions( |
3646 | + "h|help" => \$help, |
3647 | + "d|debug" => \$debug, |
3648 | + "a|all" => \$all, |
3649 | + "u|update" => \$update |
3650 | +) or die "$usagemsg\n"; |
3651 | + |
3652 | +if (defined($help)) { die "$usagemsg\n"; } |
3653 | + |
3654 | +if ((not defined($all)) and (@ARGV<1)) { die "$usagemsg\n"; } |
3655 | + |
3656 | +if ($<) { die "this script must be run as root!\n"; } |
3657 | + |
3658 | +# |
3659 | +# 1) |
3660 | +# build a list of all aliases and extract the name of mailing lists plus their owners |
3661 | +# |
3662 | + |
3663 | +%mlaliases = %mlowners = %mlapprovers = (); |
3664 | + |
3665 | +open (FH, "< $aliasin") or die "can't open $aliasin\n"; |
3666 | + |
3667 | +while (<FH>) { |
3668 | + # first, build a list of all active aliases and their resolution |
3669 | + if (/^([^\#:]+)\s*:\s*(.*)$/) { |
3670 | + $mlaliases{$1} = $2; |
3671 | + } |
3672 | +} |
3673 | + |
3674 | +my $mlalias; |
3675 | +for $mlalias (keys %mlaliases) { |
3676 | + # if we encounter an alias with :include: as expansion |
3677 | + # it is save to assume that the alias has the form |
3678 | + # <mailinglist>-outgoing - |
3679 | + # that way we find the names of all active mailing lists |
3680 | + if ($mlaliases{$mlalias} =~ /\:include\:/) { |
3681 | + my $ml; |
3682 | + ($ml = $mlalias) =~ s/-outgoing//g; |
3683 | + $mlowners{$ml} = $mlaliases{"owner-$ml"}; |
3684 | + $mlapprovers{$ml} = $mlaliases{"$ml-approval"}; |
3685 | + } |
3686 | +} |
3687 | + |
3688 | +close (FH); |
3689 | + |
3690 | +# |
3691 | +# 2) |
3692 | +# for each list read the Majordomo configuration params |
3693 | +# and create a Mailman clone |
3694 | +# |
3695 | + |
3696 | +my $ml; |
3697 | +for $ml ((defined ($all)) ? sort keys %mlowners : @ARGV) { |
3698 | + |
3699 | + init_defaultmlconf($ml); |
3700 | + %mlconf = %defaultmlconf; |
3701 | + |
3702 | + init_defaultmmconf($ml); |
3703 | + %mmconf = %defaultmmconf; |
3704 | + |
3705 | + my @privileged; # addresses that are mentioned in restrict_post |
3706 | + my @members; |
3707 | + my ($primaryowner, @secondaryowner); |
3708 | + my ($primaryapprover, @secondaryapprover); |
3709 | + |
3710 | + my ($skey, $terminator); |
3711 | + my $filename; |
3712 | + my @args; |
3713 | + |
3714 | + # |
3715 | + # a) |
3716 | + # parse the configuration file |
3717 | + # |
3718 | + |
3719 | + open (FH, "< $listdir/$ml.config") or die "can't open $listdir/$ml.config\n"; |
3720 | + |
3721 | + while (<FH>) { |
3722 | + # key = value ? |
3723 | + if (/^\s*([^=\#\s]+)\s*=\s*(.*)\s*$/) { |
3724 | + $mlconf{$1} = $2; |
3725 | + } |
3726 | + # key << EOF |
3727 | + # value |
3728 | + # EOF ? |
3729 | + elsif (/^\s*([^<\#\s]+)\s*<<\s*(.*)\s*$/) { |
3730 | + ($skey, $terminator) = ($1, $2); |
3731 | + while (<FH>) { |
3732 | + last if (/^$terminator\s*$/); |
3733 | + $mlconf{$skey} .= $_; |
3734 | + } |
3735 | + chomp $mlconf{$skey}; |
3736 | + } |
3737 | + } |
3738 | + |
3739 | + close (FH); |
3740 | + |
3741 | + # |
3742 | + # b) |
3743 | + # test if there are so-called flag files (clue that this is an old-style Majordomo lists) |
3744 | + # and overwrite previously parsed values |
3745 | + # (stolen from majordomo::config_parse.pl: handle_flag_files()) |
3746 | + # |
3747 | + |
3748 | + if ( -e "$listdir/$ml.private") { |
3749 | + $mlconf{"get_access"} = "closed"; |
3750 | + $mlconf{"index_access"} = "closed"; |
3751 | + $mlconf{"who_access"} = "closed"; |
3752 | + $mlconf{"which_access"} = "closed"; |
3753 | + } |
3754 | + |
3755 | + $mlconf{"subscribe_policy"} = "closed" if ( -e "$listdir/$ml.closed"); |
3756 | + $mlconf{"unsubscribe_policy"} = "closed" if ( -e "$listdir/$ml.closed"); |
3757 | + |
3758 | + if ( -e "$listdir/$ml.auto" && -e "$listdir/$ml.closed") { |
3759 | + print STDERR "sowohl $ml.auto als auch $ml.closed existieren. Wähle $ml.closed\n"; |
3760 | + } |
3761 | + else { |
3762 | + $mlconf{"subscribe_policy"} = "auto" if ( -e"$listdir/$ml.auto"); |
3763 | + $mlconf{"unsubscribe_policy"} = "auto" if ( -e"$listdir/$ml.auto"); |
3764 | + } |
3765 | + |
3766 | + $mlconf{"strip"} = 1 if ( -e "$listdir/$ml.strip"); |
3767 | + $mlconf{"noadvertise"} = "/.*/" if ( -e "$listdir/$ml.hidden"); |
3768 | + |
3769 | + # admin_passwd: |
3770 | + $filename = "$listdir/" . $mlconf{"admin_passwd"}; |
3771 | + if ( -e "$listdir/$ml.passwd" ) { |
3772 | + $mlconf{"admin_passwd"} = read_from_file("$listdir/$ml.passwd"); |
3773 | + } |
3774 | + elsif ( -e "$filename" ) { |
3775 | + $mlconf{"admin_passwd"} = read_from_file("$filename"); |
3776 | + } |
3777 | + # else take it verbatim |
3778 | + |
3779 | + # approve_passwd: |
3780 | + $filename = "$listdir/" . $mlconf{"approve_passwd"}; |
3781 | + if ( -e "$listdir/$ml.passwd" ) { |
3782 | + $mlconf{"approve_passwd"} = read_from_file("$listdir/$ml.passwd"); |
3783 | + } |
3784 | + elsif ( -e "$filename" ) { |
3785 | + $mlconf{"approve_passwd"} = read_from_file("$filename"); |
3786 | + } |
3787 | + # else take it verbatim |
3788 | + |
3789 | + # |
3790 | + # c) |
3791 | + # add some information from additional configuration files |
3792 | + # |
3793 | + |
3794 | + # restrict_post |
3795 | + if (defined ($mlconf{"restrict_post"})) { |
3796 | + @privileged = (); |
3797 | + for $filename (split /\s+/, $mlconf{"restrict_post"}) { |
3798 | + open (FH, "< $listdir/$filename") or die "can't open $listdir/$filename\n"; |
3799 | + push (@privileged, <FH>); |
3800 | + chomp @privileged; |
3801 | + close (FH); |
3802 | + } |
3803 | + } |
3804 | + |
3805 | + if ($susehack =~ m/yes/i) { |
3806 | + @privileged = grep(!/$susearchuser\@$mydomain/i, @privileged); |
3807 | + } |
3808 | + |
3809 | + $mlconf{"privileged"} = \@privileged; |
3810 | + |
3811 | + # members |
3812 | + @members = (); |
3813 | + open (FH, "< $listdir/$ml") or die "can't open $listdir/$ml\n"; |
3814 | + push (@members, <FH>); |
3815 | + chomp @members; |
3816 | + close (FH); |
3817 | + |
3818 | + $mlconf{"gated"} = "no"; |
3819 | + |
3820 | + if ($susehack =~ m/yes/i) { |
3821 | + if (grep(/$susearchuser\@$mydomain/i, @members)) { |
3822 | + $mlconf{"gated"} = "yes"; |
3823 | + } |
3824 | + @members = grep(!/$susearchuser\@$mydomain/i, @members); |
3825 | + } |
3826 | + |
3827 | + $mlconf{"members"} = \@members; |
3828 | + |
3829 | + # intro message |
3830 | + if (open (FH, "< $listdir/$ml.intro")) { |
3831 | + { local $/; $mlconf{"intro"} = <FH>; } |
3832 | + } |
3833 | + else { $mlconf{"intro"} = ""; } |
3834 | + |
3835 | + # info message |
3836 | + if (open (FH, "< $listdir/$ml.info")) { |
3837 | + { local $/; $mlconf{"info"} = <FH>; } |
3838 | + } |
3839 | + else { $mlconf{"info"} = ""; } |
3840 | + |
3841 | + # |
3842 | + # d) |
3843 | + # take over some other params into the configuration table |
3844 | + # |
3845 | + |
3846 | + $mlconf{"name"} = "$ml"; |
3847 | + |
3848 | + ($primaryowner, @secondaryowner) = |
3849 | + expand_alias (split (/\s*,\s*/, aliassub($mlowners{$ml}))); |
3850 | + |
3851 | + ($primaryapprover, @secondaryapprover) = |
3852 | + expand_alias (split (/\s*,\s*/, aliassub($mlapprovers{$ml}))); |
3853 | + |
3854 | + $mlconf{"primaryowner"} = $primaryowner; |
3855 | + $mlconf{"secondaryowner"} = \@secondaryowner; |
3856 | + |
3857 | + $mlconf{"primaryapprover"} = $primaryapprover; |
3858 | + $mlconf{"secondaryapprover"} = \@secondaryapprover; |
3859 | + |
3860 | + # |
3861 | + # debugging output |
3862 | + # |
3863 | + |
3864 | + if (defined ($debug)) { |
3865 | + print "##################### $ml ####################\n"; |
3866 | + for $skey (sort keys %mlconf) { |
3867 | + if (defined ($mlconf{$skey})) { print "$skey = $mlconf{$skey}\n"; } |
3868 | + else { print "$skey = (?)\n"; } |
3869 | + } |
3870 | + my $priv; |
3871 | + for $priv (@privileged) { |
3872 | + print "\t$ml: $priv\n"; |
3873 | + } |
3874 | + } |
3875 | + |
3876 | + # |
3877 | + # e) |
3878 | + # with the help of Mailman commands - create a new list and subscribe the old staff |
3879 | + # |
3880 | + |
3881 | + if (defined($update)) { |
3882 | + print "updating configuration of \"$ml\"\n"; |
3883 | + } |
3884 | + else { |
3885 | + # Mailman lists can initially be only created with one owner |
3886 | + @args = ("$mailmanbin/newlist", "-q", "-o", "$aliasout", "$ml", $mlconf{"primaryowner"}, $mlconf{"admin_passwd"}); |
3887 | + system (@args) == 0 or die "system @args failed: $?"; |
3888 | + } |
3889 | + |
3890 | + # Mailman accepts only subscriber lists > 0 |
3891 | + if (@members > 0) { |
3892 | + $filename = tmpnam(); |
3893 | + open (FH, "> $filename") or die "can't open $filename\n"; |
3894 | + for $skey (@members) { |
3895 | + print FH "$skey" . "\n"; |
3896 | + } |
3897 | + close (FH); |
3898 | + @args = ("$mailmanbin/add_members", "-n", "$filename", "--welcome-msg=n", "$ml"); |
3899 | + system (@args) == 0 or die "system @args failed: $?"; |
3900 | + } |
3901 | + |
3902 | + # |
3903 | + # f) |
3904 | + # "translate" the Majordomo list configuration |
3905 | + # |
3906 | + |
3907 | + m2m(); |
3908 | + |
3909 | + # write the Mailman config |
3910 | + |
3911 | + $filename = tmpnam(); |
3912 | + |
3913 | + open (FH, "> $filename") or die "can't open $filename\n"; |
3914 | + for $skey (sort keys %mmconf) { |
3915 | + print FH "$skey = " . $mmconf{$skey} . "\n"; |
3916 | + } |
3917 | + close (FH); |
3918 | + |
3919 | + @args = ("$mailmanbin/config_list", "-i", "$filename", "$ml"); |
3920 | + system (@args) == 0 or die "system @args failed: $?"; |
3921 | + |
3922 | + unlink($filename) or print STDERR "unable to unlink \"$filename\"!\n"; |
3923 | + |
3924 | +} |
3925 | + |
3926 | +exit 0; |
3927 | + |
3928 | +############# |
3929 | +# subs |
3930 | +############# |
3931 | + |
3932 | +# |
3933 | +# I don't know how to write Perl code |
3934 | +# therefor I need this stupid procedure to cleanly read a value from file |
3935 | +# |
3936 | + |
3937 | +sub read_from_file { |
3938 | + my $value; |
3939 | + local *FH; |
3940 | + |
3941 | + open (FH, "< $_[0]") or die "can't open $_[0]\n"; |
3942 | + $value = <FH>; |
3943 | + chomp $value; |
3944 | + close (FH); |
3945 | + |
3946 | + return $value; |
3947 | +} |
3948 | + |
3949 | + |
3950 | +# |
3951 | +# add "@$mydomain" to each element that does not contain a "@" |
3952 | +# |
3953 | + |
3954 | +sub expand_alias { |
3955 | + return map { (not $_ =~ /@/) ? $_ .= "\@$mydomain" : $_ } @_; |
3956 | +} |
3957 | + |
3958 | +# |
3959 | +# replace the typical owner-majordomo aliases |
3960 | +# |
3961 | + |
3962 | +sub aliassub { |
3963 | + my $string = $_[0]; |
3964 | + |
3965 | + $string =~ s/(owner-$majordomo|$majordomo-owner)/mailman-owner/gi; |
3966 | + |
3967 | + return $string; |
3968 | +} |
3969 | + |
3970 | +# |
3971 | +# default values of Majordomo mailing lists |
3972 | +# (stolen from majordomo::config_parse.pl: %known_keys) |
3973 | +# |
3974 | + |
3975 | +sub init_defaultmlconf { |
3976 | + my $ml = $_[0]; |
3977 | + |
3978 | + %defaultmlconf=( |
3979 | + 'welcome', "yes", |
3980 | + 'announcements', "yes", |
3981 | + 'get_access', "open", |
3982 | + 'index_access', "open", |
3983 | + 'who_access', "open", |
3984 | + 'which_access', "open", |
3985 | + 'info_access', "open", |
3986 | + 'intro_access', "open", |
3987 | + 'advertise', "", |
3988 | + 'noadvertise', "", |
3989 | + 'description', "", |
3990 | + 'subscribe_policy', "open", |
3991 | + 'unsubscribe_policy', "open", |
3992 | + 'mungedomain', "no", |
3993 | + 'admin_passwd', "$ml.admin", |
3994 | + 'strip', "yes", |
3995 | + 'date_info', "yes", |
3996 | + 'date_intro', "yes", |
3997 | + 'archive_dir', "", |
3998 | + 'moderate', "no", |
3999 | + 'moderator', "", |
4000 | + 'approve_passwd', "$ml.pass", |
4001 | + 'sender', "owner-$ml", |
4002 | + 'maxlength', "40000", |
4003 | + 'precedence', "bulk", |
4004 | + 'reply_to', "", |
4005 | + 'restrict_post', "", |
4006 | + 'purge_received', "no", |
4007 | + 'administrivia', "yes", |
4008 | + 'resend_host', "", |
4009 | + 'debug', "no", |
4010 | + 'message_fronter', "", |
4011 | + 'message_footer', "", |
4012 | + 'message_headers', "", |
4013 | + 'subject_prefix', "", |
4014 | + 'taboo_headers', "", |
4015 | + 'taboo_body', "", |
4016 | + 'digest_volume', "1", |
4017 | + 'digest_issue', "1", |
4018 | + 'digest_work_dir', "", |
4019 | + 'digest_name', "$ml", |
4020 | + 'digest_archive', "", |
4021 | + 'digest_rm_footer', "", |
4022 | + 'digest_rm_fronter', "", |
4023 | + 'digest_maxlines', "", |
4024 | + 'digest_maxdays', "", |
4025 | + 'comments', "" |
4026 | + ); |
4027 | +} |
4028 | + |
4029 | + |
4030 | +# |
4031 | +# Mailman mailing list params that are not derived from Majordomo mailing lists params |
4032 | +# (e.g. bounce_matching_headers+forbbiden_posters vs. taboo_headers+taboo_body) |
4033 | +# If you need one of this params to be variable remove it here and add some code to the |
4034 | +# main procedure; additionally, you should compare it with what you have in |
4035 | +# /usr/lib/mailman/Mailman/mm_cfg.py |
4036 | +# |
4037 | + |
4038 | +sub init_defaultmmconf { |
4039 | + |
4040 | + %defaultmmconf=( |
4041 | + 'goodbye_msg', "\'\'", |
4042 | + 'umbrella_list', "0", |
4043 | + 'umbrella_member_suffix', "\'$umbrella_member_suffix\'", |
4044 | + 'send_reminders', "0", |
4045 | + 'admin_immed_notify', "1", |
4046 | + 'admin_notify_mchanges', "0", |
4047 | + 'dont_respond_to_post_requests', "0", |
4048 | + 'obscure_addresses', "1", |
4049 | + 'require_explicit_destination', "1", |
4050 | + 'acceptable_aliases', "\"\"\"\n\"\"\"\n", |
4051 | + 'max_num_recipients', "10", |
4052 | + 'forbidden_posters', "[]", |
4053 | + 'bounce_matching_headers', "\"\"\"\n\"\"\"\n", |
4054 | + 'anonymous_list', "0", |
4055 | + 'nondigestable', "1", |
4056 | + 'digestable', "1", |
4057 | + 'digest_is_default', "0", |
4058 | + 'mime_is_default_digest', "0", |
4059 | + 'digest_size_threshhold', "40", |
4060 | + 'digest_send_periodic', "1", |
4061 | + 'digest_header', "\'\'", |
4062 | + 'bounce_processing', "1", |
4063 | + 'minimum_removal_date', "4", |
4064 | + 'minimum_post_count_before_bounce_action', "3", |
4065 | + 'max_posts_between_bounces', "5", |
4066 | + 'automatic_bounce_action', "3", |
4067 | + 'archive_private', "0", |
4068 | + 'clobber_date', "1", |
4069 | + 'archive_volume_frequency', "1", |
4070 | + 'autorespond_postings', "0", |
4071 | + 'autoresponse_postings_text', "\'\'", |
4072 | + 'autorespond_admin', "0", |
4073 | + 'autoresponse_admin_text', "\'\'", |
4074 | + 'autorespond_requests', "0", |
4075 | + 'autoresponse_request_text', "\'\'", |
4076 | + 'autoresponse_graceperiod', "90" |
4077 | + ); |
4078 | +} |
4079 | + |
4080 | +# |
4081 | +# convert a Majordomo mailing list configuration (%mlconf) into a |
4082 | +# Mailman mailing list configuration (%mmconf) |
4083 | +# only those params are affected which can be derived from Majordomo |
4084 | +# mailing list configurations |
4085 | +# |
4086 | + |
4087 | +sub m2m { |
4088 | + |
4089 | + my $elem; |
4090 | + my $admin; |
4091 | + |
4092 | + $mmconf{"real_name"} = "\'" . $mlconf{"name"} . "\'"; |
4093 | + |
4094 | + # Mailman does not know the difference between owner and approver |
4095 | + for $admin (($mlconf{"primaryowner"}, @{$mlconf{"secondaryowner"}}, |
4096 | + $mlconf{"primaryapprover"}, @{$mlconf{"secondarapprover"}})) { |
4097 | + # merging owners and approvers may result in a loop: |
4098 | + if (lc($admin) ne lc("owner-" . $mlconf{"name"} . "\@" . $mydomain)) { |
4099 | + $mmconf{"owner"} .= ",\'" . "$admin" . "\'"; |
4100 | + } |
4101 | + } |
4102 | + $mmconf{"owner"} =~ s/^,//g; |
4103 | + $mmconf{"owner"} = "\[" . $mmconf{"owner"} . "\]"; |
4104 | + |
4105 | + # remove characters that will break Python |
4106 | + ($mmconf{"description"} = $mlconf{"description"}) =~ s/\'/\\\'/g; |
4107 | + $mmconf{"description"} = "\'" . $mmconf{"description"} . "\'"; |
4108 | + |
4109 | + $mmconf{"info"} = "\"\"\"\n" . $mlconf{"info"} . "\"\"\"\n"; |
4110 | + |
4111 | + $mmconf{"subject_prefix"} = "\'" . $mlconf{"subject_prefix"} . "\'"; |
4112 | + |
4113 | + $mmconf{"welcome_msg"} = "\"\"\"\n" . $mlconf{"intro"} . "\"\"\"\n"; |
4114 | + |
4115 | + # I don't know how to handle this because the reply_to param in the lists |
4116 | + # I had were not configured consistently |
4117 | + if ($mlconf{"reply_to"} =~ /\S+/) { |
4118 | + if ($mlconf{"name"} . "\@" =~ m/$mlconf{"reply_to"}/i) { |
4119 | + $mmconf{"reply_goes_to_list"} = "1"; |
4120 | + $mmconf{"reply_to_address"} = "\'\'"; |
4121 | + } |
4122 | + else { |
4123 | + $mmconf{"reply_goes_to_list"} = "2"; |
4124 | + $mmconf{"reply_to_address"} = "\'" . $mlconf{"reply_to"} . "\'"; |
4125 | + } |
4126 | + } |
4127 | + else { |
4128 | + $mmconf{"reply_goes_to_list"} = "0"; |
4129 | + $mmconf{"reply_to_address"} = "\'\'"; |
4130 | + } |
4131 | + |
4132 | + $mmconf{"administrivia"} = ($mlconf{"administrivia"} =~ m/yes/i) ? "1" : "0"; |
4133 | + $mmconf{"send_welcome_msg"} = ($mlconf{"welcome"} =~ m/yes/i) ? "1" : "0"; |
4134 | + |
4135 | + $mmconf{"max_message_size"} = int ($mlconf{"maxlength"} / 1000); |
4136 | + |
4137 | + $mmconf{"host_name"} = ($mlconf{"resend_host"} =~ /\S+/) ? |
4138 | + $mlconf{"resend_host"} : "\'" . $mydomain . "\'"; |
4139 | + |
4140 | + $mmconf{"web_page_url"} = "\'" . $myurl . "\'"; |
4141 | + |
4142 | + # problematic since Mailman does not know access patterns |
4143 | + # I assume, that if there was given a noadvertise pattern, the |
4144 | + # list shouldn't be visible at all |
4145 | + $mmconf{"advertised"} = ($mlconf{"noadvertise"} =~ /\.\*/) ? "0" : "1"; |
4146 | + |
4147 | + # confirm+approval is much to long winded for private sites |
4148 | + $mmconf{"subscribe_policy"} = |
4149 | + ($mlconf{"subscribe_policy"} =~ m/(open|auto)/i) ? "1" : |
4150 | + ($private =~ m/yes/i) ? "2" : "3"; |
4151 | + |
4152 | + # in case this is a private site allow list visiblity at most |
4153 | + $mmconf{"private_roster"} = |
4154 | + ($mlconf{"who_access"} =~ m/open/i and not $private =~ m/yes/i) ? "0" : |
4155 | + ($mlconf{"who_access"} =~ m/open|list/i) ? "1" : "2"; |
4156 | + |
4157 | + $mmconf{"moderated"} = ($mlconf{"moderate"} =~ m/yes/i) ? "1" : "0"; |
4158 | + # there is no way to a set a separate moderator in Mailman |
4159 | + |
4160 | + # external, since lengthy |
4161 | + mm_posters(); |
4162 | + |
4163 | + if ($mlconf{"message_fronter"} =~ /\S+/) { |
4164 | + $mmconf{"msg_header"} = "\"\"\"\n" . $mlconf{"message_fronter"} . "\"\"\"\n"; |
4165 | + } |
4166 | + else { |
4167 | + $mmconf{"msg_header"} = "\'\'"; |
4168 | + } |
4169 | + |
4170 | + if ($mlconf{"message_footer"} =~ /\S+/) { |
4171 | + $mmconf{"msg_footer"} = "\"\"\"\n" . $mlconf{"message_footer"} . "\"\"\"\n"; |
4172 | + } |
4173 | + else { |
4174 | + $mmconf{"msg_footer"} = "\'\'"; |
4175 | + } |
4176 | + |
4177 | + # gateway to news |
4178 | + $mmconf{"nntp_host"} = "\'" . $newsserver . "\'"; |
4179 | + $mmconf{"linked_newsgroup"} = "\'" . $newsprefix . $mlconf{"name"} . "\'"; |
4180 | + |
4181 | + if ($mlconf{"gated"} =~ m/yes/i) { |
4182 | + $mmconf{"gateway_to_news"} = "1"; |
4183 | + $mmconf{"gateway_to_mail"} = "1"; |
4184 | + $mmconf{"archive"} = "1"; |
4185 | + } |
4186 | + else { |
4187 | + $mmconf{"gateway_to_news"} = "0"; |
4188 | + $mmconf{"gateway_to_mail"} = "0"; |
4189 | + $mmconf{"archive"} = "0"; |
4190 | + } |
4191 | + |
4192 | + # print warnings if this seems to be an umbrella list |
4193 | + for $elem (@{$mlconf{"privileged"}}, @{$mlconf{"members"}}) { |
4194 | + $elem =~ s/\@$mydomain//gi; |
4195 | + if (defined($mlaliases{$elem . $umbrella_member_suffix})) { |
4196 | + print STDERR "\"" . $mlconf{"name"} . |
4197 | + "\" possibly forms part off/is an umbrella list, since \"$elem\" is a local mailing list alias\n"; |
4198 | + } |
4199 | + } |
4200 | + |
4201 | + # print warnings if we encountered a Taboo-Header or Taboo-Body |
4202 | + if ($mlconf{"taboo_headers"} =~ /\S+/ or $mlconf{"taboo_body"} =~ /\S+/) { |
4203 | + print STDERR "\"" . $mlconf{"name"} . "\" taboo_headers or taboo_body seem to be set - please check manually.\n"; |
4204 | + } |
4205 | +} |
4206 | + |
4207 | +# |
4208 | +# with some set theory on the member and priviliged list try to determine the params |
4209 | +# $mmconf{"member_posting_only"} and $mmconf{"posters"} |
4210 | +# |
4211 | + |
4212 | +sub mm_posters { |
4213 | + if ($mlconf{"restrict_post"} =~ /\S+/) { |
4214 | + my %privileged = (); |
4215 | + my %members = (); |
4216 | + my $key; |
4217 | + |
4218 | + foreach $key (@{$mlconf{"privileged"}}) { $privileged{$key} = "OK"; } |
4219 | + foreach $key (@{$mlconf{"members"}}) { $members{$key} = "OK"; } |
4220 | + |
4221 | + # are all members privileged, too ? |
4222 | + my $included = 1; |
4223 | + foreach $key (keys %members) { |
4224 | + if (not exists $privileged{$key}) { |
4225 | + $included = 0; |
4226 | + last; |
4227 | + } |
4228 | + } |
4229 | + if ($included) { |
4230 | + $mmconf{"member_posting_only"} = "1"; |
4231 | + |
4232 | + # posters = privileged - members: |
4233 | + my %diff = %privileged; |
4234 | + foreach $key (keys %members) { |
4235 | + delete $diff{$key} if exists $members{$key}; |
4236 | + } |
4237 | + |
4238 | + $mmconf{"posters"} = ""; |
4239 | + for $key (sort keys %diff) { |
4240 | + $mmconf{"posters"} .= ",\'" . $key . "\'"; |
4241 | + } |
4242 | + $mmconf{"posters"} =~ s/^,//g; |
4243 | + $mmconf{"posters"} = "[" . $mmconf{"posters"} . "]"; |
4244 | + } |
4245 | + else { |
4246 | + $mmconf{"member_posting_only"} = "0"; |
4247 | + |
4248 | + # posters = privileged: |
4249 | + $mmconf{"posters"} = ""; |
4250 | + for $key (sort keys %privileged) { |
4251 | + $mmconf{"posters"} .= ",\'" . $key . "\'"; |
4252 | + } |
4253 | + $mmconf{"posters"} =~ s/^,//g; |
4254 | + $mmconf{"posters"} = "[" . $mmconf{"posters"} . "]"; |
4255 | + } |
4256 | + } |
4257 | + else { |
4258 | + $mmconf{"member_posting_only"} = "0"; |
4259 | + $mmconf{"posters"} = "[]"; |
4260 | + } |
4261 | +} |
4262 | + |
4263 | |
4264 | === added file 'contrib/mm-handler' |
4265 | --- contrib/mm-handler 1970-01-01 00:00:00 +0000 |
4266 | +++ contrib/mm-handler 2010-10-14 12:15:59 +0000 |
4267 | @@ -0,0 +1,236 @@ |
4268 | +#!/usr/local/bin/perl |
4269 | +## |
4270 | +## Sendmail mailer for Mailman |
4271 | +## |
4272 | +## Simulates these aliases: |
4273 | +## |
4274 | +##testlist: "|/home/mailman/mail/mailman post testlist" |
4275 | +##testlist-admin: "|/home/mailman/mail/mailman admin testlist" |
4276 | +##testlist-bounces: "|/home/mailman/mail/mailman bounces testlist" |
4277 | +##testlist-confirm: "|/home/mailman/mail/mailman confirm testlist" |
4278 | +##testlist-join: "|/home/mailman/mail/mailman join testlist" |
4279 | +##testlist-leave: "|/home/mailman/mail/mailman leave testlist" |
4280 | +##testlist-owner: "|/home/mailman/mail/mailman owner testlist" |
4281 | +##testlist-request: "|/home/mailman/mail/mailman request testlist" |
4282 | +##testlist-subscribe: "|/home/mailman/mail/mailman subscribe testlist" |
4283 | +##testlist-unsubscribe: "|/home/mailman/mail/mailman unsubscribe testlist" |
4284 | +##owner-testlist: testlist-owner |
4285 | + |
4286 | +## Some assembly required. |
4287 | +$MMWRAPPER = "/home/mailman/mail/mailman"; |
4288 | +$MMLISTDIR = "/home/mailman/lists"; |
4289 | +$SENDMAIL = "/usr/lib/sendmail -oem -oi"; |
4290 | +$VERSION = '$Id: mm-handler 5100 2002-04-05 19:41:09Z bwarsaw $'; |
4291 | + |
4292 | +## Comment this if you offer local user addresses. |
4293 | +$NOUSERS = "\nPersonal e-mail addresses are not offered by this server."; |
4294 | + |
4295 | +# uncomment for debugging.... |
4296 | +#$DEBUG = 1; |
4297 | + |
4298 | +use FileHandle; |
4299 | +use Sys::Hostname; |
4300 | +use Socket; |
4301 | + |
4302 | +($VERS_STR = $VERSION) =~ s/^\$\S+\s+(\S+),v\s+(\S+\s+\S+\s+\S+).*/\1 \2/; |
4303 | + |
4304 | +$BOUNDARY = sprintf("%08x-%d", time, time % $$); |
4305 | + |
4306 | +## Informative, non-standard rejection letter |
4307 | +sub mail_error { |
4308 | + my ($in, $to, $list, $server, $reason) = @_; |
4309 | + my $sendmail; |
4310 | + |
4311 | + if ($server && $server ne "") { |
4312 | + $servname = $server; |
4313 | + } else { |
4314 | + $servname = "This server"; |
4315 | + $server = &get_ip_addr; |
4316 | + } |
4317 | + |
4318 | + #$sendmail = new FileHandle ">/tmp/mm-$$"; |
4319 | + $sendmail = new FileHandle "|$SENDMAIL $to"; |
4320 | + if (!defined($sendmail)) { |
4321 | + print STDERR "$0: cannot exec \"$SENDMAIL\"\n"; |
4322 | + exit (-1); |
4323 | + } |
4324 | + |
4325 | + $sendmail->print ("From: MAILER-DAEMON\@$server |
4326 | +To: $to |
4327 | +Subject: Returned mail: List unknown |
4328 | +Mime-Version: 1.0 |
4329 | +Content-type: multipart/mixed; boundary=\"$BOUNDARY\" |
4330 | +Content-Disposition: inline |
4331 | + |
4332 | +--$BOUNDARY |
4333 | +Content-Type: text/plain; charset=us-ascii |
4334 | +Content-Description: Error processing your mail |
4335 | +Content-Disposition: inline |
4336 | + |
4337 | +Your mail for $list could not be sent: |
4338 | + $reason |
4339 | + |
4340 | +For a list of publicly-advertised mailing lists hosted on this server, |
4341 | +visit this URL: |
4342 | + http://$server/ |
4343 | + |
4344 | +If this does not resolve your problem, you may write to: |
4345 | + postmaster\@$server |
4346 | +or |
4347 | + mailman-owner\@$server |
4348 | + |
4349 | + |
4350 | +$servname delivers e-mail to registered mailing lists |
4351 | +and to the administrative addresses defined and required by IETF |
4352 | +Request for Comments (RFC) 2142 [1]. |
4353 | +$NOUSERS |
4354 | + |
4355 | +The Internet Engineering Task Force [2] (IETF) oversees the development |
4356 | +of open standards for the Internet community, including the protocols |
4357 | +and formats employed by Internet mail systems. |
4358 | + |
4359 | +For your convenience, your original mail is attached. |
4360 | + |
4361 | + |
4362 | +[1] Crocker, D. \"Mailbox Names for Common Services, Roles and |
4363 | + Functions\". http://www.ietf.org/rfc/rfc2142.txt |
4364 | + |
4365 | +[2] http://www.ietf.org/ |
4366 | + |
4367 | +--$BOUNDARY |
4368 | +Content-Type: message/rfc822 |
4369 | +Content-Description: Your undelivered mail |
4370 | +Content-Disposition: attachment |
4371 | + |
4372 | +"); |
4373 | + |
4374 | + while ($_ = <$in>) { |
4375 | + $sendmail->print ($_); |
4376 | + } |
4377 | + |
4378 | + $sendmail->print ("\n"); |
4379 | + $sendmail->print ("--$BOUNDARY--\n"); |
4380 | + |
4381 | + close($sendmail); |
4382 | +} |
4383 | + |
4384 | +## Get my IP address, in case my sendmail doesn't tell me my name. |
4385 | +sub get_ip_addr { |
4386 | + my $host = hostname; |
4387 | + my $ip = gethostbyname($host); |
4388 | + return inet_ntoa($ip); |
4389 | +} |
4390 | + |
4391 | +## Split an address into its base list name and the appropriate command |
4392 | +## for the relevant function. |
4393 | +sub split_addr { |
4394 | + my ($addr) = @_; |
4395 | + my ($list, $cmd); |
4396 | + my @validfields = qw(admin bounces confirm join leave owner request |
4397 | + subscribe unsubscribe); |
4398 | + |
4399 | + if ($addr =~ /(.*)-(.*)\+.*$/) { |
4400 | + $list = $1; |
4401 | + $cmd = "$2"; |
4402 | + } else { |
4403 | + $addr =~ /(.*)-(.*)$/; |
4404 | + $list = $1; |
4405 | + $cmd = $2; |
4406 | + } |
4407 | + if (grep /^$cmd$/, @validfields) { |
4408 | + if ($list eq "owner") { |
4409 | + $list = $cmd; |
4410 | + $cmd = "owner"; |
4411 | + } |
4412 | + } else { |
4413 | + $list = $addr; |
4414 | + $cmd = "post"; |
4415 | + } |
4416 | + |
4417 | + return ($list, $cmd); |
4418 | +} |
4419 | + |
4420 | +## The time, formatted as for an mbox's "From_" line. |
4421 | +sub mboxdate { |
4422 | + my ($time) = @_; |
4423 | + my @days = qw(Sun Mon Tue Wed Thu Fri Sat); |
4424 | + my @months = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec); |
4425 | + my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = |
4426 | + localtime($time); |
4427 | + |
4428 | + ## Two-digit year handling complies with RFC 2822 (section 4.3), |
4429 | + ## with the addition that three-digit years are accommodated. |
4430 | + if ($year < 50) { |
4431 | + $year += 2000; |
4432 | + } elsif ($year < 1900) { |
4433 | + $year += 1900; |
4434 | + } |
4435 | + |
4436 | + return sprintf ("%s %s %2d %02d:%02d:%02d %d", |
4437 | + $days[$wday], $months[$mon], $mday, |
4438 | + $hour, $min, $sec, $year); |
4439 | +} |
4440 | + |
4441 | +BEGIN: { |
4442 | + $sender = undef; |
4443 | + $server = undef; |
4444 | + @to = (); |
4445 | + while ($#ARGV >= 0) { |
4446 | + if ($ARGV[0] eq "-r") { |
4447 | + $sender = $ARGV[1]; |
4448 | + shift @ARGV; |
4449 | + } elsif (!defined($server)) { |
4450 | + $server = $ARGV[0]; |
4451 | + } else { |
4452 | + push(@to, $ARGV[0]); |
4453 | + } |
4454 | + shift @ARGV; |
4455 | + } |
4456 | + |
4457 | + if ($DEBUG) { |
4458 | + $to = join(',', @to); |
4459 | + print STDERR "to: $to\n"; |
4460 | + print STDERR "sender: $sender\n"; |
4461 | + print STDERR "server: $server\n"; |
4462 | + exit(-1); |
4463 | + } |
4464 | + |
4465 | +ADDR: for $addr (@to) { |
4466 | + $prev = undef; |
4467 | + $list = $addr; |
4468 | + |
4469 | + $cmd= "post"; |
4470 | + if (! -f "$MMLISTDIR/$list/config.pck") { |
4471 | + ($list, $cmd) = &split_addr($list); |
4472 | + if (! -f "$MMLISTDIR/$list/config.pck") { |
4473 | + $was_to = $addr; |
4474 | + $was_to .= "\@$server" if ("$server" ne ""); |
4475 | + mail_error(\*STDIN, $sender, $was_to, $server, |
4476 | + "no list named \"$list\" is known by $server"); |
4477 | + next ADDR; |
4478 | + } |
4479 | + } |
4480 | + |
4481 | + $wrapper = new FileHandle "|$MMWRAPPER $cmd $list"; |
4482 | + if (!defined($wrapper)) { |
4483 | + ## Defer? |
4484 | + print STDERR "$0: cannot exec ", |
4485 | + "\"$MMWRAPPER $cmd $list\": deferring\n"; |
4486 | + exit (-1); |
4487 | + } |
4488 | + |
4489 | + # Don't need these without the "n" flag on the mailer def.... |
4490 | + #$date = &mboxdate(time); |
4491 | + #$wrapper->print ("From $sender $date\n"); |
4492 | + |
4493 | + # ...because we use these instead. |
4494 | + $from_ = <STDIN>; |
4495 | + $wrapper->print ($from_); |
4496 | + |
4497 | + $wrapper->print ("X-Mailman-Handler: $VERSION\n"); |
4498 | + while (<STDIN>) { |
4499 | + $wrapper->print ($_); |
4500 | + } |
4501 | + close($wrapper); |
4502 | + } |
4503 | +} |
4504 | |
4505 | === added file 'contrib/mmdsr' |
4506 | --- contrib/mmdsr 1970-01-01 00:00:00 +0000 |
4507 | +++ contrib/mmdsr 2010-10-14 12:15:59 +0000 |
4508 | @@ -0,0 +1,572 @@ |
4509 | +#!/bin/sh |
4510 | +############################################################################### |
4511 | +# mmdsr -- Mailman Daily Status Report (cron job, run at 23:59) |
4512 | +############################################################################### |
4513 | +# Copyright (c) 2005, Brad Knowles |
4514 | +# All rights reserved. |
4515 | +# |
4516 | +# Redistribution and use in source and binary forms, with or without |
4517 | +# modification, are permitted provided that the following conditions |
4518 | +# are met: |
4519 | +# |
4520 | +# Redistributions of source code must retain the above copyright |
4521 | +# notice, this list of conditions and the following disclaimer. |
4522 | +# |
4523 | +# Redistributions in binary form must reproduce the above copyright |
4524 | +# notice, this list of conditions and the following disclaimer |
4525 | +# in the documentation and/or other materials provided with the |
4526 | +# distribution. |
4527 | +# |
4528 | +# The name of the author or other contributors may not be used |
4529 | +# to endorse or promote products derived from this software without |
4530 | +# specific prior written permission. |
4531 | +# |
4532 | +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
4533 | +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
4534 | +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
4535 | +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
4536 | +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
4537 | +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
4538 | +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
4539 | +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
4540 | +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
4541 | +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
4542 | +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
4543 | +# POSSIBILITY OF SUCH DAMAGE. |
4544 | +############################################################################### |
4545 | + |
4546 | +############################################################################### |
4547 | +# Version history |
4548 | +############################################################################### |
4549 | +# 0.0.1 Initial creation by Brad Knowles <brad@stop.mail-abuse.org> |
4550 | +# Created on: Tue Feb 15 04:01:20 PST 2005 |
4551 | +# |
4552 | +# 0.0.2 Update by Brad Knowles <brad@stop.mail-abuse.org> |
4553 | +# Updated on: Wed Feb 16 18:55:52 CET 2005 |
4554 | +# Feedback from Tom G. Christensen (tgc99): |
4555 | +# > The current UID grab command doesn't work on Solaris |
4556 | +# > (2.6 & 8 tested). |
4557 | +# > |
4558 | +# > I'd recommend this instead: |
4559 | +# > ps -o user -p $$|tail -1 |
4560 | +# > |
4561 | +# > This is tested and works on RH 6.2, RH 7.3, RHEL 2.1, |
4562 | +# > RHEL3, FC3, FreeBSD 4.9, Solaris 2.6, 8. |
4563 | +# |
4564 | +# 0.0.3 Update by Brad Knowles <brad@stop.mail-abuse.org> |
4565 | +# Updated on: Sun Mar 13 01:15:24 CET 2005 |
4566 | +# Noted errors from grep when trying to search a nonexistant |
4567 | +# file. Added "-s" option to compensate, and extra code to |
4568 | +# notify the admin if the log file doesn't exist. |
4569 | +# |
4570 | +# 0.0.4 Update by Brad Knowles <brad@stop.mail-abuse.org> |
4571 | +# Updated on: Sun Mar 13 01:58:37 CET 2005 |
4572 | +# Eliminate some extra cruft from the "fromusenet" logs. |
4573 | +# |
4574 | +# 0.0.5 Update by Brad Knowles <brad@stop.mail-abuse.org> |
4575 | +# Updated on: Wed Apr 13 00:54:42 CEST 2005 |
4576 | +# Eliminate a lot of extra cruft from the "error" & "vette" logs. |
4577 | +# Eliminate more cruft from the "fromusenet" logs. |
4578 | +# |
4579 | +# 0.0.6 Update by Brad Knowles <brad@stop.mail-abuse.org> |
4580 | +# Updated on: Thu Apr 14 03:07:08 CEST 2005 |
4581 | +# Eliminate even more cruft from the "fromusenet" logs. |
4582 | +# Eliminate more cruft from the "vette" logs. |
4583 | +# Eliminate cruft from the "mischief" logs. |
4584 | +# |
4585 | +# 0.0.7 Update by Brad Knowles <brad@stop.mail-abuse.org> |
4586 | +# Updated on: Sun May 1 22:29:13 CEST 2005 [guess] |
4587 | +# The "error" log "no such list" errors didn't need to be |
4588 | +# quite so "compressed", open them up to be more readable. |
4589 | +# Also eliminate all use of "xargs", use "fmt -w 75" instead. |
4590 | +# |
4591 | +# 0.0.8 Update by Brad Knowles <brad@stop.mail-abuse.org> |
4592 | +# Updated on: Sun May 1 23:09:13 CEST 2005 |
4593 | +# Clean up code for queue subdirectories to check. |
4594 | +# |
4595 | +# 0.0.9 Update by Brad Knowles <brad@stop.mail-abuse.org> |
4596 | +# Updated on: Wed May 11 01:23:40 CEST 2005 |
4597 | +# Eliminate date/time stamps from the rest of the logs, |
4598 | +# then pipe through `sort | uniq -c | sort -nr` |
4599 | +# so that all possible duplicates are eliminated. |
4600 | +# |
4601 | +# 0.0.10 Update by Brad Knowles <brad@stop.mail-abuse.org> |
4602 | +# Updated on: Tue Jul 12 16:53:49 CDT 2005 |
4603 | +# Add code to do summary of normal list activity |
4604 | +# |
4605 | +# 0.0.11 Update by Brad Knowles <brad@stop.mail-abuse.org> |
4606 | +# Updated on: Tue Sep 6 15:44:19 CEST 2005 |
4607 | +# Tweaked display of Mailman/qfiles subdirectories |
4608 | +# so as to avoid displaying more than $MAX_LINES |
4609 | +# output, which really helps if you have thousands |
4610 | +# of bounces sitting around, etc.... |
4611 | +# Also slightly tweaked display of fromusenet logs |
4612 | +# to split summary from errors. |
4613 | +# |
4614 | +# 0.0.12 Update by Brad Knowles <brad@stop.mail-abuse.org> |
4615 | +# Updated on: Thu Sep 22 22:37:35 CEST 2005 |
4616 | +# Bugs found by Mark Sapiro and Adrian Wells. |
4617 | +# Mark suggested splitting the Mailman log directory |
4618 | +# from the rest of the Mailman source and queues, |
4619 | +# as well as making sure to clean up all temp files. |
4620 | +# Adrian discovered that there was a log file format |
4621 | +# change between Mailman 2.1.5 and 2.1.6, which broke |
4622 | +# hourly statistics. |
4623 | +# |
4624 | +# 0.0.13 Update by Brad Knowles <brad@stop.mail-abuse.org> |
4625 | +# Updated on: Mon Dec 26 05:54:27 CET 2005 |
4626 | +# Bugs found by Tom G. Christensen (tgc99): |
4627 | +# > ps output on solaris is full of whitespace but a further |
4628 | +# > echo get's rid of it. |
4629 | +# > The lines in the smtp log are sometimes broken up by a |
4630 | +# > newline (right before the msgid) which throws of the |
4631 | +# > summary. Piping it through sed first will rejoin the broken |
4632 | +# > lines. |
4633 | +# > Use $AWK instead of awk. |
4634 | +# Enhancements from Mark Sapiro: |
4635 | +# > The vette log summary lists posts held for moderation |
4636 | +# > individually under "Other Errors:". The following patch |
4637 | +# > (watch out for wrapped lines) summarizes them by list instead. |
4638 | +# |
4639 | +# 0.0.14 Update by Brad Knowles <brad@stop.mail-abuse.org> |
4640 | +# Updated on: Thu Dec 29 08:17:38 CET 2005 |
4641 | +# Added code to check /usr/local/mailman/data for |
4642 | +# moderation hold queue |
4643 | +# |
4644 | +# 0.0.15 Update by Brad Knowles <brad@stop.mail-abuse.org> |
4645 | +# Updated on: Thu Jan 26 02:39:38 CET 2006 |
4646 | +# Tweaked display of summary data in the "smtp-failure" log, |
4647 | +# and "other" category in the "vette" log, so as to reduce |
4648 | +# the spewage when things go wonky, mostly by removing |
4649 | +# unique-ifying data like message-id or sender address. |
4650 | +# Also tweaked moderation queue information, to tell us |
4651 | +# how many Python pickle files are in the data directory, |
4652 | +# as opposed to just doing a directory listing and skipping |
4653 | +# the files in the middle if there are too many -- now we |
4654 | +# know how many are being skipped. |
4655 | +# |
4656 | +# 0.0.16 Update by Brad Knowles <brad@stop.mail-abuse.org> |
4657 | +# Updated on: Sun Jan 29 11:45:58 CET 2006 |
4658 | +# Mark found a couple of typos that I somehow let slip |
4659 | +# through. Thanks! |
4660 | + |
4661 | +############################################################################### |
4662 | +# Set up locations of standard commands, directories, etc.... |
4663 | +# You may need to modify these for your platform or installation. |
4664 | +############################################################################### |
4665 | + |
4666 | +AWK="/usr/bin/awk" |
4667 | +BASENAME="/usr/bin/basename" |
4668 | +CAT="/bin/cat" |
4669 | +DATE="/bin/date" |
4670 | +EGREP="/bin/egrep" |
4671 | +FMT="/usr/bin/fmt" |
4672 | +GREP="/bin/grep" |
4673 | +HEAD="/usr/bin/head" |
4674 | +LS="/bin/ls" |
4675 | +PS="/bin/ps" |
4676 | +RM="/bin/rm" |
4677 | +SED="/bin/sed" |
4678 | +SENDMAIL="/usr/sbin/sendmail" |
4679 | +SLEEP="/bin/sleep" |
4680 | +SORT="/usr/bin/sort" |
4681 | +TAIL="/usr/bin/tail" |
4682 | +TOUCH="/bin/touch" |
4683 | +TR="/usr/bin/tr" |
4684 | +UNIQ="/usr/bin/uniq" |
4685 | +XARGS="/usr/bin/xargs" |
4686 | +WC="/usr/bin/wc" |
4687 | + |
4688 | +############################################################################### |
4689 | +# Directory for temporary files |
4690 | +############################################################################### |
4691 | +TMPDIR="/tmp" |
4692 | + |
4693 | +############################################################################### |
4694 | +# Mailman queue directory |
4695 | +############################################################################### |
4696 | +QUEUEDIR="/usr/local/mailman/qfiles" |
4697 | + |
4698 | +############################################################################### |
4699 | +# Mailman log directory |
4700 | +############################################################################### |
4701 | +LOGDIR="/usr/local/mailman/logs" |
4702 | + |
4703 | +############################################################################### |
4704 | +# Mailman data directory |
4705 | +############################################################################### |
4706 | +DATADIR="/usr/local/mailman/data" |
4707 | + |
4708 | +############################################################################### |
4709 | +# Maximum number of subdirectory entries to display in report |
4710 | +############################################################################### |
4711 | +MAX_QUEUE_LINES=20 |
4712 | + |
4713 | +############################################################################### |
4714 | +# Maximum number of moderation queue pickle files to display in report |
4715 | +############################################################################### |
4716 | +MAX_DATA_LINES=100 |
4717 | + |
4718 | +############################################################################### |
4719 | +# Mailman Log files to check for errors. |
4720 | +# No need to specify path, only log file name. |
4721 | +############################################################################### |
4722 | +ERR_LOGS="error fromusenet locks mischief post qrunner smtp-failure vette" |
4723 | + |
4724 | +############################################################################### |
4725 | +# Mailman Log files to summarize. |
4726 | +# No need to specify path, only log file name. |
4727 | +############################################################################### |
4728 | +SUM_LOGS="fromusenet post smtp" |
4729 | + |
4730 | +############################################################################### |
4731 | +# Mailman queue subdirectories to check. No need to specify path, only name. |
4732 | +############################################################################### |
4733 | +SUBDIRS="archive bounces commands in news out retry shunt virgin" |
4734 | + |
4735 | +############################################################################### |
4736 | +# Specify recipients for report. If none, then simply print to "STDOUT". |
4737 | +# Specify sender address for report. Not used if recipient list is empty. |
4738 | +############################################################################### |
4739 | + |
4740 | +SENDER="INSERT.YOUR.SENDER.ADDRESS@HERE" |
4741 | +RCPTS="INSERT.YOUR.RECIPIENTS.ADDRESSES@HERE" |
4742 | + |
4743 | +############################################################################### |
4744 | +# If you run this program in cron at 23:59:00, you need to sleep sixty |
4745 | +# seconds to make sure that you capture all the logs for the previous day. |
4746 | +# Comment this line out to disable sleeping altogether. |
4747 | +############################################################################### |
4748 | + |
4749 | +SLEEPTIME=60 |
4750 | + |
4751 | +############################################################################### |
4752 | +# What user id is Mailman installed under? |
4753 | +# How do we determine what UID this program is running as? |
4754 | +# NB: There will probably be SysV vs. BSD semantic differences here. |
4755 | +# Be careful. Make sure the command you specify actually works. |
4756 | +# The command string specified here may seem arcane, but should pull |
4757 | +# out the information we need, and throw away everything else. If |
4758 | +# there is an easier cross-platform way to do it, please let me know. |
4759 | +############################################################################### |
4760 | + |
4761 | +GRABUID=`$PS -o user -p $$ | $TAIL -1` |
4762 | +MYUID=`echo $GRABUID` |
4763 | +RUNAS="mailman" |
4764 | + |
4765 | +############################################################################### |
4766 | +# No modifications below this line should be required. |
4767 | +############################################################################### |
4768 | + |
4769 | +DAY=`$DATE '+%b %d'` |
4770 | +YEAR=`$DATE '+%Y'` |
4771 | + |
4772 | +PROG=`$BASENAME $0` |
4773 | + |
4774 | +if [ "${MYUID}" != "${RUNAS}" -a "${MYUID}" != "root" ] ; then |
4775 | + echo "ERROR: You must be root or ${RUNAS} to run $PROG." |
4776 | + exit 1 |
4777 | +fi |
4778 | + |
4779 | +if [ "${SLEEPTIME}" != "" -a "${RCPTS}x" != "${SENDER}x" ] ; then |
4780 | + if [ $SLEEPTIME -gt 0 ] ; then |
4781 | + $SLEEP $SLEEPTIME |
4782 | + fi |
4783 | +fi |
4784 | + |
4785 | +TMP="$TMPDIR/.$PROG.$$" |
4786 | +TMPLOG="$TMP.log" |
4787 | +$RM -f $TMP |
4788 | +$TOUCH $TMP |
4789 | + |
4790 | +if [ "${RCPTS}x" != "x" ] ; then |
4791 | + echo "From: ${SENDER}" >> $TMP |
4792 | + echo "To: ${RCPTS}" >> $TMP |
4793 | + echo "Subject: Mailman Daily Status Report: $DAY $YEAR" >> $TMP |
4794 | + echo "" >> $TMP |
4795 | +fi |
4796 | + |
4797 | +echo " Mailman Daily Status Report: $DAY $YEAR" >> $TMP |
4798 | +echo "" >> $TMP |
4799 | +echo "" >> $TMP |
4800 | + |
4801 | +echo "******************************" >> $TMP |
4802 | +echo "Log File Summary" >> $TMP |
4803 | +echo "******************************" >> $TMP |
4804 | +echo "" >> $TMP |
4805 | + |
4806 | +for LOG in $SUM_LOGS |
4807 | +do |
4808 | + |
4809 | + $RM -f $TMPLOG |
4810 | + $TOUCH $TMPLOG |
4811 | + echo "Log file: $LOG" >> $TMP |
4812 | + echo "==============================" >> $TMP |
4813 | + |
4814 | + if [ -f "$LOGDIR/${LOG}" ] ; then |
4815 | + $SED -e :a -e '$!N;s/\n //;ta' -e 'P;D' $LOGDIR/$LOG | $GREP -si "^$DAY [0-9][0-9:]* $YEAR" >> $TMPLOG |
4816 | + |
4817 | + if [ "${LOG}" = "post" ] ; then |
4818 | + |
4819 | + echo "" >> $TMP |
4820 | + echo "Hourly Summary of Posts" >> $TMP |
4821 | + echo "-----------------------" >> $TMP |
4822 | + |
4823 | + $SED -e 's/^[A-Z][a-z][a-z] *[0-9]* //' -e 's/:.*$//' $TMPLOG | $UNIQ -c | $SORT -n +1 | $AWK '{ printf( "%8d %02d:00-%02d:59\n", $1, $2, $2 ) }' >> $TMP |
4824 | + |
4825 | + echo "" >> $TMP |
4826 | + echo "Post Count by List" >> $TMP |
4827 | + echo "------------------" >> $TMP |
4828 | + |
4829 | + $SED -e 's/^.* post to //' -e 's/ .*$//' $TMPLOG | $SORT | $UNIQ -c | $SORT -nr >> $TMP |
4830 | + |
4831 | + echo "" >> $TMP |
4832 | + echo "Post Count by Sender" >> $TMP |
4833 | + echo "--------------------" >> $TMP |
4834 | + |
4835 | + $SED -e 's/^.* from //' -e 's/,.*$//' $TMPLOG | $SORT | $UNIQ -c | $SORT -nr >> $TMP |
4836 | + |
4837 | + elif [ "${LOG}" = "fromusenet" ] ; then |
4838 | + |
4839 | + GRPS=`$EGREP -vi '(watermark:|nothing new| posted to |: \[[0-9\.]*\])' $TMPLOG | $GREP -i ' gating ' | $AWK '{ print $7 }' | $SORT -u | $FMT -w 75` |
4840 | + |
4841 | + for GRP in $GRPS |
4842 | + do |
4843 | + echo "" >> $TMP |
4844 | + echo "$GRP Article #'s Gated:" >> $TMP |
4845 | + echo "------------------------------" >> $TMP |
4846 | + $EGREP -vi '(watermark:|nothing new| posted to |: \[[0-9\.]*\])' $TMPLOG | $GREP -i " gating " | $GREP -i " $GRP " | $SED -e 's/^.*\[//' -e 's/\]$//' -e 's/\.\./ /' | $TR ' ' '\n' | $SORT -u | $FMT -w 75 >> $TMP |
4847 | + done |
4848 | + |
4849 | + elif [ "${LOG}" = "smtp" ] ; then |
4850 | + |
4851 | + echo "" >> $TMP |
4852 | + echo "Hourly Summary of Messages Sent" >> $TMP |
4853 | + echo "-------------------------------" >> $TMP |
4854 | + $SED -e 's/^[A-Z][a-z][a-z] *[0-9]* //' -e 's/:.* for / /' -e 's/ recips,.*$//' $TMPLOG | $AWK '{ val=int($1); sum[val]+=$2 } END { for (i=0; i<24; i++) { printf "%8d %02d:00-%02d:59\n", sum[i], i, i } }' >> $TMP |
4855 | + |
4856 | + else |
4857 | + |
4858 | + $SED 's/^.* ([0-9]*) //' $TMPLOG | $SORT | $UNIQ -c | $SORT -nr >> $TMP |
4859 | + |
4860 | + fi |
4861 | + |
4862 | + else |
4863 | + |
4864 | + echo " ### Log file ${LOG} does not exist ### " >> $TMP |
4865 | + |
4866 | + fi |
4867 | + |
4868 | + echo "" >> $TMP |
4869 | + |
4870 | +done |
4871 | + |
4872 | +echo "******************************" >> $TMP |
4873 | +echo "Log File Squawks" >> $TMP |
4874 | +echo "******************************" >> $TMP |
4875 | +echo "" >> $TMP |
4876 | + |
4877 | +for LOG in $ERR_LOGS |
4878 | +do |
4879 | + $RM -f $TMPLOG |
4880 | + $TOUCH $TMPLOG |
4881 | + echo "Log file: $LOG" >> $TMP |
4882 | + echo "==============================" >> $TMP |
4883 | + $GREP -si "^$DAY [0-9][0-9:]* $YEAR" $LOGDIR/$LOG >> $TMPLOG |
4884 | + |
4885 | + if [ -f "$LOGDIR/${LOG}" ] ; then |
4886 | + |
4887 | + if [ "${LOG}" = "error" ] ; then |
4888 | + |
4889 | + echo "Uncaught Runner Exceptions:" >> $TMP |
4890 | + echo "------------------------------" >> $TMP |
4891 | + $GREP 'Uncaught runner exception' $TMPLOG | $SED 's/^.*exception: //' | $SORT | $UNIQ -c | $SORT -nr >> $TMP |
4892 | + |
4893 | + echo "" >> $TMP |
4894 | + echo "No Such List:" >> $TMP |
4895 | + echo "------------------------------" >> $TMP |
4896 | + $GREP 'No such list' $TMPLOG | $SED -e 's/^.* "//' -e 's/".*$//' | $SORT | $UNIQ -c | $SORT -nr >> $TMP |
4897 | + |
4898 | + CNT=`$GREP -i 'shunting' $TMPLOG | $WC -l` |
4899 | + if [ "${CNT}x" != "x" ] ; then |
4900 | + if [ ${CNT} -gt 0 ] ; then |
4901 | + echo "" >> $TMP |
4902 | + echo "Shunting Count: $CNT" >> $TMP |
4903 | + echo "------------------------------" >> $TMP |
4904 | + fi |
4905 | + fi |
4906 | + |
4907 | + echo "" >> $TMP |
4908 | + echo "Other Errors:" >> $TMP |
4909 | + echo "------------------------------" >> $TMP |
4910 | + $EGREP -vi '(Uncaught runner exception|No such list|Ignoring unparseable message|Traceback|shunting)' $TMPLOG | $SED 's/^.* ([0-9]*) //' | $SORT | $UNIQ -c | $SORT -nr >> $TMP |
4911 | + |
4912 | + elif [ "${LOG}" = "fromusenet" ] ; then |
4913 | + |
4914 | + $EGREP -vi '(watermark:|nothing new| posted to |: \[[0-9\.]*\])' $TMPLOG | $GREP -vi " gating " | $SED 's/^.* ([0-9]*) //' | $SORT | $UNIQ -c | $SORT -nr >> $TMP |
4915 | + |
4916 | + elif [ "${LOG}" = "mischief" ] ; then |
4917 | + |
4918 | + echo "" >> $TMP |
4919 | + echo "Login failure with private rosters (by user):" >> $TMP |
4920 | + echo "------------------------------" >> $TMP |
4921 | + $GREP -i 'Login failure with private rosters' $TMPLOG | $AWK '{ print $NF }' | $SORT | $UNIQ -c | $SORT -nr >> $TMP |
4922 | + |
4923 | + echo "" >> $TMP |
4924 | + echo "Unsub attempt of non-member (by user):" >> $TMP |
4925 | + echo "------------------------------" >> $TMP |
4926 | + $GREP -i 'Unsub attempt of non-member' $TMPLOG | $AWK '{ print $NF }' | $SORT | $UNIQ -c | $SORT -nr >> $TMP |
4927 | + |
4928 | + echo "" >> $TMP |
4929 | + echo "Reminder attempt of non-member (by user):" >> $TMP |
4930 | + echo "------------------------------" >> $TMP |
4931 | + $GREP -i 'Reminder attempt of non-member' $TMPLOG | $AWK '{ print $NF }' | $SORT | $UNIQ -c | $SORT -nr >> $TMP |
4932 | + |
4933 | + echo "" >> $TMP |
4934 | + echo "Other Messages:" >> $TMP |
4935 | + echo "------------------------------" >> $TMP |
4936 | + $EGREP -vi '(Login failure with private rosters|Unsub attempt of non-member|Reminder attempt of non-member)' $TMPLOG | $SED 's/^.* ([0-9]*) //' | $SORT | $UNIQ -c | $SORT -nr >> $TMP |
4937 | + |
4938 | + elif [ "${LOG}" = "post" ] ; then |
4939 | + |
4940 | + $GREP -vi 'success' $TMPLOG | $SED 's/^.* ([0-9]*) //' | $SORT | $UNIQ -c | $SORT -nr >> $TMP |
4941 | + |
4942 | + elif [ "${LOG}" = "vette" ] ; then |
4943 | + |
4944 | + echo "" >> $TMP |
4945 | + echo "Message held -- Post by non-member (by list):" >> $TMP |
4946 | + echo "------------------------------" >> $TMP |
4947 | + $GREP 'Post by non-member' $TMPLOG | $AWK '{ print $6 }' | $SORT | $UNIQ -c | $SORT -nr >> $TMP |
4948 | + |
4949 | + echo "" >> $TMP |
4950 | + echo "Message held -- Suspicious header (by list):" >> $TMP |
4951 | + echo "------------------------------" >> $TMP |
4952 | + $GREP -i 'suspicious header' $TMPLOG | $AWK '{ print $6 }' | $SORT | $UNIQ -c | $SORT -nr >> $TMP |
4953 | + |
4954 | + echo "" >> $TMP |
4955 | + echo "Discarded posting (by list):" >> $TMP |
4956 | + echo "------------------------------" >> $TMP |
4957 | + $GREP -i 'Discarded posting' $TMPLOG | $AWK '{ print $6 }' | $SED 's/:$//' | $SORT | $UNIQ -c | $SORT -nr >> $TMP |
4958 | + |
4959 | + echo "" >> $TMP |
4960 | + echo "Bulk/Junk message discarded (by list):" >> $TMP |
4961 | + echo "------------------------------" >> $TMP |
4962 | + $EGREP -i '(bulk|junk) message discarded' $TMPLOG | $AWK '{ print $NF }' | $SORT | $UNIQ -c | $SORT -nr >> $TMP |
4963 | + |
4964 | + echo "" >> $TMP |
4965 | + echo "Implicit destination (by list):" >> $TMP |
4966 | + echo "------------------------------" >> $TMP |
4967 | + $GREP -i 'Message has implicit destination' $TMPLOG | $AWK '{ print $6 }' | $SORT | $UNIQ -c | $SORT -nr >> $TMP |
4968 | + |
4969 | + echo "" >> $TMP |
4970 | + echo "Post to moderated newsgroup (by list):" >> $TMP |
4971 | + echo "------------------------------" >> $TMP |
4972 | + $GREP -i 'Posting to a moderated newsgroup' $TMPLOG | $AWK '{ print $6 }' | $SORT | $UNIQ -c | $SORT -nr >> $TMP |
4973 | + |
4974 | + echo "" >> $TMP |
4975 | + echo "Post to moderated list (by list):" >> $TMP |
4976 | + echo "------------------------------" >> $TMP |
4977 | + $GREP -i 'Post to moderated list' $TMPLOG | $AWK '{ print $6 }' | $SORT | $UNIQ -c | $SORT -nr >> $TMP |
4978 | + |
4979 | + echo "" >> $TMP |
4980 | + echo "Other Errors:" >> $TMP |
4981 | + echo "------------------------------" >> $TMP |
4982 | + $EGREP -vi '(Post by non-member|suspicious header|message approved|Discarded posting|bulk message discarded|junk message discarded|Message has implicit destination|Posting to a moderated newsgroup|Post to moderated list|Message discarded, msgid)' $TMPLOG | $SED -e 's/^.* ([0-9]*) //' -e 's/, message-id=<[^> ]*>:/:/' | $SORT | $UNIQ -c | $SORT -nr >> $TMP |
4983 | + |
4984 | + elif [ "${LOG}" = "smtp-failure" ] ; then |
4985 | + |
4986 | + $SED 's/^.* ([0-9]*) //' $TMPLOG | $SED 's/delivery to [^@ ]*@[^@ ]* failed with code/delivery failed with code/g' | $SORT | $UNIQ -c | $SORT -nr >> $TMP |
4987 | + |
4988 | + else |
4989 | + |
4990 | + $SED 's/^.* ([0-9]*) //' $TMPLOG | $SORT | $UNIQ -c | $SORT -nr >> $TMP |
4991 | + |
4992 | + fi |
4993 | + else |
4994 | + |
4995 | + echo " ### Log file ${LOG} does not exist ### " >> $TMP |
4996 | + |
4997 | + fi |
4998 | + echo "" >> $TMP |
4999 | + |
5000 | +done |
Yeah, there's something really wrong with this branch - it's trying to merge way too much. It was probably branched off of the wrong thing, or hasn't be updated in a long time. Since this branch is pretty old (my fault I'm sure) it may not be possible for William to reconcile it with current lp:mailman trunk. I'll see if I can pick out the relevant bits.