CGI.pmでのファイルアップロード

ファイル名の取得でいろいろハマっているのでメモ。

```perl:
my $fh = $cgi->upload('file');
```

としてファイルアップロードのパラメータ名を指定するとファイル名が取得できるということですが、これは、あてにならず、

```
Content-Disposition: form-data; name="file"; filename="hoge\"hoge.pm";
```

の場合は、ファイル名は、`hoge`で切れてしまう。

なので、これをもとのヘッダから取得しようと、

```perl:
my $content_disposition = $cgi->uploadInfo($fh)->{'Content-Disposition'};
my ($filename) = $content_disposition =~ /filename\s*=\s*"(.+)"/;
```

これで、いいかなと思っていたが、RFC的には違うようで、

```
 filename*0*=iso-2022-jp'ja'%1B%24B%24%5B%244%24%5B%242%1B%28B;
  filename*1=.jpeg
```

というextended-parameter形式というUS-ASCHII以外の場合の拡張がRFC2231に規定されているそうです。[ここを参考](http://www.emaillab.org/essay/japanese-filename.html)

では、これに対応しようと試みたところで今回の疑問が。
CGI.pmのuploadInfo()で、取得されるヘッダは複数行のヘッダがとれていないのでは?と思っています。手元から、リクエストを生成し投げてみたところ、upload()メソッドでもファイルを取得することができませんでした。
以下がリクエストのスクリプト。

```perl:upload_req.pl
#!/usr/bin/perl

use strict;
use warnings;
use IO::Socket::INET;

local $|=1;
my $file = 'sample.txt';
open my $fh, $file
    or die $!;
my $content = do{local $/;<$fh>};
close $fh;
my $sock = IO::Socket::INET->new(
    PeerAddr    => 'hogehoge.com',
    PeerPort    => 80,
    Proto       => 'tcp'
) or die $@;

my $b = time . $$ . time;
my $part1 = qq{--$b\r\n};
$part1 .= qq{Content-Disposition: form-data; name="file"; filename*0*=iso-2022-jp'ja'%1B%24B%24%5B%244%24%5B%242%1B%28B\r\n};
$part1 .= qq{    filename*1=.txt\r\n};
$part1 .= qq{Content-Type: text/plain\r\n};
$part1 .= qq{\r\n};
$part1 .= qq{$content\r\n};
$part1 .= qq{--$b--\r\n};
$part1 .= qq{\r\n};

my $length = length $part1;
my $header = qq{POST /cgi_pm.cgi HTTP/1.1\r\n};
$header .= qq{Host:   hogehoge.com\r\n};
$header .= qq{User-Agent:  Mozilla/5.0\r\n};
$header .= qq{Content-Type:    multipart/form-data; boundary=$b\r\n};
$header .= qq{Content-Length:  $length\r\n};
$header .= qq{\r\n};

my $req = $header . $part1;
$sock->print($req);
while ( <$sock> ){
    print;
}
```

単一行で、

```
Content-Disposition: form-data; name="file"; filename="hoge.txt"
```

のようにするとContent-Dispositionも取れるのですが、複数行のfilenameフィールドには対応してないのかな?

CGI.pmの4017行目にheaderを読んでいるらしき箇所がありますが、このへんが関係しているのかな?これを見ると、複数行は考慮していないように見える..?

```
    # See RFC 2045 Appendix A and RFC 822 sections 3.4.8
    #   (Folding Long Header Fields), 3.4.3 (Comments)
    #   and 3.4.5 (Quoted-Strings).

    my $token = '[-\w!\#$%&\'*+.^_\`|{}~]';
    $header=~s/$CRLF\s+/ /og;		# merge continuation lines

    while ($header=~/($token+):\s+([^$CRLF]*)/mgox) {
        my ($field_name,$field_value) = ($1,$2);
	$field_name =~ s/\b(\w)/uc($1)/eg; #canonicalize
	$return{$field_name}=$field_value;
    }
    return %return;
```

どなたか詳しいい方教えて下さい。

####追記

rfc2616 19.5.1 Content-Disposition
こちらがhttpにおけるContent-Dispositionを記したもののようでした。
これによると、

```
filename-parm = "filename" "=" quoted-string
```

で複数行になることはないようです。

####追記

RFC2388ではファイル名が非ASCIIの場合は、RFC2231に従えとある。
CGI.pmでも、複数行の折りたたみ空白のヘッダはパースされ、1行となる。
上記のCGI.pmの

```perl:
$header=~s/$CRLF\s+/ /og;
```

ここは、CRLF+空白を除去して1行にしている場所であった。

そして改めてCGI.pmを見てみると、

```perl:
        # See RFC 1867, 2183, 2045
        # NB: File content will be loaded into memory should
        # content-disposition parsing fail.
        my ($filename) = $header{'Content-Disposition'}
                       =~/ filename=(("[^"]*")|([a-z\d!\#'\*\+,\.^_\`\{\}\|\~]*))/i;
```

Content-Disposition: form-dataでファイル送信時のパラメーターとしては、
filename=しか解釈されないようでした。
更に付け加えると、filename=として指定しないと、ファイルアップロードとして扱われない
ということでした。