自宅で手軽に、なんちゃってニュースサーバ

※ 取りあえず完成しました。意外に長くなっちった。コメントは、適当なニュー スグループか、乾燥板メールで。(2007年4月03日)

このページで解説してる改造やスクリプトは、自由に使っていただいてかまいま せんし、自由に改変していただいてかまいません。その際、特に私に断わる必要 はありません。でも、ナイスな改良を思いついたら、教えてくれるとうれしいか も。

更新履歴

概説

ニュースサーバを本格的に運用するのは、結構大変です。が、欲張らなければ、 結構なんとかなりそうです。

というわけで、このページでは、固定IPじゃなくても、Windowsでも、簡易ニュー スサーバを立ち上げられる方法を簡単に説明します。もちろん、固定IPでも大丈 夫だし、大抵のOSで可能な方法です。ただし、以下の内容の全てを試したわけで はありませんので、自己責任で試してください。

ちなみに、自分でニュースサーバを立ち上げると何がうれしいかというと、

  1. 複数のサーバから記事を持ってきて読めるので、サーバを渡り歩く必要が ない。
  2. ささやかながら、ネットニュースの配送に貢献できる。
  3. その気になれば、他人が読める環境を用意できる。

といったところかな?

基本方針

  1. NNTPサーバには、perlモジュールのNNMLを使う。
  2. 配送には、NNML附属のnnmirrorを使う。
  3. 非固定IPでドメイン名が必要なら、ダイナミックDNSを使う。

本当は、parかなんかでexe化しとくと楽なんだろうけど、諸般の事情でやってな い。できそうな人がいたらやってみてほしい。

インストール

perlのインストール

perlがなければ、perlをインストールしてください。WindowsだとActivePerlが 使えます。方法は例えば、「ActivePerlのインストール方 法 - Windows で perl を使おう!」とか。Cygwin環境やMingw環境のperlで も大丈夫なはず。

Windows以外なら、大抵は入ってるんじゃないかな? 入ってなかったら何か探し てインストールしてください。

NNMLのインストール

cpanを使って、NNML::Serverをインストールしてください。質問には、好みで答 えればよし。ActivePerl +CPANによるモジュールインストール方法は、まあ探せば何かあります。それ以外のperlで のcpanの使い方も、まあ、探せばあ るはず。

どこに何がインストールされたのかは、インストール画面の最後のあたりで分か ります。後で必要になるのでチェックしといてください。まあ、忘れてても大体 の見当はつきますが。ActivePerlの場合、perlをインストールしたディレクトリ をc:\perlとすると、c:\perl\site\lib\NNMLあたりの下に*.pmが入って、 c:\perl\binあたりにnnmirror.batが入ってるはず。ドキュメントは、 c:\perl\html\site\lib\NNMLとかc:\perl\html\binとか。普通のperlだとhtmlま では作ってくれないかも。perldoc使えってか?

「ActivePerlならcpanじゃなくてPPMでどうだ?」って意見もあるかもしれません が、PPMだと附属のnnmirrorをインストールしてくれないっぽいので、cpanの方 がよさげ。

つぎに、ニュースの記事を収める場所(base)を用意します。NNMLのインストール 時にbaseの設定をデフォルトのままにした人は、homeの下にMailというディレク トリ(フォルダ)を作ってください。ただし、ActivePerlの場合は、homeという概 念がないので、次の項を参考にして作ってください。baseの設定をいじった人は、 それに合わせて作ってください。

つぎに上で作ったbaseのディレクトリの下に「active」という名前の空のファイ ルを作ってください。Windowsの場合は、例えば、「右クリック→新規作成→テ キスト ドキュメント」でファイルを作って、名前を変更するとか。

ちなみに、baseのデフォルト値が「~/Mail」になってるのは、NNMLの作成動機と からんですようです。

ActivePerlのためのハック

ActivePerlの場合、未実装の関数(getpwnamとgetpwuid)が引っかかるので、NNML のConfig.pmに手を入れる必要があります。

まず、c:\perl\html\site\lib\NNMLみたいなところにConfig.pmというファイル があることを確認してください。実際にいじる前にコピーを作っておくこと。バッ クアップ用です。つぎに、Config.pmを書き込み可能にしてください。Windowsだ とConfig.pmのプロパティ開いて読み取り専用のチェックボックスを外し、セキュ リティ・タブでEveryoneにフルコントロールを許可するとか、そんな感じ。

つぎに、Config.pmを編集します。これはテキストファイルなので、適当なテキ ストエディタ(メモ帳とか)で開いてください。80行目あたりに「sub home」で始 まる関数定義があって、その中に「my $home = (getpwnam($user))[7];」という 行があるんで、これを「my $home = $ENV{'HOME'} || $ENV{'LOGDIR'} || (getpwuid($user))[7];」に書きかえます。

これで、関数homeの定義は、

sub home {
  my $self = shift;

  return $self->{_home} if exists $self->{_home};
  my $user = $ENV{'USER'} || $ENV{'LOGNAME'} || getpwuid($<);
  my $home = (getpwnam($user))[7];
  $self->{_home} = $home;
}

から、

sub home {
  my $self = shift;

  return $self->{_home} if exists $self->{_home};
  my $user = $ENV{'USER'} || $ENV{'LOGNAME'} || getpwuid($<);
  my $home = $ENV{'HOME'} || $ENV{'LOGDIR'} || (getpwuid($user))[7];
  $self->{_home} = $home;
}

になります。

つぎに、44行目あたりの「sub local_user {」という行の前あたりに、

$ENV{'USER'} = "hoge";
$ENV{'HOME'} = "c:/hoge";

という行を入れます。ただし、1行目のhogeは自分のユーザ名です。が、デフォ ルト値として使うだけなので、適当でよいです。 2行目の「"c:/hoge"」は、ニュースの記事が収まる場所を指定してください。そ の下に(デフォルトでは)Mailというディレクトリを作って、その下にいろいろ入 ります。(NNMLのインストール時にbaseをhomeからの相対パスで指定しなかった 人は、ここは適当な文字列でよい。)

これで、保存して、エディタを終了すれば、ここでの作業は取り敢えず終わりで す。

Pathを付加するためのハック

NNMLは、Path:ヘッダをいじってくれないので、自分のサイトをPath:に加えたけ れば、Config.pmとConnection.pmに手を入れる必要があります。必須ではないで すが、普通のサーバとの連携を考えるとやっといた方がよいでしょう。

まず、前項と同様の手順でConfig.pmを編集します。31行目あたりの「$Config = bless {」という行の次あたりに、「 'pathhost' => q[hoge-news],」と いう行を加えます。ただし、「hoge-news」の部分は、自分のサイトを表わす文 字列です。他のサイトとかぶるとまずいので、かぶりにくいように設定しましょ う。いくつかのニュース記事のPath:を見て、どんなのがよいか考えるのがよい でしょう。自分のサイトのドメイン名なんかが無難かも。例文通りなのは論外。

これで、

$Config = bless {
  'active'        => q[~\active],
  'base'          => q[~/Mail],
  'local_passwd'  => q[],
  'local_user'    => q[~],
  'mirror_host'   => q[],
  'mirror_port'   => q[119],
  'passwd'        => q[~\passwd],
  'port'          => q[119],
  'remote_passwd' => q[],
  'remote_user'   => q[~],
}, 'NNML::Config';

が、

$Config = bless {
  'pathhost'      => q[hoge-news],
  'active'        => q[~\active],
  'base'          => q[~/Mail],
  'local_passwd'  => q[],
  'local_user'    => q[~],
  'mirror_host'   => q[],
  'mirror_port'   => q[119],
  'passwd'        => q[~\passwd],
  'port'          => q[119],
  'remote_passwd' => q[],
  'remote_user'   => q[~],
}, 'NNML::Config';

みたいになります。(NNMLのインストール時の設定次第では、ちょこっと違うだ ろうけど。)

つぎに、同じディレクトリにある、Connection.pmを編集します。エディタで開 くまでの要領は、Config.pmのときと同じ。

577行目あたりの「sub NNML::Server::unspool」で始まる関数定義の中身をいじ ります。588行目あたりから、

    while (defined ($ent = <$sf>)) {
      chomp($ent);
      next unless $ent;
      my($ctl, $art) = split /\n/, $ent, 2;
      my ($msgid, $extra_group, $create) = split /\t/, $ctl;
なんて部分があるんですが、この直後に「 $art = add_path($art);」とい う行を加えて、
    while (defined ($ent = <$sf>)) {
      chomp($ent);
      next unless $ent;
      my($ctl, $art) = split /\n/, $ent, 2;
      my ($msgid, $extra_group, $create) = split /\t/, $ctl;
      $art = add_path($art);

とします。つぎに、609行目あたりの「sub inject_article {」という行の前に、

sub add_path {
    my $art = shift;

    my ($head, $body) = split /^$/m, $art, 2;
    my @header = split /\n/, $head;
    for (0 .. $#header) {
	if ($header[$_] =~ /^Path:\s/) {
	    $header[$_] =~ s/^Path:\s+(\S)/Path: $Config->{pathhost}!$1/
		if $header[$_] !~ /$Config->{pathhost}!/;
	    last;
	}
    }

    return join("\n", @header) . "\n" . $body;
}

という関数定義を加えます。

あとは、保存してエディタを終わるだけ。ファイルのパーミッションは、元に戻 しといた方がいいかも。

とりあえず使う

ここまでできれば、nnmirrorを使って、自分の読めるサーバから記事を持ってき たり、自分とこの記事を送ったりできます。この方法は、24時間体制じゃなくて もへっちゃらなので、なんちゃって度が高い。

まず、自分のサーバを立ち上げます。コマンドプロンプトで、

perl -MNNML::Server -e server

とでもすれば、いくつかのメッセージの後、反応が無くなります。 「listening on port 119」みたいなメッセージが出たら、成功みたい。入力促 進の状態に戻ったりしたら、立ち上げに失敗してます。エラーメッセージを読ん で対処してください。立ち上げに成功したら、この窓は最小化でもしといてくだ さい。

次に、時間的に余裕のあるときに、コマンドプロンプトで、例えば、

nnmirror -fhost film.rlss.okayama-u.ac.jp -date 20000101 -group fj.*,!fj.soc.*

とでもすれば、film.rlss.okayama-u.ac.jpにある2000年1月1日以降のfjの記事 (ただしfj.soc以外)を持ってくることができます。(とはいえ、このサーバは、 fj.*の記事を3ヶ月分しか持っていない。)

オプション「-date」を省略すれば、ここ24時間の記事を持ってきます。

サーバhogeにユーザ名foo、パスワードbarでアカウントを持ってる場合は、

nnmirror -fhost hoge -fuser foo -fpass bar -date 20000101 -group fj.*,!fj.soc.*

みたいな感じ。より詳しくは、nnmirrorのドキュメントを読むこと。

この段階では、持ってきた記事は、一つのファイルにまとめられてます。

perl -MNNML::Server -e unspool

とすれば、ニュースグループ名に応じたディレクトリが作られ、ファイル名が数 字のファイル一つにつき記事が一つ入ります。activeファイルも更新されます。

逆に、記事を送るときは、

nnmirror -fhost localhost -thost hoge -tuser foo -tpass bar -date 20000101 -group fj.*,!fj.soc.*

みたいな感じ。

サーバを停止したいときは、サーバを立ち上げた窓を開いて、ctrl+Cでよい。

この辺の作業は、自動化したいところなんで、スクリプト を用意しました。適当なディレクトリで展開して、config.plを編集してく ださい。例によってテキストファイルです。

# config.pl

$site{film} = {
    host => "film.rlss.okayama-u.ac.jp",
    mode => rw,
    group => 'kgk.*,asataku.*,jape-n.*,feedmania.*,woyadi.*,japon.*,tsukuba.*,null.*,kansai.*,kanto.*,japan.*,fj.*,nihon.*',
};

1;

# end of file

みたいな内容です。「#」で始まる行はコメントです。$site{}の括弧の中は、そ のサーバを表わす適当な文字列です。複数のサーバを登録するときは、それぞれ に、「$site{hoge}=」で始まる定義を書いてください。(中の文字列は適当に変 えて。) hostには、相手のサーバのドメイン名(またはIPアドレス)を書きます。 modeはr, w, rw, のいずれかで、それぞれ、読み込み用、書き込み用、読み書き 用です。groupは、やりとりするニュースグループを表現する文字列です。それ ぞれのグループに対する表現を「,」でつないでください。余分な空白が入ると だめです。「*」はワイルドカードです。欲しくないグループを表現するときに は「!」を使ってください。(「fj.*,!fj.soc.*」みたいな感じ。)

ユーザ名foo、パスワードbarでアカウントを持ってる場合は、userとpassを次の ような感じで設定します。

# config.pl

$site{film} = {
    host => "film.rlss.okayama-u.ac.jp",
    mode => rw,
    user => foo,
    pass => bar,
    group => 'kgk.*,asataku.*,jape-n.*,feedmania.*,woyadi.*,japon.*,tsukuba.*,null.*,kansai.*,kanto.*,japan.*,fj.*,nihon.*',
};

1;

# end of file

portというキーワードでポートを設定することもできます。

config.plの編集が終ったら、date.txtを編集してください。いつの記事からや りとりするかを表わすファイルで、年月日を4桁2桁2桁で表します。

それだけの設定が終わったら、feed.pl(Windowsならfeed.bat)をオプションなし で立ち上げてください。(結構時間かかるかも。) date.txtで指定した日付以降 の記事を取ってきて、配る必要のある記事は配って、date.txtを昨日の日付に変 えるはずです。(今日でなく昨日にしたのは、安全性を考えて。)

feed.bat(またはfeed.pl)に数字の引数を与えて立ち上げると、その秒数の間隔 で上の動作を繰り返すはずです。例えば、10分ごとにやりとりしたい場合は、コ マンドプロンプトで、

feed.bat 600

とか。これも止めたいときは、ctrl+C。

自分で読み書きする

自分のニュースリーダの見るサーバをlocalhostに設定すれば、自分で読み書き できるはず。

スプールを直接読む機能がニュースリーダにあれば、それを使うもよし。

他人に読み書きさせる

これをやろうとすると、24時間体制に準ずる程度にはサーバを立ち上げっぱなし にする必要があります。

他人に読み書きさせるには、ドメイン名か固定IPが必要です。後者が手に入って ない場合は、ダイナミックDNSで大丈夫でしょう。日本にも無料のダイナミッ クDNSのサービスがいくつかあるみたいですから、調べて使ってください。 DiCEとか使えると ころが便利かも。

アカウントを発行してユーザ認証することもできるみたいです。自分で調べてく ださい。デフォルトでは、普通にopen NNTPになるんだと思う。

取り敢えず、自分のサイトのドメイン名(ポートが標準の119でないときはポート も)を公表(なり使って欲しい人に伝えるなり)すればOK。

相互配送

上の方法でも実用上問題ないでしょうが、ちゃんと相互配送した方が配送網が広 がりやすいでしょう。それに、相手が普通のサーバなら、記事を即時送ってくれ るかもしれません。

相互配送しようとすると、24時間体制に準ずる程度にはサーバを立ち上げっぱな しにする必要があります。まあ、相手が普通のサーバなら、数日程度は平気で待っ てくれますが。

相互配送するには、ドメイン名か固定IPが必要です。後者が手に入ってない場合 は、ダイナミックDNSで大丈夫でしょう。日本にも無料のダイナミックDNSのサー ビスがいくつかあるみたいですから、調べて使ってください。

あと、相手が普通のサーバなら、特に、上の「Pathを付加するためのハック」が 重要になります。

配送先を見つけるのは、知合いに頼むなり、どっかのグループで募集するなり、 NewsFeed ML - NetNews の相互配送などのための Mailing listで募集する なり、いろいろ方法があります。

配送してくれるところが決まったら、前項に準じてconfig.plを編集します。た だし、modeはwにしてください。向こうが記事を送ってくれるので、こちらから 読みにいく必要はありません。

先方には、自分のドメイン名とpathhostと欲しいグループを伝えれば、設定して くれます。このページで解説してるなんちゃってサーバだってことも伝えた方が いいかも。なんちゃってサーバ同士の相互配送もできるはず。多分。

問題点

所詮、なんちゃってサーバなので、いろいろ問題点はあります。取り敢えず分かっ てることは、

  1. NNMLの解説は非常に少ない。
  2. 記事が多くなると遅い。
  3. 即時配送ができない。
  4. expireの機能がない。
  5. スパム対策の機能がない。
  6. Windowsユーザにperlをインストールしろってのはどうか。

ぐらい。1番目については、日本語の解説は皆無と言っていいほどありません。 英語にしても附属のドキュメント以外は見かけない。まあ、小さいプログラムな んでソースを読めってか? それとも、自分で解説書け? 誰かお願い(ぉぃ)。

2番目は、どうやら、全体のデータベースが無さそうなことに起因してるみたい。 newnewsコマンドあたりの実装をハックしないと、どうしようもないんじゃない かな?

3番目は、記事を受けとった時点で何かアクションを起すようにハックしないと 無理そう。

4番目は、MUA(ニュースリーダとかメールリーダとか)で対処できるかも。でも2 番目に対処しちゃうと同期が難しい? やはり、専用スクリプトを書けってか?

5番目は、3番目と同様。記事を受け取ったらスパムフィルターを通してから、配 送に渡すのがいいんでしょうね。

6番目は、parかなんかでexeファイルにするって手はあります。が、あれは、 perlのインタプリタを自己展開する形式だからなあ。2つか3つのスクリプトを走 らせたら、2〜3個のインタプリタを展開することになる。なんか無駄が多い。

やっといた方がいいかなってこと

  1. date.txtは日単位だけじゃなくて時刻まで指定してもバチはあたらんのじゃ ないか? これはすぐにできるはず。(やるかどうかは別として。)
  2. 先方に配送するときに、つながらなかったら、date.txtの更新をやめた方 がいいかなあ? そうなると、サーバごとに該当するデータが欲しくなる? これは、まだ、実装を考えてない。

というわけで、いろいろ問題はありますが、それでもよければ、使ってみてくだ さい。

おわり