/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, * 2018, 2019 * Jonathan Schleifer <js@heap.zone> * * All rights reserved. * * This file is part of ObjFW. It may be distributed under the terms of the * Q Public License 1.0, which can be found in the file LICENSE.QPL included in * the packaging of this file. * * Alternatively, it may be distributed under the terms of the GNU General * Public License, either version 2 or 3, which can be found in the file * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this * file. */ #include "config.h" #import "OFHTTPCookieManager.h" #import "OFArray.h" #import "OFDate.h" #import "OFHTTPCookie.h" #import "OFURL.h" @implementation OFHTTPCookieManager + (instancetype)manager { return [[[self alloc] init] autorelease]; } - (instancetype)init { self = [super init]; @try { _cookies = [[OFMutableArray alloc] init]; } @catch (id e) { [self release]; @throw e; } return self; } - (void)dealloc { [_cookies release]; [super dealloc]; } - (OFArray OF_GENERIC(OFHTTPCookie *) *)cookies { return [[_cookies copy] autorelease]; } - (void)addCookie: (OFHTTPCookie *)cookie forURL: (OFURL *)URL { void *pool = objc_autoreleasePoolPush(); OFString *cookieDomain, *URLHost; size_t i; if (![cookie.path hasPrefix: @"/"]) cookie.path = @"/"; if (cookie.secure && ![URL.scheme isEqual: @"https"]) { objc_autoreleasePoolPop(pool); return; } cookieDomain = cookie.domain.lowercaseString; cookie.domain = cookieDomain; URLHost = URL.host.lowercaseString; if (![cookieDomain isEqual: URLHost]) { URLHost = [@"." stringByAppendingString: URLHost]; if (![cookieDomain hasSuffix: URLHost]) { objc_autoreleasePoolPop(pool); return; } } i = 0; for (OFHTTPCookie *iter in _cookies) { if ([iter.name isEqual: cookie.name] && [iter.domain isEqual: cookie.domain] && [iter.path isEqual: cookie.path]) { [_cookies replaceObjectAtIndex: i withObject: cookie]; objc_autoreleasePoolPop(pool); return; } i++; } [_cookies addObject: cookie]; objc_autoreleasePoolPop(pool); } - (void)addCookies: (OFArray OF_GENERIC(OFHTTPCookie *) *)cookies forURL: (OFURL *)URL { for (OFHTTPCookie *cookie in cookies) [self addCookie: cookie forURL: URL]; } - (OFArray OF_GENERIC(OFHTTPCookie *) *)cookiesForURL: (OFURL *)URL { OFMutableArray *ret = [OFMutableArray array]; for (OFHTTPCookie *cookie in _cookies) { void *pool; OFDate *expires; OFString *cookieDomain, *URLHost, *cookiePath, *URLPath; bool match; expires = cookie.expires; if (expires != nil && expires.timeIntervalSinceNow <= 0) continue; if (cookie.secure && ![URL.scheme isEqual: @"https"]) continue; pool = objc_autoreleasePoolPush(); cookieDomain = cookie.domain.lowercaseString; URLHost = URL.host.lowercaseString; if ([cookieDomain hasPrefix: @"."]) { if ([URLHost hasSuffix: cookieDomain]) match = true; else { cookieDomain = [cookieDomain substringWithRange: of_range(1, cookieDomain.length - 1)]; match = [cookieDomain isEqual: URLHost]; } } else match = [cookieDomain isEqual: URLHost]; if (!match) { objc_autoreleasePoolPop(pool); continue; } cookiePath = cookie.path; URLPath = URL.path; if (![cookiePath isEqual: @"/"]) { if ([cookiePath isEqual: URLPath]) match = true; else { if (![cookiePath hasSuffix: @"/"]) cookiePath = [cookiePath stringByAppendingString: @"/"]; match = [URLPath hasPrefix: cookiePath]; } if (!match) { objc_autoreleasePoolPop(pool); continue; } } [ret addObject: cookie]; } [ret makeImmutable]; return ret; } - (void)purgeExpiredCookies { for (size_t i = 0, count = _cookies.count; i < count; i++) { OFDate *expires = [[_cookies objectAtIndex: i] expires]; if (expires != nil && expires.timeIntervalSinceNow <= 0) { [_cookies removeObjectAtIndex: i]; i--; count--; continue; } } } @end