## 12.2.1 電子メールメッセージの表現

`Message` クラスは、 `email` パッケージの中心となるクラスです。 これは `email` オブジェクトモデルの基底クラスになっています。 `Message` はヘッダフィールドを検索したりメッセージ本体にアクセスするための 核となる機能を提供します。

概念的には、`Message` オブジェクトには ヘッダ と ペイロード が 格納されています。ヘッダは、RFC 2822 形式のフィールド名およびフィールド値が コロンで区切られたものです。コロンはフィールド名またはフィールド値の どちらにも含まれません。

ヘッダは大文字小文字を区別した形式で保存されますが、ヘッダ名が一致するかどうかの検査は 大文字小文字を区別せずにおこなうことができます。Unix-From ヘッダまたは `From_` ヘッダとして知られるエンベロープヘッダがひとつ存在することもあります。 ペイロードは、単純なメッセージオブジェクトの場合は単なる文字列ですが、 MIME コンテナ文書 (multipart/* または message/rfc822 など) の場合は `Message` オブジェクトの リストになっています。

`Message` オブジェクトは、メッセージヘッダにアクセスするための マップ (辞書) 形式のインタフェイスと、ヘッダおよびペイロードの両方に アクセスするための明示的なインタフェイスを提供します。 これにはメッセージオブジェクトツリーからフラットなテキスト文書を 生成したり、一般的に使われるヘッダのパラメータにアクセスしたり、また オブジェクトツリーを再帰的にたどったりするための便利なメソッドを含みます。

`Message` クラスのメソッドは以下のとおりです:

class `Message`( | )

  コンストラクタは引数をとりません。

`as_string`( | [unixfrom])

  メッセージ全体をフラットな文字列として返します。 オプション unixfrom が `True` の場合、返される文字列には エンベロープヘッダも含まれます。unixfrom のデフォルトは `False` です。

  このメソッドは手軽に利用する事ができますが、必ずしも期待通りにメッセージを フォーマットするとは限りません。以下の例のように `Generator` のインスタンスを生成して `flatten()` メソッドを直接呼び出せば より柔軟な処理を行う事ができます。

      from cStringIO import StringIO
      from email.Generator import Generator
      fp = StringIO()
      g = Generator(fp, mangle_from_=False, maxheaderlen=60)
      g.flatten(msg)
      text = fp.getvalue()

`__str__`( | )

  `as_string(unixfrom=True)` と同じです。

`is_multipart`( | )

  メッセージのペイロードが子 `Message` オブジェクトからなる リストであれば `True` を返し、そうでなければ `False` を返します。 `is_multipart()` が False を返した場合は、ペイロードは 文字列オブジェクトである必要があります。

`set_unixfrom`( | unixfrom)

  メッセージのエンベロープヘッダを unixfrom に設定します。これは文字列である必要があります。

`get_unixfrom`( | )

  メッセージのエンベロープヘッダを返します。 エンベロープヘッダが設定されていない場合は None が返されます。

`attach`( | payload)

  与えられた payload を現在のペイロードに追加します。 この時点でのペイロードは `None` か、あるいは `Message` オブジェクトの リストである必要があります。このメソッドの実行後、ペイロードは必ず `Message` オブジェクトのリストになります。ペイロードに スカラーオブジェクト (文字列など) を格納したい場合は、かわりに `set_payload()` を使ってください。

`get_payload`( | [i[, decode]])

  現在のペイロードへの参照を返します。これは `is_multipart()` が `True` の場合 `Message` オブジェクトのリストになり、`is_multipart()` が `False` の場合は文字列になります。ペイロードがリストの場合、 リストを変更することはそのメッセージのペイロードを変更することになります。

  オプション引数の i がある場合、 `is_multipart()` が `True` ならば `get_payload()` は ペイロード中で 0 から数えて i 番目の要素を返します。i が 0 より小さい場合、あるいはペイロードの個数以上の場合は `IndexError` が発生します。ペイロードが文字列 (つまり `is_multipart()` が `False`) にもかかわらず i が与えられたときは `TypeError` が発生します。

  オプションの decode はそのペイロードが Content-Transfer-Encoding: ヘッダに従って デコードされるべきかどうかを指示するフラグです。 この値が `True` でメッセージが multipart ではない場合、 ペイロードはこのヘッダの値が "`quoted-printable`" または "`base64`" のときにかぎりデコードされます。これ以外のエンコーディングが 使われている場合、Content-Transfer-Encoding: ヘッダが ない場合、あるいは曖昧なbase64データが含まれる場合は、ペイロードはそのまま (デコードされずに) 返されます。 もしメッセージが multipart で decode フラグが `True` の場合は `None` が返されます。decode のデフォルト値は `False` です。

`set_payload`( | payload[, charset])

  メッセージ全体のオブジェクトのペイロードを payload に設定します。 ペイロードの形式をととのえるのは呼び出し側の責任です。 オプションの charset はメッセージのデフォルト文字セットを設定します。 詳しくは `set_charset()` を参照してください。

  バージョン2.2.2 以降で変更された 仕様: charset 引数の追加

`set_charset`( | charset)

  ペイロードの文字セットを charset に変更します。 ここには `Charset`インスタンス (`email.Charset` 参照)、 文字セット名をあらわす文字列、あるいは `None` のいずれかが指定できます。 文字列を指定した場合、これは `Charset` インスタンスに変換されます。 charset が `None` の場合、`charset` パラメータは Content-Type: ヘッダから除去されます。 これ以外のものを文字セットとして指定した場合、 `TypeError` が発生します。

  ここでいうメッセージとは、`charset.input_charset` でエンコードされた text/* 形式のものを仮定しています。これは、もし必要とあらば プレーンテキスト形式を変換するさいに `charset.output_charset` の エンコードに変換されます。MIME ヘッダ (MIME-Version:, Content-Type:, Content-Transfer-Encoding:) は必要に応じて追加されます。

  バージョン2.2.2 以降で新規追加された 仕様です。

`get_charset`( | )

  そのメッセージ中のペイロードの `Charset` インスタンスを 返します。 バージョン2.2.2 以降で新規追加された 仕様です。

以下のメソッドは、メッセージの RFC 2822 ヘッダにアクセスするための マップ (辞書) 形式のインタフェイスを実装したものです。 これらのメソッドと、通常のマップ (辞書) 型はまったく同じ意味をもつわけでは ないことに注意してください。たとえば辞書型では、同じキーが複数あることは 許されていませんが、ここでは同じメッセージヘッダが複数ある場合があります。 また、辞書型では `keys()` で返されるキーの順序は保証されていませんが、 `Message` オブジェクト内のヘッダはつねに元のメッセージ中に 現れた順序、あるいはそのあとに追加された順序で返されます。削除され、その後 ふたたび追加されたヘッダはリストの一番最後に現れます。

こういった意味のちがいは意図的なもので、最大の利便性をもつようにつくられています。

注意: どんな場合も、メッセージ中のエンベロープヘッダは このマップ形式のインタフェイスには含まれません。

`__len__`( | )

  複製されたものもふくめてヘッダ数の合計を返します。

`__contains__`( | name)

  メッセージオブジェクトが name という名前のフィールドを持っていれば true を返します。 この検査では名前の大文字小文字は区別されません。name は最後にコロンをふくんでいてはいけません。 このメソッドは以下のように `in` 演算子で使われます:

      if 'message-id' in myMessage:
          print 'Message-ID:', myMessage['message-id']

`__getitem__`( | name)

  指定された名前のヘッダフィールドの値を返します。 name は最後にコロンをふくんでいてはいけません。 そのヘッダがない場合は `None` が返され、`KeyError` 例外は発生しません。

  注意: 指定された名前のフィールドがメッセージのヘッダに 2回以上現れている場合、 どちらの値が返されるかは未定義です。ヘッダに存在するフィールドの値をすべて 取り出したい場合は `get_all()` メソッドを使ってください。

`__setitem__`( | name, val)

  メッセージヘッダに name という名前の val という値をもつ フィールドをあらたに追加します。このフィールドは現在メッセージに 存在するフィールドのいちばん後に追加されます。

  注意: このメソッドでは、すでに同一の名前で存在するフィールドは 上書きされません。もしメッセージが名前 name をもつ フィールドをひとつしか持たないようにしたければ、最初にそれを除去してください。 たとえば:

      del msg['subject']
      msg['subject'] = 'PythonPythonPython!'

`__delitem__`( | name)

  メッセージのヘッダから、 name という名前をもつ フィールドをすべて除去します。たとえこの名前をもつヘッダが 存在していなくても例外は発生しません。

`has_key`( | name)

  メッセージが name という名前をもつ ヘッダフィールドを持っていれば真を、そうでなければ偽を返します。

`keys`( | )

  メッセージ中にあるすべてのヘッダのフィールド名のリストを返します。

`values`( | )

  メッセージ中にあるすべてのフィールドの値のリストを返します。

`items`( | )

  メッセージ中にあるすべてのヘッダのフィールド名とその値を 2-タプルのリストとして返します。

`get`( | name[, failobj])

  指定された名前をもつフィールドの値を返します。 これは指定された名前がないときにオプション引数の failobj (デフォルトでは `None`) を返すことをのぞけば、`__getitem__()` と同じです。

役に立つメソッドをいくつか紹介します:

`get_all`( | name[, failobj])

  name の名前をもつフィールドのすべての値からなるリストを返します。 該当する名前のヘッダがメッセージ中に含まれていない場合は failobj (デフォルトでは `None`) が返されます。

`add_header`( | _name, _value, **_params)

  拡張ヘッダ設定。このメソッドは `__setitem__()` と似ていますが、 追加のヘッダ・パラメータをキーワード引数で指定できるところが違っています。 _name に追加するヘッダフィールドを、_value にそのヘッダの 最初の値を渡します。

  キーワード引数辞書 _params の各項目ごとに、 そのキーがパラメータ名として扱われ、キー名にふくまれる アンダースコアはハイフンに置換されます (なぜならハイフンは 通常の Python 識別子としては使えないからです)。ふつう、 パラメータの値が `None` 以外のときは、`key="value"` の 形で追加されます。パラメータの値が `None` のときはキーのみが追加されます。

  例を示しましょう:

      msg.add_header('Content-Disposition', 'attachment', filename='bud.gif')

  こうするとヘッダには以下のように追加されます。

      Content-Disposition: attachment; filename="bud.gif"

`replace_header`( | _name, _value)

  ヘッダの置換。_name と一致するヘッダで最初に見つかったものを置き換えます。 このときヘッダの順序とフィールド名の大文字小文字は保存されます。 一致するヘッダがない場合、 `KeyError` が発生します。

  バージョン2.2.2 以降で新規追加された 仕様です。

`get_content_type`( | )

  そのメッセージの content-type を返します。 返された文字列は強制的に小文字で maintype/subtype の形式に変換されます。 メッセージ中に Content-Type: ヘッダがない場合、デフォルトの content-type は `get_default_type()` が返す値によって与えられます。 RFC 2045 によればメッセージはつねにデフォルトの content-type を もっているので、`get_content_type()` はつねになんらかの値を返すはずです。

  RFC 2045 はメッセージのデフォルト content-type を、 それが multipart/digest コンテナに現れているとき以外は text/plain に規定しています。あるメッセージが multipart/digest コンテナ中にある場合、その content-type は message/rfc822 になります。 もし Content-Type: ヘッダが適切でない content-type 書式だった場合、 RFC 2045 はそれのデフォルトを text/plain として扱うよう 定めています。

  バージョン2.2.2 以降で新規追加された 仕様です。

`get_content_maintype`( | )

  そのメッセージの主 content-type を返します。 これは `get_content_type()` によって 返される文字列の maintype 部分です。

  バージョン2.2.2 以降で新規追加された 仕様です。

`get_content_subtype`( | )

  そのメッセージの副 content-type (sub content-type、subtype) を返します。 これは `get_content_type()` によって 返される文字列の subtype 部分です。

  バージョン2.2.2 以降で新規追加された 仕様です。

`get_default_type`( | )

  デフォルトの content-type を返します。 ほどんどのメッセージではデフォルトの content-type は text/plain ですが、メッセージが multipart/digest コンテナに 含まれているときだけ例外的に message/rfc822 になります。

  バージョン2.2.2 以降で新規追加された 仕様です。

`set_default_type`( | ctype)

  デフォルトの content-type を設定します。 ctype は text/plain あるいは message/rfc822 である必要がありますが、強制ではありません。 デフォルトの content-type はヘッダの Content-Type: には 格納されません。

  バージョン2.2.2 以降で新規追加された 仕様です。

`get_params`( | [failobj[, header[, unquote]]])

  メッセージの Content-Type: パラメータをリストとして返します。 返されるリストは キー/値の組からなる 2要素タプルが連なったものであり、 これらは "`=`" 記号で分離されています。"`=`" の左側は キーになり、右側は値になります。パラメータ中に "`=`" がなかった場合、 値の部分は空文字列になり、そうでなければその値は `get_param()` で 説明されている形式になります。また、オプション引数 unquote が `True` (デフォルト) である場合、この値は unquote されます。

  オプション引数 failobj は、Content-Type: ヘッダが 存在しなかった場合に返すオブジェクトです。オプション引数 header には Content-Type: のかわりに検索すべきヘッダを指定します。

  バージョン2.2.2 以降で変更された 仕様: unquote が追加されました

`get_param`( | param[, failobj[, header[, unquote]]])

  メッセージの Content-Type: ヘッダ中のパラメータ param を 文字列として返します。そのメッセージ中に Content-Type: ヘッダが 存在しなかった場合、 failobj (デフォルトは `None`) が返されます。

  オプション引数 header が与えられた場合、 Content-Type: のかわりにそのヘッダが使用されます。

  パラメータのキー比較は常に大文字小文字を区別しません。 返り値は文字列か 3 要素のタプルで、タプルになるのはパラメータが RFC 2231 エンコードされている場合です。3 要素タプルの場合、各要素の値は `(CHARSET, LANGUAGE, VALUE)` の形式になっています。 `CHARSET` と `LAGUAGE` は `None` になることがあり、その場合 `VALUE` は `us-ascii` 文字セットでエンコードされているとみなさねば ならないので注意してください。普段は `LANGUAGE` を無視できます。 この関数を使うアプリケーションは 3要素タプルが返された場合を想定している必要があります。 この場合、Unicode 文字列にして返すなどの処理をおこなうことが考えられます:

      param = msg.get_param('foo')
      if isinstance(param, tuple):
          param = unicode(param[2], param[0] or 'us-ascii')

  いずれの場合もパラメータの値は (文字列であれ 3要素タプルの `VALUE` 項目であれ) つねに unquote されます。 ただし、unquote が `False` に指定されている場合は unquote されません。

  バージョン2.2.2 以降で変更された 仕様: unquote 引数の追加、3要素タプルが返り値になる可能性あり

`set_param`( | param, value[, header[, requote[, charset[, language]]]])

  Content-Type: ヘッダ中のパラメータを設定します。 指定されたパラメータがヘッダ中にすでに存在する場合、その値は value に置き換えられます。Content-Type: ヘッダがまだ このメッセージ中に存在していない場合、RFC 2045 にしたがいこの値には text/plain が設定され、新しいパラメータ値が末尾に追加されます。

  オプション引数 header が与えられた場合、 Content-Type: のかわりにそのヘッダが使用されます。 オプション引数 unquote が `False` でない限り、 この値は unquote されます (デフォルトは `True`)。

  オプション引数 charset が与えられると、 そのパラメータは RFC 2231 に従ってエンコードされます。 オプション引数 language は RFC 2231 の言語を指定しますが、 デフォルトではこれは空文字列となります。 charset と language はどちらも文字列である必要があります。

  バージョン2.2.2 以降で新規追加された 仕様です。

`del_param`( | param[, header[, requote]])

  指定されたパラメータを Content-Type: ヘッダ中から完全に とりのぞきます。ヘッダはそのパラメータと値がない状態に書き換えられます。 requote が `False` でない限り (デフォルトでは `True` です)、 すべての値は必要に応じて quote されます。オプション変数 header が与えられた場合、 Content-Type: のかわりにそのヘッダが使用されます。

  バージョン2.2.2 以降で新規追加された 仕様です。

`set_type`( | type[, header][, requote])

  Content-Type: ヘッダの maintype と subtype を設定します。 type は maintype/subtype という形の文字列でなければなりません。 それ以外の場合は `ValueError` が発生します。

  このメソッドは Content-Type: ヘッダを置き換えますが、 すべてのパラメータはそのままにします。requote が `False` の場合、 これはすでに存在するヘッダを quote せず放置しますが、そうでない場合は 自動的に quote します (デフォルト動作)。

  オプション変数 header が与えられた場合、 Content-Type: のかわりにそのヘッダが使用されます。 Content-Type: ヘッダが設定される場合には、 MIME-Version: ヘッダも同時に付加されます。

  バージョン2.2.2 以降で新規追加された 仕様です。

`get_filename`( | [failobj])

  そのメッセージ中の Content-Disposition: ヘッダにある、 `filename` パラメータの値を返します。目的のヘッダが欠けていたり、 `filename` パラメータがない場合には failobj が返されます。 返される文字列はつねに `Utils.unquote()` によって unquote されます。

`get_boundary`( | [failobj])

  そのメッセージ中の Content-Type: ヘッダにある、 `boundary` パラメータの値を返します。目的のヘッダが欠けていたり、 `boundary` パラメータがない場合には failobj が返されます。 返される文字列はつねに `Utils.unquote()` によって unquote されます。

`set_boundary`( | boundary)

  メッセージ中の Content-Type: ヘッダにある、 `boundary` パラメータに値を設定します。`set_boundary()` は 必要に応じて boundary を quote します。そのメッセージが Content-Type: ヘッダを含んでいない場合、 `HeaderParseError` が発生します。

  注意: このメソッドを使うのは、古い Content-Type: ヘッダを 削除して新しい boundary をもったヘッダを `add_header()` で 足すのとは少し違います。`set_boundary()` は 一連のヘッダ中での Content-Type: ヘッダの位置を保つからです。 しかし、これは元の Content-Type: ヘッダ中に存在していた 連続する行の順番までは 保ちません。

`get_content_charset`( | [failobj])

  そのメッセージ中の Content-Type: ヘッダにある、 `charset` パラメータの値を返します。値はすべて小文字に変換されます。 メッセージ中に Content-Type: がなかったり、このヘッダ中に `boundary` パラメータがない場合には failobj が返されます。

  注意: これは `get_charset()` メソッドとは異なります。 こちらのほうは文字列のかわりに、そのメッセージボディのデフォルト エンコーディングの `Charset` インスタンスを返します。

  バージョン2.2.2 以降で新規追加された 仕様です。

`get_charsets`( | [failobj])

  メッセージ中に含まれる文字セットの名前をすべてリストにして返します。 そのメッセージが multipart である場合、返されるリストの 各要素がそれぞれの subpart のペイロードに対応します。それ以外の場合、 これは長さ 1 のリストを返します。

  リスト中の各要素は文字列であり、これは対応する subpart 中の それぞれの Content-Type: ヘッダにある `charset` の値です。 しかし、その subpart が Content-Type: をもってないか、 `charset` がないか、あるいは MIME maintype が text でない いずれかの場合には、リストの要素として failobj が返されます。

`walk`( | )

  `walk()` メソッドは多目的のジェネレータで、 これはあるメッセージオブジェクトツリー中のすべての part および subpart を わたり歩くのに使えます。順序は深さ優先です。おそらく典型的な用法は、 `walk()` を `for` ループ中でのイテレータとして 使うことでしょう。ループを一回まわるごとに、次の subpart が返されるのです。

  以下の例は、 multipart メッセージのすべての part において、 その MIME タイプを表示していくものです。

      >>> for part in msg.walk():
      >>>     print part.get_content_type()
      multipart/report
      text/plain
      message/delivery-status
      text/plain
      text/plain
      message/rfc822

`Message` オブジェクトはオプションとして 2つのインスタンス属性を とることができます。これはある MIME メッセージからプレーンテキストを 生成するのに使うことができます。

`preamble`
  MIME ドキュメントの形式では、 ヘッダ直後にくる空行と最初の multipart 境界をあらわす文字列のあいだに いくらかのテキスト (訳注: preamble, 序文) を埋めこむことを許しています。 このテキストは標準的な MIME の範疇からはみ出しているので、 MIME 形式を認識するメールソフトからこれらは通常まったく見えません。 しかしメッセージのテキストを生で見る場合、あるいはメッセージを MIME 対応していないメールソフトで見る場合、このテキストは 目に見えることになります。

  preamble 属性は MIME ドキュメントに加える この最初の MIME 範囲外テキストを含んでいます。 `Parser` があるテキストをヘッダ以降に発見したが、 それはまだ最初の MIME 境界文字列が現れる前だった場合、 パーザはそのテキストをメッセージの preamble 属性に格納します。 `Generator` がある MIME メッセージからプレーンテキスト形式を 生成するとき、これはそのテキストをヘッダと最初の MIME 境界の間に挿入します。 詳細は `email.Parser` および `email.Generator` を 参照してください。

  注意: そのメッセージに preamble がない場合、 preamble 属性には `None` が格納されます。

`epilogue`
  epilogue 属性はメッセージの最後の MIME 境界文字列から メッセージ末尾までのテキストを含むもので、それ以外は preamble 属性と同じです。

  注意: multipart メッセージからフラットなテキストを生成するとき、 (標準的な `Generator` を使ったとして) もしそのメッセージに epilogue が ない場合、最後の MIME 境界文字列のあとには改行文字が追加されません。 もしそのメッセージオブジェクトが epilogue をもっており、 それが改行文字から始まっていない場合、改行文字が MIME 境界文字列のあとに 追加されます。これはややぶかっこうに見えますが、ほとんどの場合はこれで うまくいきます。要するに、もし multipart 境界のあとに かならず改行を入れるようにしたければ、epilogue に 空文字列を入れておけばよいのです。