前回のPHPで書いたMVCでは「V」のコード部分はごく小さいview()関数のみでしかもほとんど書き換えすることはなさそうなのでファイル分けしないで「C」と統合させてみました。(viewctl.cgi)


ということで今回は「M」をM.pmに、「V」と「C」をviewctl.cgiにファイル分けしてみました。


処理内容は、メニューを選んで画面遷移していくシンプルなものです。


■「V」+「C」(viewctl.cgi)

#!/usr/bin/perl
#
# filename: viewctl.cgi
#
# memo: 表示(View)と制御(Controller)の機能を受け持つ
#
use strict;
use constant MAX_INPUT=>1024*512; # 最大許可受信バイト
use constant HEADER=>"Content-type: text/html; charset=Shift_JIS\n\n";
use vars qw/%req %file $obj %MENU/; # グローバル
use Switch; # switch文モジュール
use M; $obj= M->new(); # モデルを取り込んでインスタンス化


# メニューのキーと表示順と文字
%MENU= (next1=>{index=>1, text=>"メニュー1"},
    next2=>{index=>2, text=>"メニュー2"},
    next3=>{index=>3, text=>"メニュー3"});



# urldecode
&decode();



# 全actデフォルト値セット
$obj->{tpl}= $obj->{errtpl}= "./common.tpl";
$obj->{SITENAME}= "MVCで作るCGIテスト";



# actごとのページ切り替え
my $body;
switch ($req{act})
{
case "next1" {
    $obj->{TITLE}= "next1のページ";
    $obj->{act}= "next2";
    $body= $obj->body_next1() or &error;
    $obj->{body}= &menu('next1') . $body;
}
case "next2" {
    $obj->{TITLE}= "next2のページ";
    $obj->{act}= "next3";
    $body= $obj->body_next2() or &error;
    $obj->{body}= &menu('next2') . $body;
}
case "next3" {
    $obj->{TITLE}= "next3のページ";
    $obj->{act}= "next1";
    $obj->{message}= 'メッセージを表示させてみるテスト';
    $body= $obj->body_next3() or &error;
    $obj->{body}= &menu('next3') . $body;
}
else {
    $obj->{TITLE}= "トップページ";
    $obj->{act}= "next1";
    $body= $obj->body_toppage() or &error;
    $obj->{body}= &menu() . $body;
}
}



# 画面表示
&view();



sub view
{
    my $tpl= $obj->{'errmsg'} ? $obj->{'errtpl'} : $obj->{'tpl'};
    my $fh= *view;
    my $html;


    open($fh, $tpl);
    read($fh, $html, -s $fh);
    close($fh);


    $html=~ s/%(\w+)%/$obj->{$1}/g;
    $html=~ s/%act%/$obj->{act}/g; # 今回は置換後の文字に%act%が含まれるように手抜きして書いたので
    print HEADER,$html;
    exit;
}
sub error
{
    $_[0] and $obj->seterrmsg($_[0]);
    &view();
}
sub entity
{
    $_[0]=~ s/&/&/g;
    $_[0]=~ s/</&lt;/g;
    $_[0]=~ s/>/&gt;/g;
    $_[0]=~ s/"/&quot;/g;
}
sub menu
{
    my $menu;


    for (sort{$MENU{$a}->{index} <=> $MENU{$b}->{index}}keys %MENU)
    {
        &entity($MENU{$_}->{text});


        if ($_[0] eq $_){
            $menu.= $menu ? " | $MENU{$_}->{text}" : $MENU{$_}->{text};
        } else {
            my $tmp= "<a href=\"$ENV{SCRIPT_NAME}?act=$_\">$MENU{$_}->{text}</a>";
            $menu.= $menu ? " | $tmp" : $tmp;
        }
    }


    return $menu;
}
sub decode
{
    my $buf;


    if (my $cl=$ENV{CONTENT_LENGTH}) # post
    {
        if ($cl > MAX_INPUT){ &error("request data size over!"); }
        binmode(STDIN); read(STDIN, $buf, $cl);
        if ($ENV{CONTENT_TYPE}=~ /boundary="?([^";,]{5,})/) # multipart
        {
        my $boundary= "--" . $1;
        my $nl= "\x0d\x0a";


        for (split(/\Q$nl$boundary/, $buf))
        {
            if (/="([^"]+).*?(?:="([^"]+).*?:\s*([\w\-\.\/]+).*?)?$nl$nl(.*)/s)
            {
                if ($2 ne '') # file upload
                {
                    $file{$1}{'name'}= $1;
                    $file{$1}{'filename'}= $2;
                    $file{$1}{'mime'}= $3;
                    $file{$1}{'data'}= $4;


                } else { $req{$1}= $4; } # else form-data
            }
        }


        $buf= '';
        }
    }


    my($name, $value);
    my $qs= $ENV{QUERY_STRING};
    $qs=~ tr/+/ /; $buf=~ tr/+/ /;
    for (split(/&/, $qs),split(/&/, $buf),split(/;\s*/, $ENV{HTTP_COOKIE}))
    {
        ($name, $value)= split(/=/, $_, 2);
        $value=~ s/%(..)/pack('H2', $1)/ge;


        if ($name=~ /list/i){
            $req{$name}.= $req{$name} eq '' ? $value : "\0" . $value;
        } else {
            $req{$name}= $value;
        }
    }


}



ソースはすべて禁断のSJISで書いてしまいました。。。

まぁ一応はMVCなので文字化けしたところで修正は簡単なので動きを見るだけということで・・


環境変数REQUEST_METHODで場合分けせずに(POSTでURLパラメータ付加値もとれるように)、マルチパートにも対応、クッキー値もセットされるようにしてフォーム部品名にLISTが含まれていれば複数値をヌル文字つながりで取れるようにしてみました。(意外と実践的なデコードだと思います)



■「M」(M.pm)

package M;
#
# filename: M.pm
#
# memo: Model(演算)機能を受け持つ
#
use strict;
use constant FORM_START=> <<EOT;
<form action="$ENV{SCRIPT_NAME}" method="post" enctype="multipart/form-data">
<input type="hidden" name="act" value="%act%">
EOT


# コンストラクタ
sub new
{
    my $self= bless {
        errmsg => "",
    }, shift;


    return $self;
}
sub seterrmsg
{
    my $self= shift;


    $self->{errmsg} or $self->{errmsg}= $_[0];


    return;
}
sub body_toppage
{
    my $self= shift;


    return FORM_START . <<EOT;
<input type="submit" value="次へ">
</form>
EOT
}
sub body_next1
{
    my $self= shift;


    return FORM_START . <<EOT;
<input type="submit" value="次へ">
</form>
EOT
}
sub body_next2
{
    my $self= shift;


    return FORM_START . <<EOT;
<input type="submit" value="次へ">
</form>
EOT
}
sub body_next3
{
    my $self= shift;


    return FORM_START . <<EOT;
<input type="submit" value="次へ">
</form>
EOT
}


1;



「M」は前回同様、別モジュールとしてファイル分けしてクラス化しています。(値保持の必要があるので)


■テンプレート(common.tpl)

<html>
<head><title>%SITENAME%</title></head>
<body>

<h1>%TITLE%</h1>
<span style="color:red">%errmsg%</span>
<span style="color:black">%message%</span>
<br>
%body%


</body>
</html>



今回は簡易なテスト表示ということで手抜きのテンプレート1つしか用意しませんでした(common.tpl)が、実際にはデザイナーが作ったページに「$keywords%」を埋め込んでいきテンプレート化する流れになると思います。
その際には小分けされたテンプレートに置換用の「%act%」「%SITENAME%」「%TITLE%」などは確定要素なので書かずに値が直書きされる場合が多いと思います。またそのほうがデザインだけを見てどれ用のテンプレートか分かり易いと思います。


そうしない場合は、必要最小限のテンプレートデザインでとことんコンテンツ要素を置換させる、よりシンプルなテンプレートにするという方法もアリだと思います。



[表示結果] act=next1でもnext2でもnext3でもない場合

そろそろホンキ出す-mvc perl 01


[表示結果] act=next1

そろそろホンキ出す-mvc perl 02


[表示結果] act=next2

そろそろホンキ出す-mvc perl 03


[表示結果] act=next3

そろそろホンキ出す-mvc perl 04


先日作ったMBR読み出しプログラムを改造して、セクタを順送りで見ていけるようにしてみて適当にダンプデータを眺めてみました。


するとありましたよNTFSの持つMFTと呼ばれるファイル管理領域が・・・
でもまだ意味不明な部分が大半なので落ち着け自分



あれやこれや調べて一応分かったことは、ファイル情報はMFT領域において1ファイルにつき1KB単位で管理されていて、先頭16KBはメタファイル情報の領域らしい。


メタファイルのファイル名の先頭は「$」で始まるとのこと


以下のように1KBのファイル管理領域にさしかかると先頭に「FILE*」という文字列が現れるので分かり易い。




■セクタ読み出し

Drive1 - Sector257 reading...
0000 46 49 4C 45 2A 00 00 00 00 00 00 00 00 00 00 00 FILE*...........
0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0150 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF 03 ................



「0160-0170」番地辺りにファイル名の情報があることは目視で確認できた。
が、よりによってUNICODEだから「image.jpg」というファイル名だと「\0i\0m\0a\0g\0e\0.\0j\0p\0g」という具合だ。


で、ファイル名を取得するまではたやすいものの、問題は中身データが一体どこにあるのやらということ。。。


ちょいと調べてみたところ、DeviceIoControlというAPI関数に「FSCTL_GET_NTFS_FILE_RECORD」を指定すればその情報的なものが取得できるとかできないとか


C言語があればなんでもできる、C言語があればMBRだって読める



ということで・・・



読んでみましょうMBR(マスターブートレコード ディスクドライブの先頭512バイト)


なんかニワカ知識じゃディスクデータが破損するような気がして怖いですが、やってみましょうみせましょう。。。



そもそもWindows上でC言語やりだした理由が、前PCで使ってたハードディスクが読み取れない(認識はしてるのにダイナミックディスクのために読み取り不能)のでディスクを解析して閉じ込められたファイル達を救出しようという思いから!



これはその第一歩! 人類にとっては小さな一歩だが、私にとっては大きな一歩であ~る。



表示はWindows2000付属のdiskprobe16進ダンプ風にいきましょう。
ダイナミックディスクが読み取り無効なため、当然ドライブレターが割り振られてないので論理ドライブでのアクセスは不可能になるので、物理ドライブでのアクセスが必要になってくる。


幸いWin32APIには物理ドライブにアクセスできる低レベルな方法が用意されていました。(CreateFile関数)


物理ドライブは「\\.\physicaldrive0」~「\\.\physicaldriveN」の順にアクセスできて、論理ドライブなら「\\.\C」のようなアクセスになる。




■ソースコード(readmbr.c)
#include <stdio.h>
#include <windows.h>
#define MAX_DRIVE 10


int main(void)
{
    HANDLE hDrive;
    int i,j;
    char drivename[30];
    unsigned char buf[512];
    unsigned long readsize;



    // 物理ドライブ\\.\physicaldrive0から順に読み取る
    for (i=0; i<=MAX_DRIVE; i++){


        sprintf(drivename, "\\\\.\\physicaldrive%d", i);
        hDrive= CreateFile(drivename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);


        // 認識できるドライブがなければ終了
        if (hDrive == INVALID_HANDLE_VALUE) break;


        printf("Drive%d reading...\n", i);


        // bufに読み込む
        ReadFile(hDrive, buf, sizeof(buf), &readsize, NULL);
        //buf[readsize]= '\0';

        // ドライブ読み取りハンドルを閉じる
        CloseHandle(hDrive);


        // 16進ダンプ方式で表示
        for (j=0; j<readsize; j++){
            if (j % 16 == 0) printf("%04X", j);
            printf(" %02X", buf[j]);
            if (j % 16 == 15) putchar('\n');
        }
        putchar('\n');
    }


    return 0;
}



上記ソースをコンパイル(gcc readmbr.c -o readmbr.exe)して実行してみます。



[表示結果]
C:\Users\Administrator\Desktop\cwin>readmbr
Drive0 reading...
0000 33 C0 8E D0 BC 00 7C 8E C0 8E D8 BE 00 7C BF 00
0010 06 B9 00 02 FC F3 A4 50 68 1C 06 CB FB B9 04 00
0020 BD BE 07 80 7E 00 00 7C 0B 0F 85 10 01 83 C5 10
0030 E2 F1 CD 18 88 56 00 55 C6 46 11 05 C6 46 10 00
0040 B4 41 BB AA 55 CD 13 5D 72 0F 81 FB 55 AA 75 09
0050 F7 C1 01 00 74 03 FE 46 10 66 60 80 7E 10 00 74
0060 26 66 68 00 00 00 00 66 FF 76 08 68 00 00 68 00
0070 7C 68 01 00 68 10 00 B4 42 8A 56 00 8B F4 CD 13
0080 9F 83 C4 10 9E EB 14 B8 01 02 BB 00 7C 8A 56 00
0090 8A 76 01 8A 4E 02 8A 6E 03 CD 13 66 61 73 1E FE
00A0 4E 11 0F 85 0C 00 80 7E 00 80 0F 84 8A 00 B2 80
00B0 EB 82 55 32 E4 8A 56 00 CD 13 5D EB 9C 81 3E FE
00C0 7D 55 AA 75 6E FF 76 00 E8 8A 00 0F 85 15 00 B0
00D0 D1 E6 64 E8 7F 00 B0 DF E6 60 E8 78 00 B0 FF E6
00E0 64 E8 71 00 B8 00 BB CD 1A 66 23 C0 75 3B 66 81
00F0 FB 54 43 50 41 75 32 81 F9 02 01 72 2C 66 68 07
0100 BB 00 00 66 68 00 02 00 00 66 68 08 00 00 00 66
0110 53 66 53 66 55 66 68 00 00 00 00 66 68 00 7C 00
0120 00 66 61 68 00 00 07 CD 1A 5A 32 F6 EA 00 7C 00
0130 00 CD 18 A0 B7 07 EB 08 A0 B6 07 EB 03 A0 B5 07
0140 32 E4 05 00 07 8B F0 AC 3C 00 74 FC BB 07 00 B4
0150 0E CD 10 EB F2 2B C9 E4 64 EB 00 24 02 E0 F8 24
0160 02 C3 49 6E 76 61 6C 69 64 20 70 61 72 74 69 74
0170 69 6F 6E 20 74 61 62 6C 65 00 45 72 72 6F 72 20
0180 6C 6F 61 64 69 6E 67 20 6F 70 65 72 61 74 69 6E
0190 67 20 73 79 73 74 65 6D 00 4D 69 73 73 69 6E 67
01A0 20 6F 70 65 72 61 74 69 6E 67 20 73 79 73 74 65
01B0 6D 00 00 00 00 62 7A 99 D3 B2 7B 98 00 00 80 20
01C0 21 00 07 FE FF FF 00 08 00 00 41 44 38 3A 00 00
01D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA

Drive1 reading...
0000 33 C0 8E D0 BC 00 7C FB 50 07 50 1F FC BE 1B 7C
0010 BF 1B 06 50 57 B9 E5 01 F3 A4 CB BD BE 07 B1 04
0020 38 6E 00 7C 09 75 13 83 C5 10 E2 F4 CD 18 8B F5
0030 83 C6 10 49 74 19 38 2C 74 F6 A0 B5 07 B4 07 8B
0040 F0 AC 3C 00 74 FC BB 07 00 B4 0E CD 10 EB F2 88
0050 4E 10 E8 46 00 73 2A FE 46 10 80 7E 04 0B 74 0B
0060 80 7E 04 0C 74 05 A0 B6 07 75 D2 80 46 02 06 83
0070 46 08 06 83 56 0A 00 E8 21 00 73 05 A0 B6 07 EB
0080 BC 81 3E FE 7D 55 AA 74 0B 80 7E 10 00 74 C8 A0
0090 B7 07 EB A9 8B FC 1E 57 8B F5 CB BF 05 00 8A 56
00A0 00 B4 08 CD 13 72 23 8A C1 24 3F 98 8A DE 8A FC
00B0 43 F7 E3 8B D1 86 D6 B1 06 D2 EE 42 F7 E2 39 56
00C0 0A 77 23 72 05 39 46 08 73 1C B8 01 02 BB 00 7C
00D0 8B 4E 02 8B 56 00 CD 13 73 51 4F 74 4E 32 E4 8A
00E0 56 00 CD 13 EB E4 8A 56 00 60 BB AA 55 B4 41 CD
00F0 13 72 36 81 FB 55 AA 75 30 F6 C1 01 74 2B 61 60
0100 6A 00 6A 00 FF 76 0A FF 76 08 6A 00 68 00 7C 6A
0110 01 6A 10 B4 42 8B F4 CD 13 61 61 73 0E 4F 74 0B
0120 32 E4 8A 56 00 CD 13 EB D6 61 F9 C3 49 6E 76 61
0130 6C 69 64 20 70 61 72 74 69 74 69 6F 6E 20 74 61
0140 62 6C 65 00 45 72 72 6F 72 20 6C 6F 61 64 69 6E
0150 67 20 6F 70 65 72 61 74 69 6E 67 20 73 79 73 74
0160 65 6D 00 4D 69 73 73 69 6E 67 20 6F 70 65 72 61
0170 74 69 6E 67 20 73 79 73 74 65 6D 00 00 00 00 00
0180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01B0 00 00 00 00 00 2C 44 63 CA 1F FE 1E 00 00 00 01
01C0 01 00 42 FE FF FF 3F 00 00 00 C6 EA FF 0F 00 00
01D0 C1 FF 42 FE FF FF 05 EB FF 0F BC EB 42 15 00 00
01E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA


Hit any key to close this window...


物理ドライブは2つ搭載してるので一応想定どおりに動いてくれました。