ページ

2009年9月6日日曜日

NSHTTPCookieStorage相当のクラスを自前で実装する (8)クッキー受取のロジックを改良する

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

Mozzila が提供する effective_tld_names.dat を使う実装を行う。

effective_tld_names.dat を扱う専用のクラス "XCEffectiveTLDnames" を用意する。

XCEffectiveTLDnames.h

@interface XCEffectiveTLDNames : NSObject {

NSMutableSet* _domain_set;
NSMutableSet* _wildcard_set;
NSMutableSet* _exception_set;

BOOL _is_loaded_file;
}

+ (XCEffectiveTLDNames*)sharedEffectiveTLDNames;
- (BOOL)isEffectiveTLDName:(NSString*)domain;

@end



  • _domain_set は、jp や co.jp などを格納する。

  • _wildcard_set は、ワイルドカード指定されているドメイン(*.tokyo.jp など)を格納する(※実際に格納するのは "tokyo.jp")

  • _exception_set は、! で始まる除外指定のドメイン(!metro.tokyo.jpなど)を格納する(※実際に格納するのは "metro.tokyo.jp")




ファイルはダウンロードして XCodeプロジェクト内の Resources へ入れておく。エンコーディングは仕様により UTF-8と規定されいている。



XCEffectiveTLDNames はシングルトンとする。
XCEffectiveTLDNames.m
+ (XCEffectiveTLDNames*)sharedEffectiveTLDNames
{
static XCEffectiveTLDNames* _shared = nil;

if (!_shared) {
_shared = [[XCEffectiveTLDNames alloc] init];
[_shared _loadFile];
}
return _shared;
}


初期化時にファイルの読み込みを行う。
- (void)_loadFile
{
NSError* error;

NSString* path = [NSString stringWithFormat:@"%@/%@",
[[NSBundle mainBundle] resourcePath], FILENAME];
NSString* contents = [NSString stringWithContentsOfFile:path
encoding:NSUTF8StringEncoding
error:&error];
if (error) {
NSLog(@"%@", error);
_is_loaded_file = NO;
return;
}

for (NSString* line in [contents componentsSeparatedByString:@"\n"]) {
if ([line length] == 0) {
continue;
}
if ([line hasPrefix:@"//"]) {
continue;
}
if ([line hasPrefix:@"*."]) {
if ([line length] > 2) {
[_wildcard_set addObject:[line substringFromIndex:2]];
// *.tokyo.jp => tokyo.jp
}
continue;
}
if ([line hasPrefix:@"!"]) {
if ([line length] > 1) {
[_exception_set addObject:[line substringFromIndex:1]];
// !metoro.tokyo.jp => metoro.tokyo.jp
}
continue;
}
[_domain_set addObject:line];
}
_is_loaded_file = YES;

}

ちょっと大味な実装な気もするが Cocoaでファイル読み込みを扱ってみるとこんな感じになった。コメントや空行を取り除き、ワイルドカード、除外、ドメインをそれぞれ適切な NSMutableSet へ格納する。

これらを使い、クッキーで受け取れるドメインのチェックを行う。
- (BOOL)isEffectiveTLDName:(NSString*)domain
{
if ([domain hasPrefix:@"."] && [domain length] > 1) {
domain = [domain substringFromIndex:1];
}

if ([_domain_set containsObject:domain]) {
return YES;
}

if ([_exception_set containsObject:domain]) {
return NO;
}

if ([_wildcard_set containsObject:domain]) {
return YES;
}

NSRange range = [domain rangeOfString:@"."];
if (range.location == NSNotFound || (range.location+1) >= [domain length]) {
return NO;
}
NSString* check_domain = [domain substringFromIndex:range.location+1];

if ([_wildcard_set containsObject:check_domain]) {
return YES;
}

return NO;
}


最初に _domain_set, _exception_set, _wildcar_set に一致するかどうかチェックを行う。その後、ワイルドカード用にチェックを行う。Effective TLD の場合に YES を返す。実際にクッキーを受け取るかどうかはこの戻り値が NO の場合となる。

テストコードを書いて動作を確認してみよう。
- (void)_checkself
{
NSString* domain;

domain = @"com";
NSLog(@"%@: %d", domain, [self isEffectiveTLDName:domain]);

domain = @".com";
NSLog(@"%@: %d", domain, [self isEffectiveTLDName:domain]);
:
:


結果はこう(1=YES, 0=NO)。
com: 1
.com: 1
xcatsan.com: 0
.xcatsan.com: 0
www.xcatsan.com: 0
jp: 1
.jp: 1
xcatsan.jp: 0
.xcatsan.jp: 0
co.jp: 1
.co.jp: 1
xcatsan.co.jp: 0
www.xcatsan.co.jp: 0
tokyo.jp: 1
.tokyo.jp: 1
chiyoda.tokyo.jp: 1
metro.tokyo.jp: 0
www.chiyoda.tokyo.jp: 0


TLDはもちろん、ワイルドカードと除外指定もよさそうだ。
このクラスを XCHTTPCookieStorage で使おう。

なお何らかの理由でファイルの読み込みに失敗した場合は -[XCEffectiveTLDNames isEffectiveTLDName] の戻り値は常に NO が帰るのでセキュリティレベルが低下する(当たり前だが)。

- - - -
effective_tld_names.dat のサイズは 58KB程度ある。大したサイズではないが iPhone/iPod touch で実行する場合はどなんだろうか。メンテナンス性は落ちるが NSMutableSet の内容をそのまま plist へ落としておいてそれを使い回すと実行時間とメモリ効率は良くなる。