デコード2

 昨日の記事で書いたデコーダをもうちょっと簡単に出来ないものかと思い、ちょっと違うものを書いてみた。

!String methodsFor: 'converting'!

urlDecode
    | inData str |

    str := ReadWriteStream on: ''.
    inData := self copyReplaceAll: '+' with: '%20'.
    inData := (inData tokenize: '%').

    ((inData at: 1) ~= '' or: [(inData at: 1) = ''])
        ifTrue: [
            str nextPutAll: (inData at: 1).
            inData := inData copyFrom: 2 to: inData size.
        ].

    inData do: [:each |
        | asciiVal |
        asciiVal := (each at: 1) asUppercase digitValue * 16 +
                    (each at: 2) asUppercase digitValue.

        asciiVal > 255 ifTrue: [^self].
            str nextPut: (Character value: asciiVal).

        (2 < each size)
            ifTrue: [str nextPutAll: (each copyFrom: 3 to: each size)].
    ].

    ^str contents.
!

ちょっと簡単になった? けど、遅そうだな・・・ 計測してみる。

#!/usr/bin/env gst

| str time |
str := '大量のURLエンコード文字列'.

time := Time millisecondsToRun: [
    str urlDecode.
].
time printNl.

予想通り、めっちゃ遅いorz 昨日のやつの方が約2倍は早い。他に何か良い方法はないかなぁ。
 まぁ、なにわともあれデコード出来るようになったので、テスト的にメールフォームCGIなんぞを作ってみる。

<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>てすとmailフォーム</title>
</head>
<body>
<form action="sendMail.cgi" method="POST" accept-charset="UTF-8">
  お名前<br>
  <input name="name" size="30"><br>
  メールアドレス<br>
  <input name="email" size="30"><br>
  メッセージ<br>
  <textarea name="message" rows="10" cols="30"></textarea><br>
  <input type="submit" value="送信">
  <input type="reset" value="リセット">
</form>
</body>
</html>

sendMail.cgi

#!/usr/bin/env gst -Q

stdout nextPutAll: 'Content-Type: text/plain; charset=UTF-8';nl;nl;
nextPutAll: 'ありがとうございます^^'.

PackageLoader fileInPackage: 'NetClients'.
!

| inData message client |
inData := ((stdin nextAvailable: 
    (Smalltalk getenv: 'CONTENT_LENGTH') asInteger) tokenize: '&')
    inject: Dictionary new
    into: [:dic :each |
        | array |
        array := (each tokenize: '=').
        dic at: (array at: 1)
            put: (array at: 2) urlDecode; yourself
    ].

message := NetClients.MIME.MimeEntity readFrom:
('From: %1
To: %2
Subject: %3

[%4]さんからメッセージ
%5
' % {inData at: 'email'.
    'user@hoge.domain'.
    'Message'.
    inData at: 'name'.
    inData at: 'message'}
) readStream.

client := NetClients.SMTP.SMTPClient connectToHost: 'localhost'.
[client sendMessage: message]
    ensure: [client close].
!

 本当はもっとちゃんとエラー処理とかやらなきゃならないけど、まぁ、テストなんで、こんな感じでOK.メールも問題無く送信できた。SMTPライブラリは素のままだと使い勝手が良いとは言えないので、何かラップした方がよさそう。あと、このライブラリをCGIで使う場合、 smalltalk/net/SMTP.st の139行目あたりの aMessage inspect.コメントアウトしとかないといけない。でないと、inspectの結果がstdoutに出力されてしまう。デバッグ用には良いですけどね。さらに、 smalltalk/kernel/Metaclass.st の181行目と335行目あたりの Transcript nextPutAll: 'Recompiling classes...'; nl.コメントアウトしとかないと、 PackageLoader fileInPackage: でパッケージをfileinしたときに Recompiling classes... がstdoutに出力される時がある。自分は最初その事に気付かなくて、原因不明のBad headerエラーが出るなぁとか思ったりしてたんすけど、原因はこれだった。^^;