/* * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im> * * 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 "OFIRI.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 forIRI: (OFIRI *)IRI { void *pool = objc_autoreleasePoolPush(); OFString *cookieDomain, *IRIHost; size_t i; IRI = IRI.IRIByAddingPercentEncodingForUnicodeCharacters; if (![cookie.path hasPrefix: @"/"]) cookie.path = @"/"; if (cookie.secure && [IRI.scheme caseInsensitiveCompare: @"https"] != OFOrderedSame) { objc_autoreleasePoolPop(pool); return; } cookieDomain = cookie.domain.lowercaseString; cookie.domain = cookieDomain; IRIHost = IRI.host.lowercaseString; if (![cookieDomain isEqual: IRIHost]) { IRIHost = [@"." stringByAppendingString: IRIHost]; if (![cookieDomain hasSuffix: IRIHost]) { 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 forIRI: (OFIRI *)IRI { for (OFHTTPCookie *cookie in cookies) [self addCookie: cookie forIRI: IRI]; } - (OFArray OF_GENERIC(OFHTTPCookie *) *)cookiesForIRI: (OFIRI *)IRI { OFMutableArray *ret = [OFMutableArray array]; void *pool = objc_autoreleasePoolPush(); IRI = IRI.IRIByAddingPercentEncodingForUnicodeCharacters; for (OFHTTPCookie *cookie in _cookies) { void *pool2; OFDate *expires; OFString *cookieDomain, *IRIHost, *cookiePath, *IRIPath; bool match; expires = cookie.expires; if (expires != nil && expires.timeIntervalSinceNow <= 0) continue; if (cookie.secure && [IRI.scheme caseInsensitiveCompare: @"https"] != OFOrderedSame) continue; pool2 = objc_autoreleasePoolPush(); cookieDomain = cookie.domain.lowercaseString; IRIHost = IRI.host.lowercaseString; if ([cookieDomain hasPrefix: @"."]) { if ([IRIHost hasSuffix: cookieDomain]) match = true; else { cookieDomain = [cookieDomain substringFromIndex: 1]; match = [cookieDomain isEqual: IRIHost]; } } else match = [cookieDomain isEqual: IRIHost]; if (!match) { objc_autoreleasePoolPop(pool2); continue; } cookiePath = cookie.path; IRIPath = IRI.path; if (![cookiePath isEqual: @"/"]) { if ([cookiePath isEqual: IRIPath]) match = true; else { if (![cookiePath hasSuffix: @"/"]) cookiePath = [cookiePath stringByAppendingString: @"/"]; match = [IRIPath hasPrefix: cookiePath]; } if (!match) { objc_autoreleasePoolPop(pool2); continue; } } [ret addObject: cookie]; } [ret makeImmutable]; objc_autoreleasePoolPop(pool); 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