20.22. "Cookie" --- HTTPの状態管理
**********************************

注釈: Python 3 では "Cookie" モジュールは "http.cookies" にリネーム
  されま した。ソースコードを 3 用に変換する時は、 *2to3* ツールが自動
  的に import を修正します。

**ソースコード:** Lib/Cookie.py

======================================================================

"Cookie" モジュールはHTTPの状態管理機能であるcookieの概念を抽象化、定
義しているクラスです。単純な文字列のみで構成されるcookieのほか、シリア
ル化可能なあらゆるデータ型でクッキーの値を保持するための機能も備えてい
ます。

このモジュールは元々 **RFC 2109** と **RFC 2068** に定義されている構文
解析の規則を厳密に守っていました。しかし、MSIE 3.0x がこれらの RFC で
定義された文字の規則に従っていないことが判明し、また、現代の多くのブラ
ウザとサーバも Cookie の処理に緩い解析をしており、結局、やや厳密さを欠
く構文解析規則にせざるを得ませんでした。

"string.ascii_letters" 、 "string.digits" 、 "!#$%&'*+-.^_`|~" が、こ
のモジュールが Cookie 名 ("key") での利用に合法である認めている文字集
合です。

注釈: 正しくない cookie に遭遇した場合、 "CookieError" 例外を送出し
  ます。 なので、ブラウザから持ってきた cookie データを parse するとき
  には常 に "CookieError" 例外を catch して不正な cookie に備えるべき
  です。

exception Cookie.CookieError

   属性や *Set-Cookie* ヘッダが正しくないなど、 **RFC 2109** に合致し
   ていないときに発生する例外です。

class Cookie.BaseCookie([input])

   このクラスはキーが文字列、値が "Morsel" インスタンスで構成される辞
   書風オブジェクトです。値に対するキーを設定するときは、値がキーと値
   を含む "Morsel" に変換されることに注意してください。

   *input* が与えられたときは、そのまま "load()" メソッドへ渡されます
   。

class Cookie.SimpleCookie([input])

   このクラスは "BaseCookie" の派生クラスで、 "value_decode()" は与え
   られた値の正当性を確認するように、 "value_encode()" は "str()" で文
   字列化するようにそれぞれオーバライドします。

class Cookie.SerialCookie([input])

   このクラスは "BaseCookie" の派生クラスで、 "value_decode()" と
   "value_encode()" をそれぞれ "pickle.loads()" と "pickle.dumps()" を
   実行するようにオーバーライドします。

   バージョン 2.3 で非推奨: このクラスを使ってはいけません! 信頼できな
   いcookieのデータから pickle 化された値を読み込むことは、あなたのサ
   ーバ上で任意のコードを実行するために pickle 化した文字列の作成が可
   能であることを意味し、重大なセキュリティホールとなります。

class Cookie.SmartCookie([input])

   このクラスは "BaseCookie" の派生クラスで、 "value_decode()" を、値
   が pickle 化されたデータとして正当なときは "pickle.loads()" を実行
   、そうでないときはその値自体を返すようにオーバーライドします。また
   "value_encode()" を、値が文字列以外のときは "pickle.dumps()" を実行
   、文字列のときはその値自体を返すようにオーバーライドします。

   バージョン 2.3 で非推奨: "SerialCookie" と同じセキュリティ上の注意
   が当てはまります。

関連して、さらなるセキュリティ上の注意があります。後方互換性のため、
"Cookie" モジュールは "Cookie" というクラス名を "SmartCookie" のエイリ
アスとしてエクスポートしています。これはほぼ確実に誤った措置であり、将
来のバージョンでは削除することが適当と思われます。アプリケーションにお
いて "SerialCookie" クラスを使うべきでないのと同じ理由で "Cookie" クラ
スを使うべきではありません。

参考:

  "cookielib" モジュール
     Web *クライアント* 向けの HTTP クッキー処理です。 "cookielib" と
     "Cookie" は互いに独立しています。

  **RFC 2109** - HTTP State Management Mechanism
     このモジュールが実装しているHTTPの状態管理に関する規格です。


20.22.1. Cookieオブジェクト
===========================

BaseCookie.value_decode(val)

   文字列表現を値にデコードして返します。戻り値の型はどのようなもので
   も許されます。このメソッドは "BaseCookie" において何も実行せず、オ
   ーバーライドされるためにだけ存在します。

BaseCookie.value_encode(val)

   エンコードした値を返します。元の値はどのような型でもかまいませんが
   、戻り値は必ず文字列となります。このメソッドは "BaseCookie" におい
   て何も実行せず、オーバーライドされるためにだけ存在します。

   通常 "value_encode()" と "value_decode()" はともに *value_decode*
   の処理内容から逆算した範囲に収まっていなければなりません。

BaseCookie.output([attrs[, header[, sep]]])

   HTTPヘッダ形式の文字列表現を返します。 *attrs* と *header* はそれぞ
   れ "Morsel" の "output()" メソッドに送られます。 *sep* はヘッダの連
   結に用いられる文字で、デフォルトは "'\r\n'" (CRLF)となっています。

   バージョン 2.5 で変更: デフォルトのセパレータを "'\n'" 　から、クッ
   キーの使用にあわせた.

BaseCookie.js_output([attrs])

   ブラウザがJavaScriptをサポートしている場合、HTTPヘッダを送信した場
   合と同様に動作する埋め込み可能なJavaScript snippetを返します。

   *attrs* の意味は "output()" と同じです。

BaseCookie.load(rawdata)

   *rawdata* が文字列であれば、 "HTTP_COOKIE" として処理し、その値を
   "Morsel" として追加します。辞書の場合は次と同様の処理をおこないます
   。

      for k, v in rawdata.items():
          cookie[k] = v


20.22.2. Morselオブジェクト
===========================

class Cookie.Morsel

   **RFC 2109** の属性をキーと値で保持するabstractクラスです。

   Morselは辞書風のオブジェクトで、キーは次のような **RFC 2109** 準拠
   の定数となっています。

   * "expires"

   * "path"

   * "comment"

   * "domain"

   * "max-age"

   * "secure"

   * "version"

   * "httponly"

   "httponly" 属性は、 cookie が HTTP リクエストでのみ送信されて、
   JavaScript からのはアクセスできない事を示します。これはいくつかのク
   ロスサイトスクリプティングの脅威を和らげることを意図しています。

   キーの大小文字は区別されません。

   バージョン 2.6 で追加: "httponly" 属性が追加されました。

Morsel.value

   クッキーの値。

Morsel.coded_value

   実際に送信する形式にエンコードされたcookieの値。

Morsel.key

   cookieの名前。

Morsel.set(key, value, coded_value)

   属性 *key* 、 *value* 、 *coded_value* に値をセットします。

Morsel.isReservedKey(K)

   *K* が "Morsel" のキーであるかどうかを判定します。

Morsel.output([attrs[, header]])

   MoselをHTTPヘッダ形式の文字列表現にして返します。 *attrs* を指定し
   ない場合、デフォルトですべての属性を含めます。 *attrs* を指定する場
   合、属性をリストで渡さなければなりません。 *header* のデフォルトは
   ""Set-Cookie:"" です。

Morsel.js_output([attrs])

   ブラウザがJavaScriptをサポートしている場合、HTTPヘッダを送信した場
   合と同様に動作する埋め込み可能なJavaScript snippetを返します。

   *attrs* の意味は "output()" と同じです。

Morsel.OutputString([attrs])

   Moselの文字列表現をHTTPやJavaScriptで囲まずに出力します。

   *attrs* の意味は "output()" と同じです。


20.22.3. 例
===========

次の例は "Cookie" の使い方を示したものです。

   >>> import Cookie
   >>> C = Cookie.SimpleCookie()
   >>> C["fig"] = "newton"
   >>> C["sugar"] = "wafer"
   >>> print C # generate HTTP headers
   Set-Cookie: fig=newton
   Set-Cookie: sugar=wafer
   >>> print C.output() # same thing
   Set-Cookie: fig=newton
   Set-Cookie: sugar=wafer
   >>> C = Cookie.SimpleCookie()
   >>> C["rocky"] = "road"
   >>> C["rocky"]["path"] = "/cookie"
   >>> print C.output(header="Cookie:")
   Cookie: rocky=road; Path=/cookie
   >>> print C.output(attrs=[], header="Cookie:")
   Cookie: rocky=road
   >>> C = Cookie.SimpleCookie()
   >>> C.load("chips=ahoy; vienna=finger") # load from a string (HTTP header)
   >>> print C
   Set-Cookie: chips=ahoy
   Set-Cookie: vienna=finger
   >>> C = Cookie.SimpleCookie()
   >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";')
   >>> print C
   Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;"
   >>> C = Cookie.SimpleCookie()
   >>> C["oreo"] = "doublestuff"
   >>> C["oreo"]["path"] = "/"
   >>> print C
   Set-Cookie: oreo=doublestuff; Path=/
   >>> C["twix"] = "none for you"
   >>> C["twix"].value
   'none for you'
   >>> C = Cookie.SimpleCookie()
   >>> C["number"] = 7 # equivalent to C["number"] = str(7)
   >>> C["string"] = "seven"
   >>> C["number"].value
   '7'
   >>> C["string"].value
   'seven'
   >>> print C
   Set-Cookie: number=7
   Set-Cookie: string=seven
   >>> # SerialCookie and SmartCookie are deprecated
   >>> # using it can cause security loopholes in your code.
   >>> C = Cookie.SerialCookie()
   >>> C["number"] = 7
   >>> C["string"] = "seven"
   >>> C["number"].value
   7
   >>> C["string"].value
   'seven'
   >>> print C
   Set-Cookie: number="I7\012."
   Set-Cookie: string="S'seven'\012p1\012."
   >>> C = Cookie.SmartCookie()
   >>> C["number"] = 7
   >>> C["string"] = "seven"
   >>> C["number"].value
   7
   >>> C["string"].value
   'seven'
   >>> print C
   Set-Cookie: number="I7\012."
   Set-Cookie: string=seven
