OSDN Git Service

split Cookie-Date parsing
[jindolf/Jindolf.git] / src / main / java / jp / sfjp / jindolf / net / AccountCookie.java
1 /*
2  * account cookie
3  *
4  * License : The MIT License
5  * Copyright(c) 2008 olyutorskii
6  */
7
8 package jp.sfjp.jindolf.net;
9
10 import java.net.HttpURLConnection;
11 import java.net.URI;
12 import java.net.URISyntaxException;
13
14 /**
15  * 人狼BBSアカウント管理用のCookie。
16  * <p>JRE1.6 java.net.HttpCookie の代用品。
17  * <p>人狼BBSではCookieヘッダに"Set-Cookie"が用いられる。
18  * (Set-Cookie2ではない)
19  * <p>人狼BBSではCookieの寿命管理に"Expires"が用いられる。(Max-Ageではない)
20  * <p>人狼BBSではセッション管理のCookie名に"login"が用いられる。
21  * @see <a href="http://www.ietf.org/rfc/rfc6265.txt">RFC6265</a>
22  */
23 class AccountCookie{          // TODO JRE 1.6対応とともにHttpCookieへ移行予定
24
25     private static final String HEADER_COOKIE = "Set-Cookie";
26     private static final String SEPARATOR = ";";
27     private static final String COOKIE_PATH = "Path";
28     private static final String COOKIE_EXPIRES = "Expires";
29     private static final String BBS_IDENTITY = "login";
30
31
32     private final String loginData;
33     private final URI pathURI;
34     private final long expireDate;
35
36     /**
37      * 認証クッキーの生成。
38      * @param loginData 認証データ
39      * @param path Cookieパス
40      * @param expireDate expire日付
41      * @throws java.lang.NullPointerException 引数がnull
42      * @throws java.lang.IllegalArgumentException パスが変
43      */
44     public AccountCookie(String loginData, String path, long expireDate)
45             throws NullPointerException, IllegalArgumentException{
46         super();
47
48         if(loginData == null || path == null){
49             throw new NullPointerException();
50         }
51
52         this.loginData = loginData;
53         try{
54             this.pathURI = new URI(path);
55         }catch(URISyntaxException e){
56             throw new IllegalArgumentException(path, e);
57         }
58         this.expireDate = expireDate;
59
60         return;
61     }
62
63     /**
64      * 文字列両端に連続するWSP(空白もしくはタブ)を取り除く。
65      * @param txt テキスト
66      * @return 取り除いた結果。引数がnullならnull
67      */
68     static String chopWsp(String txt){
69         if(txt == null) return null;
70
71         int startPt = -1;
72         int endPt   = -1;
73
74         int len = txt.length();
75         for(int idx = 0; idx < len; idx++){
76             char ch = txt.charAt(idx);
77             if(ch != '\u0020' &&  ch != '\t'){
78                 if(startPt < 0) startPt = idx;
79                 endPt = idx + 1;
80             }
81         }
82
83         if(startPt < 0) startPt = 0;
84         if(endPt   < 0) endPt   = 0;
85
86         String result = txt.substring(startPt, endPt);
87
88         return result;
89     }
90
91     /**
92      * Cookie属性を名前と値に分割する。
93      * '='が無い属性(例:Secure)は値がnullになり、=付き空文字列とは区別される。
94      * 余分なWSP空白はトリミングされる。
95      * @param pair '='を挟む名前と値のペア。
96      * @return [0]名前 [1]値 ※空文字列の名前およびnullな値がありうる。
97      */
98     static String[] splitPair(String pair){
99         String[] result = new String[2];
100
101         String[] split = pair.split("=", 2);
102
103         if(split.length >= 1){
104             result[0] = chopWsp(split[0]);
105         }
106
107         if(split.length >= 2){
108             result[1] = chopWsp(split[1]);
109         }
110
111         return result;
112     }
113
114     /**
115      * 人狼BBS用ログインデータを抽出する。
116      * Cookie名は"login"。
117      * @param nameValue '='で区切られたCookie name-value構造
118      * @return 人狼BBS用ログインデータ。見つからなければnull
119      */
120     private static String parseLoginData(String nameValue){
121         String[] nvPair = splitPair(nameValue);
122         String name  = nvPair[0];
123         String value = nvPair[1];
124         if(name.length() <= 0) return null;  // 名前なし
125         if(value == null) return null;       // '=' なし
126
127         if( ! BBS_IDENTITY.equals(name) ) return null;
128
129         return value;
130     }
131
132     /**
133      * 認証Cookieを抽出する。
134      * @param cookieSource HTTPヘッダ 「Set-Cookie」の値
135      * @return 認証Cookie
136      */
137     public static AccountCookie createCookie(String cookieSource){
138         String[] cookiePart = cookieSource.split(SEPARATOR);
139
140         String login = parseLoginData(cookiePart[0]);
141         String path = null;
142         String expires = null;
143
144         int partNo = cookiePart.length;
145         for(int idx = 0 + 1; idx < partNo; idx++){
146             String pair = cookiePart[idx];
147             String[] attr = splitPair(pair);
148             String attrName  = attr[0];
149             String attrValue = attr[1];
150
151             if(COOKIE_PATH.equalsIgnoreCase(attrName)){
152                 path = attrValue;
153             }else if(COOKIE_EXPIRES.equalsIgnoreCase(attrName)){
154                 expires = attrValue;
155             }
156         }
157
158         if(login   == null) return null;
159         if(path    == null) return null;
160         if(expires == null) return null;
161
162         long expTime = CookieDateParser.parseToEpoch(expires);
163         if(expTime < 0L) return null;
164
165         AccountCookie cookie = new AccountCookie(login, path, expTime);
166
167         return cookie;
168     }
169
170     /**
171      * 認証Cookieを抽出する。
172      * @param connection HTTP接続
173      * @return 認証Cookie
174      */
175     public static AccountCookie createCookie(HttpURLConnection connection){
176         String cookieHeader = connection.getHeaderField(HEADER_COOKIE);
177         if(cookieHeader == null) return null;
178         AccountCookie cookie = createCookie(cookieHeader);
179         return cookie;
180     }
181
182     /**
183      * 認証データを返す。
184      * 取り扱い注意!人狼BBSでは機密情報だよ!
185      * @return 認証データ
186      */
187     public String getLoginData(){
188         return this.loginData;
189     }
190
191     /**
192      * Cookieパスを返す。
193      * @return Cookieパス
194      */
195     public URI getPathURI(){
196         return this.pathURI;
197     }
198
199     /**
200      * Cookie期限をエポック時刻で返す。
201      * @return Cookieが期限切れを起こす日時。(msec)
202      */
203     public long getExpiredTime(){
204         return this.expireDate;
205     }
206
207     /**
208      * Cookie期限が切れてないか判定する。
209      * @param nowMs 比較時刻(msec)
210      * @return 期限が切れていたらtrue
211      */
212     public boolean hasExpired(long nowMs){
213         long expireMs = getExpiredTime();
214         if(expireMs < nowMs) return true;
215         return false;
216     }
217
218     /**
219      * 現時点でCookie期限が切れてないか判定する。
220      * @return 期限が切れていたらtrue
221      */
222     public boolean hasExpired(){
223         long nowMs = System.currentTimeMillis();
224         boolean result = hasExpired(nowMs);
225         return result;
226     }
227
228 }