79 |
を書くつもりでしたが、なんだかごちゃごちゃしていて、 |
を書くつもりでしたが、なんだかごちゃごちゃしていて、 |
80 |
それなら車輪の再発明になっても一から書いてみようと考えました。</p> |
それなら車輪の再発明になっても一から書いてみようと考えました。</p> |
81 |
|
|
82 |
<h2>実装状況</h2> |
<h2>特色 (という程のものでもない。)</h2> |
83 |
|
|
84 |
<ol> |
<ol> |
85 |
<li>RFC 822, RFC 2822 の頭領域 (<code class="bnf rfc2822">header</code> <code class="rfc2822">field</code>) を解釈出来ます。</li> |
<li>結構(謎)オブジェクト指向です。</li> |
86 |
<li>電子ニュース (<a href="urn:ietf:rfc:1036">RFC 1036</a> など), MIME, その他の追加頭領域の幾つかを解釈出来ます。</li> |
<li>RFC 822/2822 の <code class="bnf rfc2822">group</code> を解釈出来ます。</li> |
|
<li>RFC 822/2822 の <code class="bnf rfc2822">group</code> なメイル・アドレスの領域内容を解釈出来ます。</li> |
|
|
<li>日付形式では RFC 822/<a href="urn:ietf:rfc:1123">1123</a>, <a href="urn:ietf:rfc:733">RFC 733</a>, asctime, ISO 8601 (HTTP) などに対応。他の用途に転用出来ます。</li> |
|
87 |
<li><a href="urn:ietf:id:draft-ietf-usefor-msg-id-alt-00">draft-ietf-usefor-msg-id-alt-00</a> に基づいた送信アドレスなどによる <code class="rfc2822">Message-ID</code> を生成出来ます。</li> |
<li><a href="urn:ietf:id:draft-ietf-usefor-msg-id-alt-00">draft-ietf-usefor-msg-id-alt-00</a> に基づいた送信アドレスなどによる <code class="rfc2822">Message-ID</code> を生成出来ます。</li> |
88 |
<li>まだ解釈出来ない構造化 (<code class="rfc2822">structured</code>) 頭領域について、表示のために <code class="bnf rfc2822">quoted-pair</code> を unquote して値を返すなど出来ます。</li> |
<li>文字コード独立 (CSI) です。 (但し RFC 822 である都合上(謎)、 |
89 |
<li>MIME 本体 (<code class="bnf rfc822">body</code>) にはまだ対応していません。 (<code class="media-type">text/plain</code> <code class="mime-cte">8bit</code> 固定)</li> |
ASCII 互換である必要はあります。 EBCDIC とかは無理です:-<)</li> |
90 |
<li>説明はまだ不備です。各モジュールに pod で説明が入っていますが、 |
</ol> |
91 |
抜けていたり実態に合っていなかったりもします。 |
|
92 |
今の段階では code そのものが簡単に理解出来るとは思いますが。</li> |
<h2>各仕様への対応状況</h2> |
93 |
|
|
94 |
|
<ol> |
95 |
|
<li>電子メイルのメッセージ (RFC 822, RFC 2822) |
96 |
|
の全機能に (抜けが無ければ) 対応しています。 |
97 |
|
但し長さ制限などはチェックしていません。 (MIME の |
98 |
|
<code class="mime">Content-Transfer-Encoding</code> |
99 |
|
と一緒に実装予定)</li> |
100 |
|
<li>電子ニュース記事 (<a href="/uri-res/N2L?urn:ietf:rfc:1036">RFC 1036</a>, |
101 |
|
<a href="spec/son-of-RFC1036">son-of-RFC1036</a>, |
102 |
|
<a href="/uri-res/N2L?urn:ietf:id:draft-usefor-article-06"> |
103 |
|
draft-usefor-article (06)</a>) の頭領域の多くに対応しています。</li> |
104 |
|
<li>MIME の本体部分 (body part) にはまだ対応していません。</li> |
105 |
|
<li>MIME の追加頭領域 |
106 |
|
(<a href="/uri-res/N2L?urn:ietf:rfc:2045">RFC 2045</a>, |
107 |
|
<code class="mime">Content-Disposition</code>) に対応しています。 |
108 |
|
パラメーター値拡張 (<a href="/uri-res/N2L?urn:ietf:rfc:2231">RFC 2231</a>) |
109 |
|
も入出力ともに実装しました。</li> |
110 |
|
<li>MIME 符号化語 (<code class="mime bnf">encoded-word</code>) |
111 |
|
の解読に対応しています:-) 但し別途変換処理を指定する必要があります。 |
112 |
|
(<a href="#code">文字コードの扱い</a>参照)</li> |
113 |
|
<li>HTTP/1.0, HTTP/1.1, CGI/1.1, CGI/1.2 の頭領域のうち、 |
114 |
|
ごく一部に対応しています。 MHTML の |
115 |
|
<code class="mime">Content-Location</code> にも対応しています。</li> |
116 |
|
<li>日付形式では RFC 822/<a href="urn:ietf:rfc:1123">1123</a>, |
117 |
|
<a href="urn:ietf:rfc:733">RFC 733</a>, asctime, ISO 8601 (HTML) |
118 |
|
などに対応しています。</li> |
119 |
|
</ol> |
120 |
|
|
121 |
|
<h2>制限事項</h2> |
122 |
|
|
123 |
|
<ol> |
124 |
|
<li>類似モジュール(謎)のように、ファイル名やファイル・ハンドルを |
125 |
|
渡して読み込ませることが出来ません。</li> |
126 |
|
<li>大きなメッセージでも一気に読み込み、全て主記憶領域で |
127 |
|
保持しています。ですからあまり大きなメッセージの処理には |
128 |
|
向いていないでしょう。</li> |
129 |
|
<li><code>CR</code> や <code>LF</code> が単体で出現する場合、 |
130 |
|
正しく処理出来ません。 (<code>CRLF</code> と等価とみなします。) |
131 |
|
将来の版ではオプションで制御可能になるかもしれません。</li> |
132 |
|
<li>あったら良さそうな機能が未実装かもしれません。 |
133 |
|
(<a href="mailto:w@suika.fam.cx">電子メイル</a>などで教えて下さい。)</li> |
134 |
|
<li>各モジュールのオプション体系があまり整備されていません。 |
135 |
|
(それでも気持ち悪くない程度には体系的だと思います。)</li> |
136 |
|
<li>説明文 (document) が良い加減です。</li> |
137 |
</ol> |
</ol> |
138 |
|
|
139 |
<h2>今後の予定</h2> |
<h2>今後の予定</h2> |
140 |
|
|
141 |
<ol> |
<ol> |
142 |
<li>電子ニュースの頭領域 (RFC 1036, <a href="spec/son-of-RFC1036">son-of-RFC1036</a>, draft-usefor-article) の実装</li> |
<li>電子ニュースの頭領域 (RFC 1036, |
143 |
<li>MIME の頭領域の実装。</li> |
<a href="spec/son-of-RFC1036">son-of-RFC1036</a>, |
144 |
|
draft-usefor-article) の完全実装</li> |
145 |
|
<li><del>MIME の頭領域の実装。</del></li> |
146 |
<li>追加/非標準の頭領域の実装。</li> |
<li>追加/非標準の頭領域の実装。</li> |
147 |
<li>MIME 本体 (<code class="bnf rfc822">body</code>) の実装。</li> |
<li>MIME 本体 (<code class="bnf rfc822">body</code>) の実装。</li> |
148 |
<li>文字符号変換のための hook の実装?</li> |
<li><del>文字符号変換のための hook の実装?</del></li> |
149 |
<li>documentation。</li> |
<li>documentation。</li> |
150 |
<li>使用例の作成。</li> |
<li>使用例の作成。</li> |
151 |
</ol> |
</ol> |
152 |
|
|
153 |
|
<h2>必要環境</h2> |
154 |
|
|
155 |
|
<ol> |
156 |
|
<li>perl (Perl 5.6 以降または<span title="human parser">人間解析者</span>:-)) |
157 |
|
<p class="note"><code class="bnf rfc822">comment</code> |
158 |
|
を表すのに正規表現 <code class="regex">(??{ <var>code</var> })</code> |
159 |
|
を使っているので、これを解釈出来る、 |
160 |
|
5.6 以降の版である必要があります。</p> |
161 |
|
</li> |
162 |
|
<li>Digest::MD2, Digest::MD5, Digest::SHA1 |
163 |
|
<p>Message-ID の生成にこれらを使用する場合のみ、 |
164 |
|
<code>Message::Field::MsgID::MsgID</code> が使います。</p> |
165 |
|
<p>これらが用意されていない環境ではエラーになるので、 |
166 |
|
(現状では) 上記モジュールの該当部分を書き換えて対処して下さい。</p> |
167 |
|
</li> |
168 |
|
<li>文字コード変換処理 |
169 |
|
<p>日本語メッセージを扱うなら必須でしょう。 |
170 |
|
詳しくは<a href="#code">文字コードの扱い</a> |
171 |
|
の章をご参照下さい。</p> |
172 |
|
</li> |
173 |
|
</ol> |
174 |
|
|
175 |
<h2>入手</h2> |
<h2>入手</h2> |
176 |
|
|
177 |
<p>suika.fam.cx の SSH account をお持ちの場合、 CVS から入手出来ます。</p> |
<p>suika.fam.cx の SSH account をお持ちの場合、 CVS から入手出来ます。</p> |
191 |
<li><a href="spec/">関連する仕様書 (RFC, Internet-Draft 等)</a></li> |
<li><a href="spec/">関連する仕様書 (RFC, Internet-Draft 等)</a></li> |
192 |
</ul> |
</ul> |
193 |
|
|
194 |
|
<h2 id="code">文字コードの扱い</h2> |
195 |
|
|
196 |
|
<p>卑しいことで頭を悩ますのは嫌なので(藁)、 |
197 |
|
Message::* は符号化方法独立 (CSI) を目指して実装しています。 |
198 |
|
(但し ASCII のしがらみだけは断ち切っていません:-)) |
199 |
|
0x00 〜 0x7F が ASCII (または ASCII と見なして良いもの) である |
200 |
|
場合は、 Message::* を通したことでデータが壊れることは |
201 |
|
無いと思います。</p> |
202 |
|
|
203 |
|
<p>(もちろん、 RFC 822 など各仕様に照らして正統(的)で |
204 |
|
ある必要があります。 <code class="bnf rfc822">atom</code> |
205 |
|
に8ビット・コードが含まれていると正しく扱えません。) |
206 |
|
(早い話が、 <code class="bnf rfc822">quoted-string</code> |
207 |
|
などでは8ビット透過だということです。回りくどくてごめんなさい。)</p> |
208 |
|
|
209 |
|
<p>既定の状態では文字コードに関係する変換処理は行われません。 |
210 |
|
しかし、フック関数っぽいもの(謎)を指定することで、 |
211 |
|
変換処理をさせられます。</p> |
212 |
|
|
213 |
|
<p>指定出来るフック関数っぽいものは2種類です。 |
214 |
|
<code>DECODER</code> は、元のメッセージを解析する時 |
215 |
|
(<code class="perl">parse ()</code>) に適宜呼び出されます。 |
216 |
|
<code>ENCODER</code> は、メッセージとして文字列化する際 |
217 |
|
(<code class="perl">stringify ()</code> など) に適宜呼び出されます。</p> |
218 |
|
|
219 |
|
<p>これらの関数は、当然、当該処理が呼び出される前に指定しておく |
220 |
|
必要があります。 |
221 |
|
<samp class="perl">Message::Entity->parse</samp> などする前に |
222 |
|
定義しておくと良いでしょう。</p> |
223 |
|
|
224 |
|
<pre class="example perl"> |
225 |
|
require Message::MIME::Charset; |
226 |
|
$Message::MIME::Charset::DECODER{'*default'} = sub {jcode::euc ($_[1])}; |
227 |
|
$Message::MIME::Charset::ENCODER{'*default'} = sub {jcode::jis ($_[1], 'euc')}; |
228 |
|
</pre> |
229 |
|
|
230 |
|
<p>この例では、 jcode.pl を変換処理に使います。 |
231 |
|
(もちろん、既に <code class="perl">require</code> |
232 |
|
されていると仮定しています。)</p> |
233 |
|
<p>最初の <code class="perl">require</code> で、変換処理を担当している |
234 |
|
<code class="perl">Message::MIME::Charset</code> を読み込みます。 |
235 |
|
(こうしておかないと、後から既定値 (= 無変換) で |
236 |
|
<code class="perl">*default</code> が上書きされてしまいます。)</p> |
237 |
|
|
238 |
|
<p>この code を使ったスクリプトは内部処理を日本語 EUC |
239 |
|
で行うと仮定しています。ですから、 <code class="perl">DECODER</code> |
240 |
|
で日本語 EUC に変換します。</p> |
241 |
|
<p>また、日本語メッセージでは <code>ISO-2022-JP</code> |
242 |
|
を使うのが慣習ですから、 <code class="perl">ENCODER</code> |
243 |
|
では 7ビット JIS に変換しています。</p> |
244 |
|
<p>処理を行う関数は、引数が2つ以上与えられます。 |
245 |
|
1つ目の引数は呼び出した class module, いわゆる |
246 |
|
<code class="perl">$self</code> です。(この場合 self ではありませんが:-) |
247 |
|
でも普通は必要ないでしょう。</p> |
248 |
|
<p>2つ目の引数は処理対象の文字列です。</p> |
249 |
|
<p>3つ目以降の引数は、追加オプションのハッシュです。 |
250 |
|
ただし、現在追加オプションは定義されていません。</p> |
251 |
|
<p>関数が返す値は(今のところ)一つだけです。 |
252 |
|
処理が終わった文字列です。変換結果として何もなくなってしまったら、 |
253 |
|
もちろん空文字列を返して構いません。 (<code class="perl">undef</code> |
254 |
|
よりも空文字列の方が望ましいでしょう。)</p> |
255 |
|
|
256 |
|
<p>さて、上記の例では「<code>*default</code>」の EN/DECODER |
257 |
|
を指定しましたが、ここには代わりに charset 名を指定出来ます。</p> |
258 |
|
|
259 |
|
<pre class="perl example"> |
260 |
|
$Message::MIME::Charset::DECODER{'iso-2022-jp'} = sub {jcode::euc ($_[1], 'jis')}; |
261 |
|
</pre> |
262 |
|
|
263 |
|
<p>ここでは、 <code>ISO-2022-JP</code> を内部コードに変換する |
264 |
|
方法を定義しています。 charset 名 (および「<code>*default</code>」 |
265 |
|
は必ず小文字で書いて下さい!)</p> |
266 |
|
<p>MIME body や、 encoded-word, RFC 2231 の拡張パラメーター値 |
267 |
|
など、 charset が指定されている時はその charset 名の変換関数が |
268 |
|
呼び出されます。 (指定された charset 名の変換関数が未定義の時は、 |
269 |
|
何も処理しません。) これ以外の場面では、 <code>*default</code> |
270 |
|
で定義された関数が使われます。</p> |
271 |
|
|
272 |
|
<p>最後に、日本語メッセージを扱う際の例を挙げておきます。</p> |
273 |
|
|
274 |
|
<pre class="example perl"> |
275 |
|
<span class="comment">## jcode.pl を使用</span> |
276 |
|
require 'jcode.pl'; |
277 |
|
require Message::MIME::Charset; |
278 |
|
$Message::MIME::Charset::DECODER{'*default'} = sub {jcode::euc ($_[1])}; |
279 |
|
$Message::MIME::Charset::DECODER{'iso-2022-jp'} = sub {jcode::euc ($_[1], 'jis')}; |
280 |
|
$Message::MIME::Charset::DECODER{'euc-jp'} = sub {$_[1]}; |
281 |
|
$Message::MIME::Charset::DECODER{'shift_jis'} = sub {jcode::euc ($_[1], 'sjis')}; |
282 |
|
$Message::MIME::Charset::ENCODER{'*default'} = sub { |
283 |
|
my $s = $_[1]; |
284 |
|
<span class="comment">## 正規化</span> |
285 |
|
jcode::tr(\$s, |
286 |
|
"\xa3\xb0-\xa3\xb9\xa3\xc1-\xa3\xda\xa3\xe1-\xa3\xfa\xa1\xf5". |
287 |
|
"\xa1\xa4\xa1\xa5\xa1\xa7\xa1\xa8\xa1\xa9\xa1\xaa\xa1\xae". |
288 |
|
"\xa1\xb0\xa1\xb2\xa1\xbf\xa1\xc3\xa1\xca\xa1\xcb\xa1\xce". |
289 |
|
"\xa1\xcf\xa1\xd0\xa1\xd1\xa1\xdc\xa1\xf0\xa1\xf3\xa1\xf4". |
290 |
|
"\xa1\xf6\xa1\xf7\xa1\xe1\xa2\xaf\xa2\xb0\xa2\xb2\xa2\xb1". |
291 |
|
"\xa1\xe4\xa1\xe3\xA1\xC0\xA1\xA1" |
292 |
|
=> '0-9A-Za-z&,.:;?!`^_/|()[]{}+$%#*@=\'"~-><\\ '); |
293 |
|
jcode::jis ($s, 'euc', 'z') |
294 |
|
}; |
295 |
|
</pre> |
296 |
|
|
297 |
|
<pre class="example perl"> |
298 |
|
<span class="comment">## Jcode.pm を使用</span> |
299 |
|
use Jcode; |
300 |
|
require Message::MIME::Charset; |
301 |
|
$Message::MIME::Charset::DECODER{'*default'} = sub {jcode::euc ($_[1])}; |
302 |
|
$Message::MIME::Charset::DECODER{'iso-2022-jp'} = sub {Jcode->new ($_[1], 'jis')->euc}; |
303 |
|
$Message::MIME::Charset::DECODER{'euc-jp'} = sub {$_[1]}; |
304 |
|
$Message::MIME::Charset::DECODER{'shift_jis'} = sub {Jcode->new ($_[1], 'sjis')->euc}; |
305 |
|
$Message::MIME::Charset::DECODER{'utf-8'} = sub {Jcode->new ($_[1], 'utf8')->euc}; |
306 |
|
$Message::MIME::Charset::ENCODER{'*default'} = sub {Jcode->new ($_[1], 'euc')->jis}; |
307 |
|
$Message::MIME::Charset::ENCODER{'utf-8'} = sub {Jcode->new ($_[1], 'euc')->utf8}; |
308 |
|
</pre> |
309 |
|
|
310 |
|
<p>Perl 5.8 で Encode モジュールが使えるようになれば、 |
311 |
|
もっと楽になると期待しています。</p> |
312 |
|
|
313 |
<div class="navigation"> |
<div class="navigation"> |
314 |
[<a href="/" title="このサーバーの首頁">/</a> |
[<a href="/" title="このサーバーの首頁">/</a> |
315 |
<a href="/map" title="このサーバーの案内" rel="index">地図</a> |
<a href="/map" title="このサーバーの案内" rel="index">地図</a> |