rfc.822
- RFC822メッセージ形式 ¶電子メールを交換する際に使用されるテキストのフォーマットである、“インターネット・ メッセージ・フォーマット”をパーズ/生成する手続きを定義しています。 最新の仕様は、RFC5322にあります。 このフォーマットは最初 RFC 822 で定義されたため、未だに“RFC822形式”と 呼ばれています。それがこのモジュール名の由来です。 以下では、このフォーマットを“RFC822形式”と呼びます。
{rfc.822
}
入力ポート iport から、メッセージ・ヘッダの終わりに達するまで、
RFC822 形式のメッセージを読み込みます。
ヘッダ・フィールドは以下のフォーマットのリストに展開、分離されます。
((name body) ...)
Name … はフィールド名で、body … は対応するフィールドの ボディ、ともに文字列です。 フィールド名は小文字に変換されます。フィールドのボディは、行折り返しが 取り除かれる以外は変更されません。 フィールドの順番は保存されます。
デフォルトでは、パーザの動作は寛容です。ヘッダをパーズ中に EOF に 出会うとそれをメッセージの終端とみなします。継続(折り返し)行でもなく、 新しいヘッダフィールドの始端でもない行は無視します。このふるまいは キーワード引数 strict? に真の値を渡すことで変更することができます。 真を渡すと、このような不正な形式のヘッダに対してエラーを発生させるように なります。
キーワード引数 reader は iport から一行読み込む手続きを
とります。デフォルトは read-line
です。ほとんどの場合これで
十分のはずです。
{rfc.822
}
Deprecated.
これはrfc822-read-headers
の古い名前です。
互換性のために残してありますが、新しいコードは
rfc822-read-headers
を使って下さい。
{rfc.822
}
rfc822-read-headers
が返すパーズ済みのヘッダリストから
特定のフィールドを得るためのユーティリティ手続きです。
Field-name は小文字の文字列でフィールド名を指定します。
与えられた名前をもつフィールドが header-list 中にあれば、
その値を文字列で返します。そうでない場合、もし default が
与えられていればそれが返り、与えられていなければ #f
が返されます。
もし同じフィールド名を持つヘッダエントリが複数あった場合は、
最初のエントリの値が返されます。複数ヘッダエントリの全ての値を取りたい場合は
下のrfc822-header-ref*
を使ってください。
この手続きはrfc822-read-headers
の結果だけでなく、
文字列をキーにしたリストのリスト ((name value option ...) ...)
という構造からvalue部分を取り出すのに使えます。
例えばparse-cookie-string
の結果をrfc-822-header-ref
に渡せます。
(parse-cookie-string
についてはsee rfc.cookie
- HTTPクッキー参照。)
(rfc822-header-ref '(("from" "foo@example.com") ("to" "bar@example.com")) "from") ⇒ "foo@example.com" ;; If no entry matches, #f is returned by default (rfc822-header-ref '(("from" "foo@example.com") ("to" "bar@example.com")) "reply-to") ⇒ #f ;; You can give the default value for no-match case (rfc822-header-ref '(("from" "foo@example.com") ("to" "bar@example.com")) "reply-to" 'none) ⇒ none ;; By giving the default value, you can distinguish ;; the no-match case and there's actually an entry with value #f. (rfc822-header-ref '(("from" "foo@example.com") ("reply-to" #f)) "reply-to" 'none) ⇒ #f
rfc822-header-ref
のようにheader-listから
field-nameを持つヘッダを探しますが、最初に見つかったものだけでなく
field-nameを持つ全てのヘッダの値をリストにして返します。
指定の名前を持つヘッダが無ければ空リストが返されます。
RFC822ヘッダリストheader-listに、名前field-nameと値 field-valueを持つヘッダを追加したヘッダリストを返します。 field-nameは全て小文字に変換されます。 header-listが既に同名のヘッダを持っていた場合、それらは全て出力から除かれます。 引数に渡したheader-list自体は変更されません。
RFC2822メッセージの「構造化」されたヘッダフィールドをパーズするために、
いくつかの手続きが提供されています。これらの手続きはヘッダフィールドの
本体部を処理します。たとえば、ヘッダフィールドが、
"To: Wandering Schemer <schemer@example.com>
" であれば、これらの
手続きは "Wandering Schemer <schemer@example.com>
" をパーズします。
ほとんどの手続きは入力ポートを引数にとります。通常は最初に、ヘッダフィールド
全部を rfc822-read-headers
でパーズし、ヘッダの本体を
rfc822-header-ref
で取得してから、その本体用に入力文字列ポートを
オープンして、それをこれらの手続きを用いてパーズします。
このように複雑になっているのは、フィールドのタイプによって別々の トークン化スキームが必要になるからです。RFC2822 では多くの場合 トークン間にコメントがあらわれことを許しているので、初心な正規表現では うまくいきません。RFC2822 のコメントはネスト可能で、正規表現では表現 しきれないからです。 そういうわけで、このレイヤの手続きは、いろいろな構文に対応できるよう 十分な柔軟性があるように設計されています。標準的なタイプのヘッダについては 高水準のパーザも提供されています。後述の「特定フィールド用パーザ」を 参照してください。
{rfc.822
}
基本的なトークナイザです。まず、もしあれば、白空白および/または
コメント (CFWS
) を iport から読み飛ばします。それから、
tokenizer-specs にしたがってトークンをひとつ読み込みます。
トークンを読み込む前に、iport が EOF に到達したら、EOF が
返されます。
tokenizer-specs はトークナイザ仕様のリストです。 トークナイザ仕様は、文字集合または文字集合と手続きのペアのどちらかです。
CFWS
を読み飛ばしたあと、この手続きは iport の先頭の一文字
を見て、tokenizer-specs のひとつひとつに対してチェックします。
その文字が含まれている文字集合がみつかれば、トークンを次のようにして
引き出します。トークナイザ仕様が文字集合だけの場合、その文字集合に
属している文字の並びがトークンを構成します。トークナイザ仕様が文字集合と
手続きのペアだったら、その手続きを iport とともに呼びだし、
トークンを読み込みます。
もし、先頭の文字がどの文字集合ともマッチしなければ、その文字が iport から取り出され、それが返されます。
デフォルトの tokenizer-specs は以下のようになっています。
(list (cons #["] rfc822-quoted-string) (cons *rfc822-atext-chars* rfc822-dot-atom))
ここで rfc822-quoted-string
および rfc822-dot-atom
は
後述するトークナイザ手続きで、*rfc822-atext-chars*
は RFC2822 で
規定された atext
の文字集合に束縛されています。
つまり、rfc822-next-token
はデフォルトでは RFC2822 で規定された
quoted-string
あるいは dot-atom
のトークンを引き出します。
tokenizer-specs をつかって、ヘッダフィールドのパーズ方法を
カスタマイズすることができます。たとえば、(1) 英字で構成された単語、または
(2) クウォート文字列、のトークンを取り出したいときには、
rfc822-next-token
をこんなふうに呼べます。
(rfc822-next-token iport `(#[[:alpha:]] (#["] . ,rfc822-quoted-string)))
{rfc.822
}
これは便利関数です。フィールド本体 field に対応する入力文字列ポート
を生成し、それに対して、rfc822-next-token
を全入力を消費するまで、
繰り返しよび、トークンのリストを返します。Tokenizer-specs は、
rfc822-next-token
に渡されます。
{rfc.822
}
iport から、すべてのコメントおよび/または白空白文字を消費し、
白空白でもコメントでもない、先頭の文字を返します。返された文字は、
iportに残ります。
{rfc.822
}
atom
を構成する有効な文字集合に束縛されています。
{rfc.822
}
デフォルトの tokenizer-specs に束縛されています。
{rfc.822
}
それぞれ、atom
、dot-atom
および quoted-string
に
対応するトークナイザです。quoted-string
中の二重引用符および
エスケープのためのバックスラッシュは rfc822-quoted-string
に
よって取り除かれます。
{rfc.822
}
RFC822 形式の日付文字列を取り、8つの値を返します。
year, month, day-of-month, hour, minutes, seconds, timezone, day-of-week.
timezone は UT(グリニッジ標準時)からの分単位のオフセットです。 day-of-week は日曜日から数えた曜日で、情報が不足している場合は #f です。 monthは1から12までの整数です。 文字列がパーズ不可能ならば、全ての要素が #f になります。
{rfc.822
}
RFC822形式の日付フォーマットをパーズし、SRFI-19 の <date>
オブジェクト
(日付 参照) を返します。string がパーズできないときは
かわりに #f
を返します。
SRFI-19の日付からRFC822形式の日付文字列を作成するには、
後で述べるdate->rfc822-date
が使えます。
{rfc.822
}
これはrfc822-read-headers
の一種の逆関数と言えます。
各ヘッダデータが(<name> <body>)
の形になっているリストを受け取り、
outputキーワード引数で指定されるポートにRFC822形式で書き出します。
outputが省略された場合は現在の出力ポートが使われます。
デフォルトでは、この手続きはheaderで全てのヘッダが完結するものと みなし、最後にヘッダ部分の終了を示す空行を出力します。 ただしcontinueキーワード引数に真の値を渡すとこの動作は抑制され、 後からヘッダを追加で出力することができます。
上で「一種の」と書きましたが、それはこの手続きが完全な逆関数ではないからです
特に、ヘッダ行が長い場合の「行の折り返し」は呼び出し側でやっておかねばなりません。
RFC2822ではヘッダ行の絶対的な長さを998オクテットに制限しています。
データがそれを越える場合は適宜改行(\r\n
)+空白文字を挿入してください。
これは自動ではできません。ヘッダデータのどこに改行を挿入できるかは
各ヘッダのセマンティクスに依存するからです。
また、ヘッダフィールドボディはNUL文字以外のASCII文字のみで構成されている
必要があります。それ以外の文字を含めたい場合は、MIME等適切なプロトコルを
使ってあらかじめエンコードしておいてください。
rfc.mime
モジュールのmime-encode-text
が便利でしょう
(rfc.mime
- MIMEメッセージ処理)。
これもまた、ヘッダフィールド毎のセマンティクスに依存するため、
自動で行うことができません。
しかしこの手続きは、渡されたデータがRFC2822に沿っているかどうかをチェック できます。デフォルトではRFC2822に違反していればエラーが投げられます。 checkキーワード引数でこの振る舞いを変えられます。 可能な値は次のとおりです。
:error
デフォルト。違反があればエラーを投げます。
#f, :ignore
呼び出し側を信頼し、チェックを行いません。
procedure
rfc822-write-headers
が違反を検出したら、
この手続きを3つの引数で呼び出します。
ヘッダフィールド名、ヘッダフィールドボディ、
そして違反の種類を示す以下のシンボルです。
この手続きは、修正されたヘッダフィールド名とヘッダフィールドボディの
ふたつの値を返さなければなりません。
もしこの手続きがヘッダフィールド名とヘッダフィールドボディを修正せずに
返したら、:error
を指定したときのようにエラーが投げられます。
check引数に手続きを渡した際に、第3引数に渡されるシンボルは以下のとおりです。 将来のバージョンでは新たなシンボルが追加されるかもしれません。
incomplete-string
不完全な文字列が渡された
bad-character
NUL文字または非ASCII文字が含まれている
line-too-long
1行が998オクテットの制限を越えている
stray-crlf
CRLFの組み合わせでない、CRのみまたはLFのみが含まれている。