diff options
-rw-r--r-- | Makefile | 17 | ||||
-rw-r--r-- | changelog.txt | 11 | ||||
-rw-r--r-- | listadmin.1 (renamed from listadmin.man) | 11 | ||||
-rwxr-xr-x | listadmin.pl | 401 | ||||
-rw-r--r-- | listadmin.txt | 213 |
5 files changed, 511 insertions, 142 deletions
@@ -2,14 +2,14 @@ SHELL = /bin/sh # a BSD or GNU style install is required, e.g., /usr/ucb/install on Solaris INSTALL = install -VERSION = 2.40 +VERSION = 2.42 PREFIX = /usr/local prefix = $(PREFIX) bindir = $(prefix)/bin mandir = $(prefix)/share/man -SRCFILES = Makefile listadmin.pl listadmin.man +SRCFILES = Makefile listadmin.pl listadmin.1 changelog.txt all: @echo Nothing needs to be done @@ -17,9 +17,9 @@ all: install: $(INSTALL) -d $(DESTDIR)$(bindir) $(DESTDIR)$(mandir)/man1 $(INSTALL) -m 755 listadmin.pl $(DESTDIR)$(bindir)/listadmin - $(INSTALL) -m 644 listadmin.man $(DESTDIR)$(mandir)/man1/listadmin.1 + $(INSTALL) -m 644 listadmin.1 $(DESTDIR)$(mandir)/man1/listadmin.1 -listadmin.txt: listadmin.man +listadmin.txt: listadmin.1 # Note the verbatim backspace in the sed command env TERM=dumb nroff -man $< | sed -e '/^XXX/d' -e 's/.//g' | uniq > $@.tmp mv $@.tmp $@ @@ -38,10 +38,5 @@ distclean: rm -rf $(TARFILE) listadmin.txt listadmin-$(VERSION) # for my use only -WWW_DOCS = /hom/kjetilho/www_docs/hacks -publish: dist - cp -p listadmin.txt $(WWW_DOCS)/listadmin.txt - cp -p $(TARFILE) $(WWW_DOCS)/ - cp -p listadmin.pl $(WWW_DOCS)/listadmin - cp -p listadmin.man $(WWW_DOCS)/listadmin.man - perl -pi -e 's/listadmin(.)\d+\.\d+/listadmin$${1}'$(VERSION)'/g' $(WWW_DOCS)/index.html +upload: + rsync -avh --progress $(TARFILE) solbu@frs.sourceforge.net:/home/frs/project/listadmin/$(VERSION)/
\ No newline at end of file diff --git a/changelog.txt b/changelog.txt new file mode 100644 index 0000000..538c473 --- /dev/null +++ b/changelog.txt @@ -0,0 +1,11 @@ +2.42 +- Fix Parsing of undecoded UTF-8 error +- Add IPv6 support (Debian bug #699768) +- Use proper filename for manpage (listadmin.man > listadmin.1) + +2.41 +Applied Debian pathces +- Add support for discarding subscription requests +- Add translations +- Add option to disable certificate checking (Debian bug #707787) +- Add option to configure which CA certificate file to use diff --git a/listadmin.man b/listadmin.1 index 2355454..9c8672b 100644 --- a/listadmin.man +++ b/listadmin.1 @@ -1,4 +1,4 @@ -.TH LISTADMIN 1 "24 Feb 2005" +.TH LISTADMIN 1 "2016-12-21" .\" turn off hyphenation .hy 0 .\" turn on ragged right if run through nroff @@ -173,6 +173,13 @@ This option is enabled by default for lists in uio.no, and is needed to avoid clearing the list of meta members when manipulating the list of ordinary members. \fINote: Requires additional Perl module WWW::Mechanize\fP +.IP "cafile \fI/path/to/CAcertificate\fP" +Specify which CA certificate to use for all lists following. Setting +cafile to NONE will use the default cafile. +.IP "verify_peer \fIyes|no\fP" +If set to no SSL certificate verification will be disabled for all lists +following. + \" "dumpdir" is for developer use, so it isn't documented. @@ -305,4 +312,6 @@ double width characters (e.g. Chinese). This is probably a bug in Text::Reform. .SH AUTHOR Kjetil T. Homme <kjetilho+listadmin@ifi.uio.no> + +Project manager: Johnny A. Solbu <johnny@solbu.net> .br diff --git a/listadmin.pl b/listadmin.pl index b85a471..36514c4 100755 --- a/listadmin.pl +++ b/listadmin.pl @@ -4,13 +4,15 @@ # Written 2003 - 2007 by # Kjetil Torgrim Homme <kjetilho+listadmin@ifi.uio.no> # +# 2016: Johnny A. Solbu <johnny@solbu.net> +# # Thank you, Sam Watkins and Bernie Hoeneisen, for contributions and # feedback. # # Released into public domain. -my $version = "2.40"; -my $maintainer = "kjetilho+listadmin\@ifi.uio.no"; +my $version = "2.42"; +my $maintainer = "johnny\@solbu.net"; use HTML::TokeParser; use LWP::UserAgent; @@ -25,6 +27,8 @@ use I18N::Langinfo qw(langinfo CODESET); # appeared in Perl 5.7.2 use Encode; # appeared in perl 5.7.1 use strict; use English; +use IO::Socket::SSL; +use Net::INET6Glue::INET_is_INET6; my $rc = $ENV{"HOME"}."/.listadmin.ini"; @@ -93,6 +97,9 @@ my $time_limit = time + 60 * ($opt_t || 24*60); my $term; my $term_encoding = langinfo(CODESET()); +my $default_ssl_cafile = $ua->ssl_opts("SSL_ca_file"); +my $default_ssl_verify = IO::Socket::SSL::SSL_VERIFY_PEER; # This is the default for clients + # the C and POSIX locale in Solaris uses the charset "646", but Perl # doesn't support it. $term_encoding = "ascii" if $term_encoding eq "646"; @@ -131,10 +138,18 @@ my $list = $lists[0]; my $subscribe_result; if (@opt_add_member) { + $ua->ssl_opts("SSL_ca_file" => $config->{$list}->{"cafile"}); + $ua->ssl_opts("verify_hostname" => $config->{$list}->{"verify_hostname"}); + $ua->ssl_opts("SSL_verify_mode" => $config->{$list}->{"verify_peer"}); + $subscribe_result = add_subscribers($list, $config->{$list}, $opt_mail, @opt_add_member); } if (@opt_remove_member) { + $ua->ssl_opts("SSL_ca_file" => $config->{$list}->{"cafile"}); + $ua->ssl_opts("verify_hostname" => $config->{$list}->{"verify_hostname"}); + $ua->ssl_opts("SSL_verify_mode" => $config->{$list}->{"verify_peer"}); + $subscribe_result = remove_subscribers($list, $config->{$list}, @opt_remove_member); } @@ -150,6 +165,10 @@ if (defined $subscribe_result) { } } if (defined $opt_l) { + $ua->ssl_opts("SSL_ca_file" => $config->{$list}->{"cafile"}); + $ua->ssl_opts("verify_hostname" => $config->{$list}->{"verify_hostname"}); + $ua->ssl_opts("SSL_verify_mode" => $config->{$list}->{"verify_peer"}); + my @subscribers = list_subscribers($list, $config->{$list}); print join("\n", @subscribers, ""); exit(@subscribers == 0); @@ -163,6 +182,10 @@ for (@lists) { my $user = $config->{$list}{"user"}; my $pw = $config->{$list}{"password"} || ""; + $ua->ssl_opts("SSL_ca_file" => $config->{$list}->{"cafile"}); + $ua->ssl_opts("verify_hostname" => $config->{$list}->{"verify_hostname"}); + $ua->ssl_opts("SSL_verify_mode" => $config->{$list}->{"verify_peer"}); + if (time > $time_limit) { print "Time's up, skipping the remaining lists\n"; last; @@ -254,7 +277,7 @@ sub process_subscriptions { } my $count = keys (%subscribers); my $def = $config->{"subdefault"}; - my $prompt = 'Accept/Reject/Skip/Quit'; + my $prompt = 'Accept/Discard/Reject/Skip/Quit'; $prompt .= " [" . uc($def) . "]" if $def; $prompt .= " ? "; @@ -281,6 +304,9 @@ sub process_subscriptions { } elsif ($ans eq "a") { $change->{$id} = [ "sa" ]; last; + } elsif ($ans eq "d") { + $change->{$id} = [ "sd" ]; + last; } elsif ($ans eq "r") { my $r = prompt ("Why do you reject? [optional] "); unless (defined $r) { @@ -295,6 +321,7 @@ and pressing Return. a Accept -- allow the user to join the mailing list r Reject -- notify sender that the request was turned down + d Discard -- silently discard the request s Skip -- do not decide now, leave it for later q Quit -- go on to approving messages @@ -647,7 +674,7 @@ sub get_list { unless ($resp->is_success) { return {'servererror' => $resp->status_line, 'url' => $url}; } - $page = $resp->content; + $page = $resp->decoded_content ; my $dumpdir = $config->{"dumpdir"}; my $dumpfile; @@ -709,7 +736,7 @@ sub get_list { unless ($resp->is_success) { return {'servererror' => $resp->status_line, 'url' => $url}; } - $page_appr = $resp->content; + $page_appr = $resp->decoded_content ; if (defined $dumpdir && open (DUMP, ">$dumpdir/dump-details-$list.html")) { print DUMP $page_appr; @@ -718,7 +745,7 @@ sub get_list { } my $data; - if ($mmver eq "2.1") { + if ($mmver ge "2.1") { my $parse_appr = HTML::TokeParser->new(\$page_appr) || die; $data = parse_pages_mm_2_1($mmver, $config, $parse, $parse_appr); } else { @@ -764,14 +791,26 @@ sub parse_pages_mm_2_1 { my %data = (); my $headline; + # some (newer?) servers show only 1 <hr> tag when there is no subscriptions + # Try resolve first seen <hr> as subscription, and fall back to approvals $parse_subs->get_tag ("hr"); if ($parse_subs->get_tag ("h2")) { - parse_subscriptions ($mmver, $config, $parse_subs, \%data); - } + my $title = $parse_subs->get_trimmed_text ("/h2") || die; + if ($title =~ get_trans_re("subscriptions")) { + parse_subscriptions ($mmver, $config, $parse_subs, \%data); - $parse_appr->get_tag ("hr"); - if ($parse_appr->get_tag ("h2")) { - parse_approvals ($mmver, $config, $parse_appr, \%data); + $parse_appr->get_tag ("hr"); + if ($parse_appr->get_tag ("h2")) { + parse_approvals ($mmver, $config, $parse_appr, \%data); + } + } else { + parse_approvals ($mmver, $config, $parse_appr, \%data); + } + } else { + $parse_appr->get_tag ("hr"); + if ($parse_appr->get_tag ("h2")) { + parse_approvals ($mmver, $config, $parse_appr, \%data); + } } return (\%data); } @@ -829,42 +868,325 @@ sub get_trans_re { # # Please send additions if you have them. + # Below strings are found in source of Mailman 2.1.10 and "washed": + # * high-bit chars and html ligatures in latin charsets replaced with .* + # (\S would be better but for some reason the code chokes on that) + # * non-latin charsets included as-is and (if not already) as utf-8 + # * trailing punctuation stripped (to allow small changes to locales) my %translations = + # grep -ri -- '<title>' templates/*/admlogin.html ("authentication" => { - "en" => "authentication", - "de" => "Authentifikation", - "fr" => "authentification", + "ar" => "التحقق من الشخصية لـ .* للقائمة", + "ca" => "Authentication", + "cs" => "p.*ihl.*en.*", + "da" => "Login", + # include old string (possibly bogusly grabbed from PO file) + "de" => "Anmeldung|Authentifikation", + "en" => "Authentication", + "es" => "Autentificaci.*n", + "et" => "autoriseerimine", + "eu" => "Zerrendako .* Identifikatzen", + "fi" => "Authentication", + "fr" => "Authentification", + "gl" => "Autenticaci.*n", + "he" => "האימות של", + "hr" => "Autentikacija", + "hu" => "Azonos.*t.*s", + "ia" => "Authentication", + "it" => "Autenticazione", + # | recode EUC-JP..utf8 + "ja" => "ǧ|認証", + # | recode EUC-KR..utf8 + "ko" => " |관리자 인증", + "lt" => "prisijungimas", + "nl" => "inloggen", + "no" => "Innlogging", + "pl" => "%(listname)s", + "pt" => "Authentication", + "pt_BR" => "Autentica.*o", + "ro" => "Autentificare", + # | recode koi8-r..utf8 + "ru" => "|Аутентификация", + "sk" => "prihlásenie", + "sl" => "Avtentikacija", + "sr" => "Authentication", + "sv" => "Inloggning", + "tr" => "Giri.*i", + "uk" => "Автентифікація", + "vi" => "Xác th.*c", + "zh_CN" => "Authentication", + "zh_TW" => "論壇 壇主驗證", }, + # grep -r -A 1 'msgid "Subscription Requests"' messages/* + "subscriptions" => + { + "C" => "Subscription Requests", + "ar" => "طلبات التسجيل", + "ca" => "Petici.* de Subscripci.*", + "cs" => "Po.*adavky na p.*ihl.*en", + "da" => "Anmoder om medlemskab", + "de" => "Abonnement-Anfragen", + "es" => "Peticiones de suscripci.*n", + "et" => "Liitumisssoovid", + "eu" => "Harpidetza Eskakizunak", + "fi" => "Liittymispyynt.*j.*", + "fr" => "Requ.*tes d'abonnement", + "gl" => "Solicitudes de subscrici.*n", + "he" => "בקשות מנוי", + "hr" => "Zahtjevi za Pretplatom", + "hu" => "Feliratkozási k.*relmek", + "ia" => "Requestas de abonamento", + "it" => "Richieste di iscrizione", + # | recode EUC-JP..utf8 + "ja" => "|入会申請", + # | recode EUC-KR..utf8 + "ko" => " |가입 결과", + "lt" => "Uþsisakymo Pra.*ymas", + "nl" => "Aanmeldingsverzoeken", + "no" => "S.*knader om medlemskap", + "pl" => "Pro.*by o zapisanie", + "pt" => "Pedidos de inscri.*o", + "pt_BR" => "Requisi.*es de Inscri.*o", + "ro" => "Cereri de abonare", + # | recode koi8-r..utf8 + "ru" => " |Запросы на подписку", + "sk" => ".*iadosti o prihl.*senie", + "sl" => "Zahteve za prijavo", + "sr" => "Захтјеви за упис", + "sv" => "Ans.*kningar om medlemskap", + "tr" => "Listeye .*yelik .*stekleri", + "uk" => "Запити на підписку", + "vi" => "Y.*u c.*u .*ng k.*", + "zh_CN" => "订阅请求", + "zh_TW" => "訂閱申請", + }, + # grep -r -A 1 'msgid "Successfully \(subscribed\|Unsubscribed\|Removed\):"' messages/* "subscr_success" => { - "en" => "Successfully ((un)?subscribed|Removed)", - "de" => "Erfolgreich (ein|aus)getragen", + # include old (mistyped, or are these case-insensitive?) uppercase + "C" => "Successfully (([uU]n)?subscribed|Removed)", + "ar" => "تم اشتراكه بنجاح|خطأ في تسجيل الاشتراك|تمت إزالته بنجاح", + "ca" => "Subscrit satisfact.*riament|Subscripci.* Cancel.*lada Satisfact.*riament|Eliminat satisfact.*riament", + "cs" => ".*sp.*n.* p.*ihl.*eni|.*sp.*n.* odhl.*eni|.*sp.*n.* odstran.*ni", + "da" => "Tilmelding er sket|Framelding udf.*rt|Framelding udf.*rt", + "de" => "Erfolgreich (eingetragen|beendete Abonnements|entfernt)", + "es" => "(Subscritos|Ha anulado su suscripci.*n|Ha sido borrado) satisfactoriamente", + "et" => "Lisati aadressid|Tellimus l.*petati|Edukalt eemaldatud", + "eu" => "Behar bezala harpidetuta|Behar Bezala Ezabatuta|Arrakastaz ezabatua", + "fi" => "Onnistuneesti liitetty|Erotettu onnistuneesti|Poistettu onnistuneesti", + "fr" => "Abonnement r.*ussi|R.*siliation r.*ussie|Abonnement r.*sili.* avec succ.*s", + "gl" => "Subscribiuse con éxito|Anulou a súa subscrición satisfactoriamente|Eliminouse satisfactoriamente", + "he" => "נרשם בהצלחה|מנוי בוטל בהצלחה|הוסר בהצלחה", + "hr" => "Uspje.*no (pretpla.*eni|Odjavljeni|Maknut)", + "hu" => "Sikeresen (fel.*rva|t.*r.*lve|t.*r.*lve)", + "ia" => "(Abonate|Disabonate|Removite) con successo", + "it" => "(Iscritti|Cancellati|Rimosso) con successo", + # | recode EUC-JP..utf8 + "ja" => "(||)³λ|(入|退|退)会手続き完了", + # | recode EUC-KR..utf8 + "ko" => " (Ե|Ż|ŵ) |성공적으로 (가입된|탈퇴된|제거된) 명단", + "lt" => "S.*kmingai (u.*sisak.*|atsisak.*|pa.*alinti)", + "nl" => "Met succes (aangemeld|afgemeld|verwijderd)", + "no" => "(P.*melding|Utmelding) utf.*rt", + "pl" => "Pomy.*lnie (zapisano|wypisano|usuni.*to)", + "pt" => "(Inscrito|Inscri.*o anulada|Removido) co?m sucesso", + "pt_BR" => "(Inscrito|Descadastrado|Removido) com [sS]ucesso", + "ro" => "Au fost (abona.*i|dezabona.*i) cu succes", + # | recode koi8-r..utf8 + "ru" => " (| |)|Успешно (подписаны|удалена подписка для|удалены)", + "sk" => "Úspe.*ne (prihlásení|odhlásení|zmazaní)", + "sl" => "Uspe.*no (prijavljeni|odjavljen|odstranjeni)", + "sr" => "Успјешно (уписани|исписани|уклоњени)", + "sv" => "(Anm.*lan|Avanmlan) gjord", + "tr" => "Ba.*ar.*yla (.*ye yap.*ld.*|.*yelikten .*kar.*ld.*|Silindi)", + "uk" => "Успішно (підписано|видалено підписку|видалено)", + "vi" => "Đã đăng ký được|Đã bỏ đăng ký được|Đã gỡ bỏ được", + "zh_CN" => "成功订阅|成功取消订阅|成功删除", + "zh_TW" => "訂閱成功|退訂成功|成功除名", }, + # grep -r -A 1 'msgid "Error \(subscribing\|Unsubscribing\):"' messages/* "subscr_error" => { - "en" => "Error (un)?subscribing", + # include old (mistyped, or are these case-insensitive?) uppercase + "C" => "Error ([uU]n)?subscribing", + "ar" => "خطأ في (الاشتراك|إلغاء الاشتراك)", + "ca" => "Error (subscrivint|cancel.*lant la subscripci.*)", + "cs" => "Chyba p.*i (p.*ihla.*ov.*n.*|odhla.*ov.*n.*)", + "da" => "Fejl under (tilmelding|framelding)", + "de" => "Fehler beim (Abonnieren|Beenden des Abonnement)", + "es" => "Error dando de (alta|baja) la suscripci.*n", + "et" => "Viga aadresside lisamisel|Viga aadressi kustutamisel", + "eu" => "Errorea harpidetzan|Zerrenda uztean errorea", + "fi" => "Virhe (liitt.*ess.*|eroamisessa)", + "fr" => "Erreur lors de (l'abonnement|la r.*siliation)", + "gl" => "(Houbo un erro ao dar de alta|Produciuse un erro ao dar de baixa) a subscrición", + "he" => "שגיאה (ברישום|בביטול המנוי)", + "hr" => "Gre.*ka kod (pretpla.*ivanja|Odjavljivanja)", + "hu" => "Hiba a (feliratkoz.*skor|t*rl*sn*l)", + "ia" => "Error in (abonar|disabonar)", + "it" => "Errore durante (l'iscrizione|la cancellazione)", + # | recode EUC-JP..utf8 + "ja" => "(|)³Υ顼|(入|退)会手続きのエラー", + # | recode EUC-KR..utf8 + "ko" => "(|Ż) |(가입|탈퇴) 에러", + "lt" => "Nes.*kmingai u.*sisakin.*jo|Klaida atsisakant", + "nl" => "Fout bij (het aanmelden|afmelden)", + "no" => "Feil under (p.*melding|utmelding)", + "pl" => "B.*dy przy (za|wy)pisywaniu", + "pt" => "Erro (inscrevendo|ao cancelar a inscri.*o)", + "pt_BR" => "Erro ao (inscrever|descadastrar)", + "ro" => "Eroare la (abonare|dezabonare)", + # | recode koi8-r..utf8 + "ru" => " | |Подписаны НЕ были|Ошибка удаления подписки", + "sk" => "Chyba pri (prihlasovan.*|odhlasovan.*)", + "sl" => "Napaka pri (prijavljanju|odjavi)", + "sr" => "Грешка при (у|uc)пису", + "sv" => "Fel under (anm.*lan|avanm.*lan)", + "tr" => "(.*ye yaparken|.*yelikten .*kar.*l.*rken) hata oldu", + "uk" => "Помилка (при спробі|видалення) підписки", + "vi" => "Lỗi đăng ký|Lỗi bỏ đăng ký", + "zh_CN" => "错误(取)?订阅", + "zh_TW" => "訂閱失敗|退訂時出錯", }, + # grep -r -A 1 'msgid "No such list .*"' messages/* "no_such_list" => { - "en" => "Mailman Admindb Error.*No such list:", + "C" => "No such list", + "ar" => "لا يوجد قائمة بالإسم", + "ca" => "La llista .* no existeix", + "cs" => "Nenalezl jsem konferenci", + "da" => "Listen findes ikke", + "de" => "(Keine Liste mit Namen .* vorhanden|Liste nicht vorhanden)", + "es" => "(La lista .* no existe|No existe tal lista)", + "et" => "(Sellist listi pole|Selle nimega listi pole)", + "eu" => "(zerrendarik ez dago|Zerrenda ezezaguna)", + "fi" => "(Listaa .* ei ole olemassa|Lista on jo olemassa)", + "fr" => "(Liste inexistante|Liste introuvable)", + "gl" => "(A rolda .* non existe|Non existe esa rolda)", + "he" => "(אין רשימה בשם|אין כזו רשימה)", + "hr" => "Takva lista ne postoji <em>%(safelistname)s</em>", + "hu" => "Nincs .* nev.* lista", + "ia" => "(Le lista .* non existe|Nulle tal lista)", + "it" => "Non esiste .*la lista", + # | recode EUC-JP..utf8 + "ja" => "ȤꥹȤϤޤ|というリストはありません", + # | recode EUC-KR..utf8 + "ko" => " ϸ Ʈ ʽϴ|라는 메일링 리스트가 존재하지 않습니다.", + "lt" => "N.*ra forumo", + "nl" => "Er is geen lijst met de naam", + "no" => "Listen finnes ikke", + "pl" => "Nie znaleziono listy|Nie ma takiej listy", + "pt" => "N.*o existe essa lista|Lista inexistente", + "pt_BR" => "Lista .*inexistente", + "ro" => "Nu exist.* lista|Lista aceata nu exist.*", + # | recode koi8-r..utf8 + "ru" => " .* |Список рассылки .*не существует", + "sk" => "Neznáma .*konferencia", + "sl" => "Seznam .*ne obstaja", + "sr" => "Нема листе", + "sv" => "Listan finns inte", + "tr" => "ad.*nda bir liste yok", + "uk" => "Список розсилки .*не існує", + "vi" => "Không có hộp thư (chung|như vậy)", + "zh_CN" => "没有类似的列表|没有这个列表", + "zh_TW" => "(沒有.*這個|無此)論壇", }, + # head -n 2 templates/*/admindbsummary.html + # grep -r -A 1 'msgid "There are no pending requests."' messages/* "pending_req" => { - "en" => "(current set of administrative|pending request)", - "de" => "(gegenwärtigen administrativen|unbearbeiteten Anfragen)", + "C" => "There are no pending requests", + "ar" => "تحتوي هذه الصفحة على تلخيص للطلبات الإشرافية|لا يوجد طلبات معلقة", + "ca" => "Aquesta p.*gina cont.* un sumari del conjunt actual de peticions administratives|No hi ha peticions pendents", + "cs" => "P.*ehled po.*adavk.* pro konferenci|.*dn.* po.*adavky ne.*ekaj.* na vy.*zen.*", + "da" => "Her finder du en oversigt over anmodninger der skal vurderes for maillisten|Der venter ingen anmodninger", + "de" => "Diese Seite zeigt eine .*bersicht der gegenw.*rtigen administrativen|Keine unbearbeiteten Anfragen", + "en" => "This page contains a summary of the current set of administrative", + "es" => "Esta página contiene un sumario de las solicitudes administrativas que|No hay peticiones pendientes", + "et" => "Sellel lehel on ülevaade kõigist||Taotlusi pole", + "eu" => "Orri honetan .* posta zerrendan|Ez dago eskaerarik zain", + "fi" => "Tällä sivulla on lista toimiasi vaativista|Ei odottavia pyynt.*j.*", + "fr" => "Cette page contient un r.*sum.* de l'ensemble des requ.*tes|Pas de requ.*tes en instance", + "gl" => "Esta páxina cont.*n un sumario das solicitudes administrativas que|Non hai ningunha solicitude pendente", + "he" => "עמוד זה מכיל סיכום של קבוצת כל הבקשות המנהלתיות שדורשות|אין בקשות ממתינות", + "hr" => "Ova stranica sadr.*i sa.*etak trenutnog skupa administrativnih zahtjeva|Nema zahtjeva na .*ekanju", + "hu" => "Ezen az oldalon .* levelezõlistához.* tartozó beavatkozásra|Nincsen beavatkoz.*sra v.*r.* teend.*", + "ia" => "Iste pagina contine un summario del collection del requestas|Il non ha requestas pendente", + "it" => "Questa pagina contiene la lista delle richieste amministrative|Non ci sono richieste in attesa", + "ja" => "Υڡ|このページは|αοϤޤ|保留中の申請はありません", + "ko" => " .* ϸ Ʈ|이 페이지는 .* 메일링 리스트| û ϴ|대기중인 요청이 없습니다", + "lt" => "Sprendimo laukian.*i.* lai.*k.* santrauka|There are no pending requests", + "nl" => "Deze pagina toont een overzicht van alle administratieve verzoeken m.b.t. de .* maillijst die wachten op uw goedkeuring|Er zijn geen wachtende verzoeken", + "no" => "Her finner du en oversikt over foresp.*rsler som skal vurderes for epostlisten|Det venter ingen foresp.*rsler eller s.*knader", + "pl" => "This page contains a summary of the current set of administrative|Brak skolejkowanych zada.*", + "pt" => "Esta p.*gina cont.*m um sum.*rio dos pedidos administrativos da lista|N.*o h.* pedidos pendentes", + "pt_BR" => "Esta p.*gina cont.*m um resumo do conjunto atual de requisi.*es|N.*o existem requisi.*es pendentes", + "ro" => "Aceast.* pagin.* con.*ine un sumar al setului curent de cereri administrative|Nu sunt cereri .*n a.*teptare", + "ru" => " |Эта страница содержит сводный список требующих обработки административных| , |Нет запросов, требующих обработки", + "sk" => "Preh.*ad po.*iadaviek pre konferenciu|.*iadne .*iadosti ne.*akaj.* na spracovanie", + "sl" => "Ta stran vsebuje povzetek trenutnih skrbni.*kih zahtev, ki .*akajo|Ni .*akajo.*ih zahtev", + "sr" => "Ова страна садржи преглед тренутних услова за ваше укључење у листу слања|Нема захтјева на чекању", + "sv" => "H.*r finns en .*versikt .*ver f.*rfr.*gningar som ska avg.*ras f.*r e-postlistan|Inga ans.*kningar v.*ntar", + "tr" => "Bu sayfa|Bekleyen istek yok", + "uk" => "Ця сторінка містить загальний список адміністративних запитів|Відсутні запити, що очікують рішень", + "vi" => "Trang này chứa bản tóm tắt các yêu cầu quản trị cần thiết bạn tán thành cho|Không có yêu cầu bị hoãn nào", + "zh_CN" => "此页面包含.*邮件列表|没有挂起的请求", + "zh_TW" => "沒有待決的事項", }, + # TODO: get strings from older Mailman (pre 2.1) containing this one "headline_subscr" => { "en" => "subscription", + "da" => "medlemskab", }, + # TODO: get strings from older Mailman (pre 2.1) containing this one "held_for_approval" => { "en" => "held for approval", }, + # grep -r -A 1 'msgid "Already a member"' messages/* "already_member" => { - "en" => "Already a member", + "C" => "Already a member", + "ar" => "مشترك أصلاً", + "ca" => "Ja ets membre", + "cs" => "Je ji.* .*astn.*kem", + "da" => "Allerede medlem", + "de" => "Bereits Mitglied", + "es" => "Ya est.* suscrito", + "et" => "On juba liige", + "eu" => "Dagoeneko harpidetuta", + "fi" => "Jo j.*sen", + "fr" => "D.*j.* abonn.*", + "gl" => "Xa está subscrito", + "he" => "הנו כבר מנוי", + "hr" => "Ve.* je .*lan", + "hu" => "M.*r tag", + "ia" => "Ja es un membro", + "it" => "Gi.* iscritto", + # | recode EUC-JP..utf8 + "ja" => "˲Ǥ|既に会員です", + # | recode EUC-KR..utf8 + "ko" => "̹ ȸԴϴ|이미 회원입니다", + "lt" => "Jau dalyvis", + "nl" => "Is al lid", + "no" => "Allerede medlem", + "pl" => "Ju.* jest zapisany", + "pt" => "J.* .* um membro", + "pt_BR" => "J.* .* um membro", + "ro" => "Este membru deja", + # | recode koi8-r..utf8 + "ru" => " |Уже является подписчиком", + "sk" => "Je už účastníkom", + "sl" => "Je .*e .*lan", + "sr" => "Корисник је већ учлањен.", + "sv" => "Redan medlem", + "tr" => "Zaten listeye .*ye", + "uk" => "Вже є учасником", + "vi" => "Đã thành viên", + "zh_CN" => "已经是成员了", + "zh_TW" => "已是訂戶", }, ); @@ -1082,6 +1404,7 @@ sub set_param_values { "d" => 3, "sa" => 4, # subscribe approve "sr" => 2, # subscribe reject + "sd" => 3, # subsribe discard }; } else { $data->{"global"}{"actions"} = { "a" => 0, @@ -1108,6 +1431,9 @@ sub read_config { $cur{user} = $cur{password} = $cur{action} = $cur{default} = ""; $cur{confirm} = 1; $cur{unprintable} = "questionmark"; + $cur{cafile} = $default_ssl_cafile; + $cur{verify_peer} = $default_ssl_verify; + $cur{verify_hostname} = 1; my $conf = {}; my $line = ""; @@ -1116,7 +1442,7 @@ sub read_config { my %act = ("approve" => "a", "discard" => "d", "reject" => "r", "skip" => "s", "none" => ""); - my %sact = ("accept" => "a", + my %sact = ("accept" => "a", "discard" => "d", "reject" => "r", "skip" => "s", "none" => ""); return undef unless open (CONF, $file); @@ -1219,6 +1545,23 @@ sub read_config { "unprintable characters: '$cur{unprintable}'\n"; exit 1; } + } elsif ($line =~ /^cafile\s+/i) { + $cur{cafile} = unquote($POSTMATCH); + $cur{cafile} = $default_ssl_cafile + if $cur{cafile} eq "NONE"; + } elsif ($line =~ /^verify_peer\s+/i) { + my $value = unquote($POSTMATCH); + if ($value eq "no") { + $cur{verify_peer} = IO::Socket::SSL::SSL_VERIFY_NONE; + $cur{verify_hostname} = 0; + } elsif ($value eq "yes") { + $cur{verify_peer} = $default_ssl_verify; + $cur{verify_hostname} = 1; + } else { + print STDERR "$file:$lineno: Illegal value: '$value\n"; + print STDERR "choose one of yes or no\n"; + exit 1; + } } else { print STDERR "$file:$lineno: Syntax error: '$line'\n"; exit 1; @@ -1340,13 +1683,13 @@ sub commit_changes { my $params = mailman_params ($user, $pw); my $log = log_timestamp ($list); - # Expand {list}, {subdomain} and {domain} - $logfile = mailman_url($list, $logfile); + # Expand {list}, {subdomain} and {domain}, if there is something to expand + $logfile = mailman_url($list, $logfile) if $logfile; for my $id (sort { $a <=> $b } keys %{$change}) { my ($what, $text) = @{$change->{$id}}; $params->{$id} = $action->{$what}; - unless ($what =~ /^s[ar]$/) { + unless ($what =~ /^s[ard]$/) { # we don't log subscription approval or rejects $log .= sprintf ("%s D:[%s] F:[%s] S:[%s]\n", $what, @@ -1414,7 +1757,7 @@ sub add_subscribers { my $resp = $ua->post($url, \%params); return $resp->status_line unless $resp->is_success; - my $result = parse_subscribe_response($resp->content); + my $result = parse_subscribe_response($resp->decoded_content ); if (!$mail) { my %left = map { $_ => 1 } @addresses; @@ -1472,7 +1815,7 @@ sub remove_subscribers { my $resp = $ua->post($url, \%params); return $resp->status_line unless $resp->is_success; - return parse_subscribe_response($resp->content); + return parse_subscribe_response($resp->decoded_content ); } @@ -1549,7 +1892,7 @@ sub list_subscribers { unless $letter eq "a"; while ($resp->is_success) { - $page = $resp->content; + $page = $resp->decoded_content ; $parse = HTML::TokeParser->new(\$page); my $count = 0; my $repeated = 0; @@ -1634,7 +1977,7 @@ sub fetch_meta_members { $agent->get(mailman_url($list, $config->{adminurl}, "", "members")); - my $page = $agent->content(); + my $page = $agent->decoded_content (); my $parse = HTML::TokeParser->new(\$page); my $tag = $parse->get_tag("textarea"); $tag = $parse->get_tag("textarea"); diff --git a/listadmin.txt b/listadmin.txt index ba1873c..8d38e40 100644 --- a/listadmin.txt +++ b/listadmin.txt @@ -1,49 +1,49 @@ -LISTADMIN(1) LISTADMIN(1) +LISTADMIN(1) General Commands Manual LISTADMIN(1) -NAME +[1mNAME[0m listadmin - process messages held by Mailman for approval -SYNOPSIS - listadmin [-?] [-V] [-f configfile] [-t minutes] [--mail] [--nomail] - [{-a|-r} file] [--add-member address] [--remove-member address] [-l] - [listname] +[1mSYNOPSIS[0m + [1mlistadmin [-?] [-V] [-f [4m[22mconfigfile[24m[1m] [-t [4m[22mminutes[24m[1m] [--mail] [--nomail][0m + [1m[{-a|-r} [4m[22mfile[24m[1m] [--add-member [4m[22maddress[24m[1m] [--remove-member [4m[22maddress[24m[1m] [-l][0m + [1m[[4m[22mlistname[24m[1m][0m -DESCRIPTION - listadmin is a textual alternative to Mailman's WWW interface for +[1mDESCRIPTION[0m + [4mlistadmin[24m is a textual alternative to Mailman's WWW interface for administering mailing lists. -OPTIONS - -f configfile - Fetch list of mailing lists from configfile rather than the - default (~/.listadmin.ini). +[1mOPTIONS[0m + -f [4mconfigfile[0m + Fetch list of mailing lists from [4mconfigfile[24m rather than the + default ([1m~/.listadmin.ini[22m). - -t minutes - Stop processing after minutes has passed. Mostly useful for - completely automated configurations of listadmin. + -t [4mminutes[0m + Stop processing after [4mminutes[24m has passed. Mostly useful for + completely automated configurations of [1mlistadmin[22m. - --mail Addresses added as subscribers will have nomail turned off. + --mail Addresses added as subscribers will have [4mnomail[24m turned off. --nomail - Addresses added as subscribers will have nomail turned on. + Addresses added as subscribers will have [4mnomail[24m turned on. - -a file - Add e-mail addresses listed in file (one address per line) to + -a [4mfile[0m + Add e-mail addresses listed in [4mfile[24m (one address per line) to the subscriber list. The welcome message is suppressed. - --add-member address - Add address to the subscriber list, works as above. + --add-member [4maddress[0m + Add [4maddress[24m to the subscriber list, works as above. - -r file - Remove e-mail addresses listed in file (one address per line) + -r [4mfile[0m + Remove e-mail addresses listed in [4mfile[24m (one address per line) from the subscriber list. - --remove-member address - Remove address from the subscriber list. + --remove-member [4maddress[0m + Remove [4maddress[24m from the subscriber list. -l Display the subscriber list. - listname - Only process the lists matching listname. Specify a complete + [4mlistname[0m + Only process the lists matching [4mlistname[24m. Specify a complete address, a substring or a regular expression. -? or --help @@ -52,7 +52,7 @@ OPTIONS -V or --version Output version number. -CONFIGURATION SYNTAX +[1mCONFIGURATION SYNTAX[0m The configuration file contains lines which can contain either a comment, a directive, or a mailing list address. @@ -66,153 +66,162 @@ CONFIGURATION SYNTAX space characters. Inside double quotes, \" can be used to include a literal double quote, and \\ for a literal backslash. -DIRECTIVES +[1mDIRECTIVES[0m A directive affects all the mailing lists addresses which follow after it in the configuration file. The directives are: - username username + username [4musername[0m Specifies the username to use for authentication. (Not all Mailman servers require a username.) - password password + password [4mpassword[0m Specifies the password to use for authentication. - adminurl url + adminurl [4murl[0m The URL for maintaining Mailman requests. Some substitutions are performed: (examples below refer to the - hypothetical list foo-devel@example.net) + hypothetical list [4mfoo-devel@example.net[24m) - {list} The local part of the list name, e.g., "foo- - devel". + {list} The local part of the list name, e.g., "foo- + devel". {domain} - The domain part of the list name, e.g., - "example.net". + The domain part of the list name, e.g., + "example.net". {subdomain} - The first component of the domain part, e.g., - "example". + The first component of the domain part, e.g., + "example". - default action + default [4maction[0m Specifies the action to take when the user presses just Return. Available actions are: approve - The message will be sent to all member of the - list. + The message will be sent to all member of the + list. - reject Notify sender that the message was rejected. + reject Notify sender that the message was rejected. discard - Throw message away, don't notify sender. + Throw message away, don't notify sender. - skip Don't decide now, leave it for later. + skip Don't decide now, leave it for later. - none Reset to no default action. + none Reset to no default action. - action action + action [4maction[0m This action will be taken for all messages where none of - the other rules apply (e.g., spamlevel, discard_if_from + the other rules apply (e.g., [4mspamlevel[24m, [4mdiscard_if_from[0m etc.), ie., whenever the user would have been asked what - to do. The same actions as for default are available, + to do. The same actions as for [4mdefault[24m are available, although reject isn't very useful. - spamlevel number + spamlevel [4mnumber[0m This specifies the threshold for automatic discard of suspected spam messages. 12 is unlikely to have false positives. No user confirmation is needed, so it is best to play it safe. Less than 5 is not recommended. - spamheader header-name + spamheader [4mheader-name[0m The name of the header which contains the spam score. It is assumed that the score is encoded as a sequence of characters, like "*****" for the value 5. By default it will look for all headers with names containing "spam" and "score" or "level", and pick the highest score if there is more than one. Setting the header-name to - default will restore this behaviour. + [4mdefault[24m will restore this behaviour. - not_spam_if_from pattern + not_spam_if_from [4mpattern[0m If the message's From header matches the pattern, all automatic actions will be cancelled and you will be asked what action to take explicitly. The pattern can use Perl regexp syntax. If enclosed in slashes, some modifiers - can be added, a typical example being /pattern/i to match + can be added, a typical example being [1m/pattern/i [22mto match case-insensitively. - not_spam_if_subject pattern + not_spam_if_subject [4mpattern[0m As above, but matches against the Subject header. - discard_if_from pattern + discard_if_from [4mpattern[0m If the message's From header matches the pattern, it will be discarded automatically. - discard_if_subject pattern + discard_if_subject [4mpattern[0m As above, but matches against the Subject header. - discard_if_reason pattern + discard_if_reason [4mpattern[0m As above, but matches against Mailman's reason for holding the message for approval. - subscription_default action + subscription_default [4maction[0m Specifies the action to take when the user presses just Return while processing subscriptions. Available actions are: - accept The new subscriber will be added. + accept The new subscriber will be added. - reject Notify sender that s/he was not allowed to join - the list. + reject Notify sender that s/he was not allowed to join + the list. - skip Don't decide now, leave it for later. + skip Don't decide now, leave it for later. - none Reset to no default action. + none Reset to no default action. - subscription_action action - This action will be taken always for all new subscribers + subscription_action [4maction[0m + This action will be taken [1malways [22mfor all new subscribers in the relevant lists, no user interaction will take - place. The same actions as for subscription_default are + place. The same actions as for [4msubscription_default[24m are available, although only skip is very useful. It is better to get automatic accept and reject behaviour by changing the Mailman configuration. - confirm yes|no + confirm [4myes|no[0m Before submitting changes, ask for confirmation. Default is "yes". - unprintable questionmark|unicode + unprintable [4mquestionmark|unicode[0m If the subject or sender address contains characters the terminal can't display, they will be replaced by either - "<?>" (in questionmark mode, the default) or something - like "<U+86a8>" (in unicode mode). + "<?>" (in [4mquestionmark[24m mode, the default) or something + like "<U+86a8>" (in [4municode[24m mode). - log filename + log [4mfilename[0m Changes submitted to the web interface are logged. All the changes for one list are sent in batches at the end of processing. The format in the log is first a line containing the list name and a time stamp in local time. Then one line for each message, in the format - action D:[date] F:[sender] S:[subject] + [4maction[24m D:[[4mdate[24m] F:[[4msender[24m] S:[[4msubject[24m] This batch of lines is terminated by a line saying - changes sent to server. + [1mchanges sent to server[22m. - The same substitutions are performed on filename as on - the argument to adminurl. Tilde syntax can be used to - refer to home directories. The filename none turns off + The same substitutions are performed on [4mfilename[24m as on + the argument to [1madminurl[22m. Tilde syntax can be used to + refer to home directories. The filename [1mnone [22mturns off logging. - meta_member_support yes|no + meta_member_support [4myes|no[0m Meta members are an experimental feature at the University of Oslo. This option is enabled by default for lists in uio.no, and is needed to avoid clearing the list of meta members when manipulating the list of - ordinary members. Note: Requires additional Perl module - WWW::Mechanize + ordinary members. [4mNote:[24m [4mRequires[24m [4madditional[24m [4mPerl[24m [4mmodule[0m + [4mWWW::Mechanize[0m -INTERACTIVE USE - The user interface to listadmin is line oriented with single letter + cafile [4m/path/to/CAcertificate[0m + Specify which CA certificate to use for all lists + following. Setting cafile to NONE will use the default + cafile. + + verify_peer [4myes|no[0m + If set to no SSL certificate verification will be + disabled for all lists following. + +[1mINTERACTIVE USE[0m + The user interface to [1mlistadmin [22mis line oriented with single letter commands. By pressing Return, the default action is chosen. The default action is printed in brackets in the prompt. The available actions are: @@ -232,30 +241,30 @@ INTERACTIVE USE t View Time, display the Date header from the message. - number Jump forward or backward to message number. + [4mnumber[24m Jump forward or backward to message [4mnumber[24m. u Go back to the previous message and undo the last approve, discard or reject action. - /pattern + /[4mpattern[0m Search (case-insensitively) for the next message with - matching From or Subject. If pattern is left out, the + matching From or Subject. If [4mpattern[24m is left out, the previous value will be used. - ?pattern + ?[4mpattern[0m As above, but backwards. . Redisplay information about current message. - add Add address as subscriber to the list. If address is + add Add [4maddress[24m as subscriber to the list. If [4maddress[24m is left out, use the sender of the current message. - nomail As add, but adds address with "nomail" enabled. + nomail As [4madd[24m, but adds [4maddress[24m with "nomail" enabled. - list List subscriber addresses matching pattern, or the full - list if no pattern is specified. + list List subscriber addresses matching [4mpattern[24m, or the full + list if no [4mpattern[24m is specified. - rem Remove address from the subscriber list. Note: there is + rem Remove [4maddress[24m from the subscriber list. Note: there is no undo for this action. q Quit processing this list and go on to the next. @@ -264,7 +273,7 @@ INTERACTIVE USE reached. At that time, the user will be prompted whether the changes should be submitted to Mailman (see also "confirm" directive above). -EXAMPLES +[1mEXAMPLES[0m To process only the lists of a single domain, specify the domain as the pattern: listadmin example.com @@ -308,22 +317,22 @@ EXAMPLES confirm no foo-announce@example.net -ENVIRONMENT - http_proxy or HTTP_PROXY +[1mENVIRONMENT[0m + [1mhttp_proxy [22mor [1mHTTP_PROXY[0m Specifies a proxy to use for HTTP. - https_proxy or HTTPS_PROXY + [1mhttps_proxy [22mor [1mHTTPS_PROXY[0m Specifies a proxy to use for HTTPS. - LC_CTYPE + [1mLC_CTYPE[0m The character set support is deduced from this variable. -FILES - $HOME/.listadmin.ini +[1mFILES[0m + [1m$HOME/.listadmin.ini[0m The default configuration file. -BUGS +[1mBUGS[0m The HTML parser is quite fragile and depends on Mailman not to change the format of its generated code. @@ -331,7 +340,9 @@ BUGS double width characters (e.g. Chinese). This is probably a bug in Text::Reform. -AUTHOR +[1mAUTHOR[0m Kjetil T. Homme <kjetilho+listadmin@ifi.uio.no> - 24 Feb 2005 LISTADMIN(1) + Project manager: Johnny A. Solbu <johnny@solbu.net> + + 2016-12-21 LISTADMIN(1) |