カテゴリー:プログラム

検索



Please click now!!

perl で祝日を特定するサブルーチン。

ちょっとわけあってカレンダーをホゲっているんですが、祝日管理というのが大変に面倒であることが判明した。 perl でホゲっているのでウェブで探したり、モジュールを検討したんだけど、どうもしっくり来ない。祝日が変更になるとソースコードを改変したり、モジュールをインストールしなければならなかったりで、ずっと保守がつきまとう・・。orz。

何かないかなぁ?とか探していたら google カレンダーの API を叩いて祝日をゲットして来る。ってのを見つけたけど XML のパースとか、どーもやることが多すぎる。他に何か無いかなぁ。とか探したり、手段を見つけようとしてます。あ。そー言えば、僕、ical サーバ作ったじゃん。てのを思い出したので、ical サーバから情報持って来られないか調べたけど、ちょっとダメっぽい。

それならば。と思いついたのが、ics ファイルをパースするサブルーチン作ってしまえ。ってことで非常にオオチャクな発想をしてしまったのであります;-)。

まず、Apple のサイトから日本の休日カレンダーを取ってきます。MacOSX のアプリとして iCal があり、それ用に提供している ics ファイルがたくさんある URL があります。

iCal カレンダーライブラリ

ここから日本の祝日用カレンダーを2,3ヶ月に一度 cron で持ってくるようにします。祝日はそーそー更新されることは無いと思うので頻度は低くて大丈夫。

そして、Apple が提供している情報は多分 iCal アプリがある間は公開し続けてくれるだろうと勝手に思っているので安心感がある;-)。

% ftp -a http://ical.mac.com/ical/Japanese32Holidays.ics

さてと。これで絶えず最新の祝日情報がゲットできました。あとはこれをパースして祝日を抽出すれば良いだけですね;-)。

僕の書いたサンプルコードはこんな感じ。もっと美しくかける方いましたら絶賛募集中;-)。

#!/usr/bin/perl
use strict;
use Date::Manip;
# ## main # my $date = shift; if ($date) { my $data = &holiday_check($date); print $data ."\n\n"; } else { for (my $mm = 1;$mm < 13;$mm++) { for (my $dd = 1;$dd < 32;$dd++) { my $date = "2011" . sprintf("%02d",$mm) . sprintf("%02d",$dd); my $data = &holiday_check($date); print $data ."\n" if ($data); } } }
exit 1;
# ## 祝日のチェック # sub holiday_check { my $date = shift; my $name = ""; my $day = ""; my $f = 0;
open(CAL,"<./Japanese32Holidays.ics"); while( my $line = <CAL> ) { $line =~ s/\r\n//g; chomp $line; $f = 0; if ($line =~ /^BEGIN:VEVENT/) { while( my $line = <CAL> ) { $line =~ s/\r\n//g; chomp $line; last if ($line =~ /^END:VEVENT/); # 日付が決まっている祝日の場合はそれを利用する if ($line =~ /^(DTSTART;VALUE=DATE:)/) { $line =~ s/$1//; $day = $line; $f = 1 if ($day eq $date); } # 祝日名を取得 if ($line =~ /^(SUMMARY:)/) { $line =~ s/$1//; $name = $line; } # 祝日法で決まっている祝日の情報の取得 if ($line =~ /^RRULE:/) { my $yy = substr($date,0,4); my $mm = ""; my $dd = ""; my @tmp = split(";",$line); foreach my $a (@tmp) { # BYMONTH は何月かの情報 $mm = $1 if ($a =~ /BYMONTH=(.*)/); # BYDAY は月曜日が祝日の時の計算 # 今のところは月曜日しか無いので MO の狙い撃ち # 祝日法が変わったら改修しようね;-)。 if ($a =~ /BYDAY=(.*)MO/) { $dd = $1 ; my @a = ParseRecur("*$yy:$mm:$dd:1:0:0:0"); my $d = substr($a[0],0,8); #print "@a,$a[0] : $date,$d\n"; if ($date eq $d) { $f = 1; last; } } } # BYDAY が無い RRULE の場合は DTSTART の月日を利用する unless ($line =~ /BYDAY=/) { my $datesv = $date; my $d = substr($datesv,4,4); if ($day =~ /$d$/) { $f = 1; last; } } } } last if ($f); } } close(CAL);
my $data = ""; if ($f) { $data = "$date : $name "; } return($data); }

このプログラムを CheckHoliday.pl として保存して、実行する場合は以下のような感じ。

 % ./CheckHoliday.pl 20110718
20110718 : 海の日 (Marine Day)

スクリプトの引数に YYYYMMDD を指定します。その日が祝日であった場合にはその名前を表示します。日付を指定しない場合は 2011 年の祝日全てを表示します。メインのほうは皆さんの環境に合わせて作ってください。今回はサブルーチンのほうがメインになります。


このスクリプト、ちょっと解説すると以下になります。

1. 基本的には VCALENDAR 形式の ics ファイルをパースします。
2. 振替休日 など、日付が固定しているデータは DTSTART を参照します。
3. 海の日 など、第二、三月曜日が休みな場合は RRULE の BYDAY を見ます。
4. RRULE に BYDAY が無いヤツは国が日付を決めた祝日なので DTSTART の MMDD のみを参照します。

これで祝日の情報を取得します。Date::Manip モジュールの ParseRecur は第何週の日付を返してくれるモジュールでこれが非常に助かりました。

と、言うことで、日付を与えればその日付が祝日であれば値を返す。と言うサブルーチンの完成です。

速度的にみると 20110101 から 20111231 までを調べるとだいたい 1.5 秒くらいかかりますかねぇ。ちょっと重いか?

良かったら参考にしてください。あ。もっと綺麗なコード、絶賛募集中です;-)。

apache で Proxy を作ってみた。(2)

前回書いた続編と言うか・・。

一応、apache を Proxy として利用できるようになり、コメントで hagyさん から、mod_mem_disk と mod_mem_cache の両方を書いたときの動作確認してよー。って言われたので、そのテストをしようと思った矢先・・。

テスト環境では、キャッシュしようと思っていたデータがベーシック認証の中にあったのでありました。つまり、キャッシュしてはいけないデータなので apache Proxy はデータをキャッシュしてくれないんですね・・。

フリーソフトウェアのほとんどの Proxy サーバはベーシック認証やダイジェスト認証の中にあるコンテンツについて、セキュリティの観点によりキャッシュサーバでは絶対にキャッシュしない。と言うポリシーのもとで作成されているみたいです。apache の場合、ドキュメントにちゃんと書いてあります。

http://httpd.apache.org/docs/2.2/ja/mod/mod_cache.html#cacheignorecachecontrol

NetApp 社の NetCache もしかりですが、設定が用意されているみたいで on no-cache とか on uncond-cache がそれにあたります(当然、設定 GUI からもできるとは思いますが;-)。

さてと。どーすんべなぁ。とか思って、一応 apache のソースを眺めたらわりと簡単にソース修正できそうだったので変更してしまいました。以下はそのパッチです。

--- modules/cache/mod_cache.c.ORG       2009-05-02 10:40:03.000000000 +0900
+++ modules/cache/mod_cache.c   2009-05-02 10:39:25.000000000 +0900
@@ -95,9 +95,12 @@
     /* First things first - does the request allow us to return
      * cached information at all? If not, just decline the request.
      */
+
+    /* Authorization pass.
     if (auth) {
         return DECLINED;
     }
+    */
/* * Try to serve this request from the cache.

認証を通った後の判断を調べてリターンしている所をそっくりコメントアウトするパッチです。これにより、ベーシック認証通過後のコンテンツもキャッシュすることができるようになります。


ふぅ。これでキャッシュ用 Proxy サーバはなんとかでき上がりました。では続いて懸案であった、mod_mem_disk と mod_mem_cache の両方を書いたときの動作確認をしたいと思います。

が・・。どう言うわけか mod_mem_cache の設定を書いていると httpd が Segmentation fault(11) してしまいます(今回は prefork で make したので一個のプロセスが Segmentation fault を起こすのみで、全ての httpd は死んではいないため port:80 では LISTEN が可能状態です)。一番最初、キャッシュしていない状態でアクセスがあった場合はマスターサーバから、仮に「A」と言うコンテンツを取ってきてキャッシュして、そしてクライアントに返します。

次に別のクライアントがコンテンツ「A」を取りに来たとき、httpd がこの時に Segmentation fault(11) で落ちてしまう。と言う状態に陥ったのでありました。なので、コンテンツは DISK 上(もしくはメモリ上)に溜まるけど、利用されることは無い。と言う感じですね。

#ちなみに mod_mem_cache で溜まったキャッシュの情報ってどうやって確認するのだろう・・。メモリ利用量のみで判断するのかなぁ?

mod_mem_cache を削除して mod_mem_disk の設定のみを書いたときは正しくキャッシュできて、 Segmentation fault(11) もせず、キャッシュされたデータも再利用できることを確認しました。なので運用的にはキャッシュデータは DISK のみに保存する。と言う方向で行きたいと思います。

ごめんねー。hagyさん。

それにしても、色々詳しく調べるまで、ベーシック認証配下のコンテンツはキャッシュしない。と言うのを知らなかったなぁ。まぁ、考えてみれば当たり前だよなー。認証の奥にある情報が実は Proxy キャッシュサーバの中に残っていた。なんてのは怖いことだもんなぁ。

iPod Touch のメールを ssh トンネルで。

前回のブログで iPod Touch を JailBreack したと書いた。でもってメールソフトは iPhone のファームから抜いて iPodTouch にインストールした。

メールソフトも利用できて大変嬉しいのだけど、うちのメールサーバは ssl に対応していないので POP もしくは IMAP4 の時に生パスワードが流れてしまう。

iPod Touch の性格上、野良 AP (そこいらに落ちているアクセスポイントのこと)を利用する場合も多々有るわけで、そう言うのを利用するとアクセスポイントを置いた人が tcpdump を仕掛けていないとも限らないし、ポートスキャンしてくるかもしれない。なので、POP/IMAP4 の時は是非とも暗号化して利用したいものです。

幸い、JailBreack した iPod Touch には ssh が入っているのでこれを利用して POP サーバ、もしくは IMAP4 サーバに ssh トンネルを掘ることが可能です。

と、言うことで簡単に ssh トンネルを掘るスクリプトを書いてみました。利用方法は以下の通り。

1. POP/IMAP4 サーバ上に自分のアカウントが存在し ssh ログインできること。
2. メールのアカウント設定で POP/IMAP4 サーバではなく localhost に接続するようにすること。
3. ターミナルから stun.sh が実行できること。

これだけです。まぁ、1. はちょっと敷居が高いかも。けど、それがクリアできれば特に問題は無いでしょう。以下の URL からダウンロードできます。

http://www.icmpv6.org/Prog/MacOSX/stun.sh

利用方法ですが、以下の通り。

1. コマンドオプションは三つ。s(start) t(stop) c(check)。
2. s オプションで Mail を kill してから ssh トンネルを掘りますが、接続できた時はパスフレーズを聞いて来るので入力。
3. t オプションで Mail と ssh トンネルのプロセスを殺します。
4. c オプションで Mail と ssh のプロセス番号を表示します。

注意点としては、ssh トンネルを掘る前に Mail が起動していると POP/IMAP4 サーバに接続できません。なので、s オプションの時に Mail を kill しています。c オプションで ssh プロセスの番号が Mail より若いことを確認します。
後、一回スリープしたら ssh セッションが切れるので毎回 t して s してあげる必要があります。

こんな所でしょうか。ダウンロードしたら stun.sh の上の環境変数にサーバ名とユーザ名を指定してください。default で localhost の port:143 を使うようにしています。

もしダウンロードした人がいたらコメントなど頂ければと思います。

Count.cgi と IPv6 の関係。

ウェブページのカウンターとしては Count.cgi と言うのが有名なんですが、まぁ、以下の URL のプログラムです。

http://www.muquit.com/muquit/software/Count/Count.html

僕もその昔、ASP にいた頃良くメンテしていました。

で、このカウンター、同一のアクセスもとから何回もアクセスがあるとカウンターが上がらないようにするオプションがあるんだけど、conf/count.cfg の count_reload=No かな。リロードしてもカウンタ-アップしないうにするオプションです。

しかし、IPv6 のアドレスからアクセスすると、この設定が有効にならないのねぇ・・。

カウンターの元データとなる Counter/data/file.dat にはカウンター数と最後にアクセスのあった IPv4 アドレスを保持します。このファイルに掲載されている IPv4 アドレスからアクセスがあるとカウンタアップしないんだけど、IPv6 アドレスからのアクセスだと count_reload=No が有効にならないのでガンガンカウンター値が上がってしまうX-(。

こらー、IPv6 対応させる必要があるかなぁ・・。どなたかチャレンジして、FreeBSD の ports でええので対応しませんか?:D:D

 
Copyright (c) 1997-2011 takachan@running-dog.net All Rights Reserved.