[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
rfc.mime
- MIMEメッセージ処理 RFC2045からRFC2049で定義されている、 多目的インターネットメール拡張(Multipurpose Internet Mail Extensions; MIME) メッセージを扱う便利な手続きです。
このモジュールは、rfc.822
モジュールと一緒に使うことを
想定しています(rfc.822
- RFC822メッセージ形式参照)。
MIME特有のヘッダフィールドをパーズしたり生成したりする便利な手続き。
fieldがそのMIMEバージョンのヘッダフィールドとして有効であれば、
そのメジャーバージョン番号とマイナーバージョン番号をリストにして
返します。そうでなければ、#f
を返します。
fieldには#f
を渡せるので、rfc822-header-ref
の
戻り値を直接渡すこともできます。rfc822-read-headers
により
返されるパーズ済みヘッダのリストを渡すことで、以下のように
MIMEのバージョンを得ることができます。(現在は、(1 0)
です。)
(mime-parse-version (rfc822-header-ref headers "mime-version")) |
注意: fieldはトークンの間にコメントを含むかもしれないので、
#/\d+\.\d+/
のような単純な正規表現では不十分です。
“content-type”ヘッダフィールドをパーズし、次のようなリストを 返します。
(type subtype (attribute . value) …) |
ここで、typeとsubtypeはそれぞれ、MIMEメディアタイプと サブタイプを文字列で表したものになります。
(mime-parse-content-type "text/html; charset=iso-2022-jp") ⇒ ("text" "html" ("charset" . "iso-2022-jp")) |
fieldが有効なcontent-typeフィールドでない場合は、
#f
が返ります。
RFC2047でエンコードされたwordをデコードします。 wordがRFC2047でエンコードされたものでない場合は、そのまま 返されます。
この手続きはword全体がRFC2047の規定する“encoded-word”である場合にのみ
デコードを行うことに注意してください。複数のエンコードされた部分や
エンコードされていない部分が混ざっているフィールドを扱う場合は、
下に示すmime-decode-text
を使います。
(mime-decode-word "=?iso-8859-1?q?this=20is=20some=20text?=") ⇒ "this is some text" |
text中に含まれるすべてのencoded wordをデコードした文字列を返します。 この手続きは、エンコードされていない部分とエンコードされている部分が混ざっていたり、 複数のエンコードされている部分を持つヘッダフィールドボディを処理することが できます。そのようなフィールドの例はemailのSubjectフィールドです。
(mime-decode-text "Yamada Taro (=?utf-8?B?5bGx55SwIOWkqumDjg==?=)") ⇒ "Yamada Taro (山田 太郎)" |
この手続きを「構造化された」ヘッダフィールドボディ (RFC2822 2.2.2節参照)
に適用する際には注意が必要です。
構造化されたヘッダフィールドボディをパーズする正式な方法は、
最初にトークンに分割して、それから各wordを
mime-decode-word
を使ってデコードするというものです。
なぜならデコード後のテキスト中に、パージングに影響を与える文字が含まれている
かもしれないからです。
(ただし、単に参考情報を人間にわかりやすいように表示するだけの目的の場合は、
簡便のためにヘッダフィールド全体をこの手続きで一度にデコードしてしまっても
良いでしょう)。
wordをRFC2047フォーマットにエンコードします。キーワード引数
charsetは文字列かシンボルで文字エンコーディングスキームを指定します。
デフォルトはutf-8
です。charsetの指定がGaucheの
内部文字エンコーディングと異なっており、wordが完全な文字列である場合は、
まずwordがcharsetのエンコーディングへと変換され、
その上でトランスファーエンコーディングがかけられます。
(mime-encode-word "this is some text") ⇒ "=?utf-8?B?dGhpcyBpcyBzb21lIHRleHQ=?=" |
キーワード引数transfer-encodingは各オクテットを伝達上安全な
文字列へどエンコードする方法を指定します。サポートされている値は、
Base64を指定するシンボルb
、B
、base64
、
およびQuoted printableを指定する
Q
、q
、quoted-printable
です。
これ以外の値を渡した場合はエラーが通知されます。デフォルトはBase64です。
この手続きは結果のencoded wordの長さを気にしませんが、
RFC2047によればencoded wordは75オクテットまでに収めることが
要請されています。この要請に対応するには下に示す
mime-encode-text
を使って下さい。
(註:ほとんどのGaucheの手続きでは、キーワード引数encoding
により
文字エンコーディングを指定します。しかしこの手続きの文脈では
2つの「エンコーディング」が存在しているので、混乱を避けるために
RFC文書で使われている“charset”および“transfer-encoding”の用語を
使うこととしました。)
textを、必要ならばRFC2047フォーマットに従いエンコードします。 また、結果が長すぎる場合の行の折り返しも考慮します。
キーワード引数charsetとtransfer-encodingの意味は
mime-encode-word
と同じです。
もしwordが印字可能なASCII文字のみで構成されていた場合は エンコーディングは行われず、行の折り返しのみが処理されます。 但し、force引数に真の値が与えられた場合はASCIIのみのtextも エンコードされます。
line-widthは結果に現れる行の最大値を指定します。デフォルトは76です。
encoded wordがこれを越える場合は、複数のencoded wordへと結果は分割され、
間にCR LF SPCシーケンス(RFC2822で定義される“folding white space”)が挿入されます。
line-widthに#f
か0
を渡すことで
行の折り返しを抑制することができます。
encoded wordには文字数でいくらかのオーバヘッドがあるため、
あまり小さいline-width
には意味がありません。現在の実装では
30以下の値は拒否されます。
start-columnキーワード引数は、ヘッダフィールド名を入れるために
エンコード結果の最初の行だけを短くするのに使えます。
例えばSubjectヘッダフィールドのボディをエンコードする際に、
(string-length "Subject: ")
の値を渡してやれば、
結果を直接"Subject: "の後に連結することができるわけです。
デフォルトの値は0です。
この手続きはstructured header fieldをエンコードするようには設計 されていません。structured header fieldには、どの部分がエンコード 可能でどの部分にfolding white spaceが挿入可能かについてさらなる 制約があるためです。安全な方法は、まず必要な部分をエンコードし、 それから折り返しを考慮しつつstructured header fieldを組み立てることです。
メッセージ全体が読み込まれる前にメッセージボディをどのように 扱うかをコントロールできるように、ストリームパーザが用意されて います。
基本的なストリームパーザです。portは、メッセージを読み込む
入力ポートです。headersはrfc822-read-headers
により
パーズされたヘッダのリストです。つまり、この手続きは、
portから読み込まれたメッセージのヘッダ部分がパーズされた
後に使われることを想定しています。
(let* ((headers (rfc822-read-headers port))) (if (mime-parse-version (rfc822-header-ref headers "mime-version")) ;; parse MIME message (mime-parse-message port headers handler) ;; retrieve a non-MIME body ...)) |
mime-parse-message
はheadersを解析し、
メッセージボディのそれぞれについて、2引数をもって
handlerを呼び出します。
(handler part-info xport) |
part-infoは、以下で説明するような、メッセージのこのパートの
情報をカプセル化した<mime-part>
ストラクチャです。
xportは入力ポートで、最初はメッセージボディの先頭を指しています。 ハンドラはこのポートからメッセージボディを読み込むことが出来ます。 xportはMIMEバウンダリを認識し、パートの最後に到達したら EOFを返します。 (元のportから直接読み込まないようにして下さい。 そうしてしまうと、vportの内部状態がおかしくなります)。
handlerは、パートをメモリに読み込んだり、ディスクに保存したり、 あるいはそのパートを無視したりできます。ただ、何をするにせよ、 vportがEOFを返すまでデータを読まなければなりません。
handlerの戻り値は、part-infoのcontent
スロットに
セットされます。
メッセージが、ネストしたマルチパートメッセージを含んでいる場合は、 handlerは深さ優先でそれぞれの“葉”のパートに対して呼ばれます。 handlerは、part-infoストラクチャを調べることで、 そのネストのレベルを知ることができます。
メッセージはマルチパートである必要はありません。メッセージが
MIME mesasge
タイプである場合は、handlerは囲まれたメッセージの
ボディに対して呼ばれます。メッセージが、text
やapplication
などの他のメディアタイプの場合は、handlerは単にメッセージボディに
対して呼ばれます。
MIMEパートのメタ情報を含むストラクチャです。 これは、そのパートのヘッダが読み込まれた時点で構築され、 そのパートのボディを読み込むハンドラに渡されます。
以下のスロットを持ちます。
MIMEメディアタイプの文字列。そのパートのcontent-type
ヘッダが
省略された場合は、適切なデフォルト値がセットされます。
MIMEメディアのサブタイプの文字列。そのパートのcontent-type
ヘッダが省略された場合は、適切なデフォルト値がセットされます。
content-type
ヘッダフィールドに渡されるパラメータの連想リスト。
content-transfer-encoding
ヘッダフィールドの値。
このヘッダフィールドが省略された場合は、適切なデフォルト値が
セットされます。
rfc822-read-headers
によりパーズされた、ヘッダフィールドのリスト。
それがマルチパートメッセージあるいはカプセル化されたメッセージの
パートである場合は、それを含んでいるパートの<mime-part>
ストラクチャを指します。そうでなければ#f
を返します。
同じ親を持つパートの中でのそのパートのシーケンス番号。
そのパートのメディアタイプがmultipart/*あるいはmessage/*で ある場合は、このスロットにはそれに含まれるパートのリストが 入っています。そうでなければ、handlerの戻り値が 格納されています。
メッセージボディを取得するための手続きです。
mime-parse-message
へ渡される、handlerの
ビルディングブロックとなることを意図しています。
part-infoは、<mime-part>
のオブジェクトです。
xportはハンドラに渡された入力ポートで、
そこからMIMEパートが読みこまれるものです。
この手続きは、xportからEOFに達するまで読み込み、
part-infoのtransfer-encoding
も見て、
ボディを適切にデコードします。つまり、base64やquoted-printable
のエンコーディングは適切に処理されます。結果が出力ポートoutpへと
出力されます。
この手続きは文字セットの変換は扱いません。
必要であれば、呼び出し側がoutpとしてCES変換ポートを
使う必要があります(gauche.charconv
- 文字コード変換参照)。
典型的なケースのために、いくつかの便利な手続きがmime-retrieve-body
の上に定義されています。
MIMEメッセージのボディを読み込み、転送(transfer)エンコーディングを デコードし、それぞれ文字列として返すか、ファイルへ書き出します。
MIMEメッセージパーザの最もシンプルな使い方は次のように なります。
(let ((headers (rfc822-read-headers port))) (mime-parse-message port headers (cut mime-body->string <> <>))) |
これは、メッセージの全てをメモリに読み込み、
一番上層の<mime-part>
オブジェクトを返します。
(“葉”である<mime-part>
オブジェクトのcontent
フィールドは、
そのパートのボディを文字列として保持しています。)
内容の転送エンコーディング(content transfer encoding)は認識され処理
されますが、文字セットの変換は行われません。
メッセージボディを直接ファイルに書き出したり、MIMEメディアタイプや 他のヘッダ情報に基づいていくつかのボディをスキップしたいかもしれません。 その場合は、ロジックをハンドラのクロージャに入れることができます。 それが、このモジュールが、オールインワンの手続きではなく、 ビルディングブロックを提供している理由です。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] |
This document was generated by Shiro Kawai on October, 7 2008 using texi2html 1.78.