Index: ObjFW.xcodeproj/project.pbxproj ================================================================== --- ObjFW.xcodeproj/project.pbxproj +++ ObjFW.xcodeproj/project.pbxproj @@ -79,10 +79,12 @@ 4B0EA9221898690E00F573A4 /* OFKernelEventObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B0EA9191898690E00F573A4 /* OFKernelEventObserver.m */; }; 4B0EA9231898690E00F573A4 /* OFKernelEventObserver+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B0EA91A1898690E00F573A4 /* OFKernelEventObserver+Private.h */; }; 4B0EA925189869D900F573A4 /* OFUDPSocketTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B0EA924189869D900F573A4 /* OFUDPSocketTests.m */; }; 4B11005C14329B9A003A45D8 /* OFXMLNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B11005A14329B9A003A45D8 /* OFXMLNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B11005D14329B9A003A45D8 /* OFXMLNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B11005B14329B9A003A45D8 /* OFXMLNode.m */; }; + 4B11D09C1EC901440094423D /* OFHTTPCookieManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B11D09B1EC901440094423D /* OFHTTPCookieManagerTests.m */; }; + 4B11D09D1EC901440094423D /* OFHTTPCookieManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B11D09B1EC901440094423D /* OFHTTPCookieManagerTests.m */; }; 4B141BA415FCDF74000C21A8 /* OFSortedList.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B141BA215FCDF74000C21A8 /* OFSortedList.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B141BA515FCDF74000C21A8 /* OFSortedList.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B141BA315FCDF74000C21A8 /* OFSortedList.m */; }; 4B1473CB17E6391900B46BB8 /* OFAutoreleasePool+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B1473CA17E6391900B46BB8 /* OFAutoreleasePool+Private.h */; }; 4B14EAC71E9A8292005E8BFD /* OFSandboxActivationFailedException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B14EAC51E9A8292005E8BFD /* OFSandboxActivationFailedException.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B14EAC81E9A8292005E8BFD /* OFSandboxActivationFailedException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B14EAC51E9A8292005E8BFD /* OFSandboxActivationFailedException.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -748,10 +750,14 @@ 4B6025A219B76A5C00694BCC /* OFSHA384Or512Hash.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B60259C19B76A5C00694BCC /* OFSHA384Or512Hash.m */; }; 4B6025A319B76A5C00694BCC /* OFSHA512Hash.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B60259D19B76A5C00694BCC /* OFSHA512Hash.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B6025A419B76A5C00694BCC /* OFSHA512Hash.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B60259E19B76A5C00694BCC /* OFSHA512Hash.m */; }; 4B6025A719B76B5000694BCC /* OFSHA384HashTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B6025A519B76B5000694BCC /* OFSHA384HashTests.m */; }; 4B6025A819B76B5000694BCC /* OFSHA512HashTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B6025A619B76B5000694BCC /* OFSHA512HashTests.m */; }; + 4B6255161EC274FA003D49F4 /* OFHTTPCookieManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B6255141EC274FA003D49F4 /* OFHTTPCookieManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4B6255171EC274FA003D49F4 /* OFHTTPCookieManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B6255141EC274FA003D49F4 /* OFHTTPCookieManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4B6255181EC274FA003D49F4 /* OFHTTPCookieManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B6255151EC274FA003D49F4 /* OFHTTPCookieManager.m */; }; + 4B6255191EC274FA003D49F4 /* OFHTTPCookieManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B6255151EC274FA003D49F4 /* OFHTTPCookieManager.m */; }; 4B62ED1518566FCA0004E0E3 /* OFCopyItemFailedException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B62ED1318566FCA0004E0E3 /* OFCopyItemFailedException.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B62ED1618566FCA0004E0E3 /* OFCopyItemFailedException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B62ED1418566FCA0004E0E3 /* OFCopyItemFailedException.m */; }; 4B6736121E2B2F6F00681F2C /* codepage_858.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B6736111E2B2F6F00681F2C /* codepage_858.m */; }; 4B6736131E2B2F6F00681F2C /* codepage_858.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B6736111E2B2F6F00681F2C /* codepage_858.m */; }; 4B6743F1163C384A00EB1E59 /* OFLockFailedException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B6743EB163C384A00EB1E59 /* OFLockFailedException.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -1152,10 +1158,11 @@ 4B0EA9191898690E00F573A4 /* OFKernelEventObserver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFKernelEventObserver.m; path = src/OFKernelEventObserver.m; sourceTree = ""; }; 4B0EA91A1898690E00F573A4 /* OFKernelEventObserver+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OFKernelEventObserver+Private.h"; path = "src/OFKernelEventObserver+Private.h"; sourceTree = ""; }; 4B0EA924189869D900F573A4 /* OFUDPSocketTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFUDPSocketTests.m; path = tests/OFUDPSocketTests.m; sourceTree = ""; }; 4B11005A14329B9A003A45D8 /* OFXMLNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFXMLNode.h; path = src/OFXMLNode.h; sourceTree = ""; }; 4B11005B14329B9A003A45D8 /* OFXMLNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFXMLNode.m; path = src/OFXMLNode.m; sourceTree = ""; }; + 4B11D09B1EC901440094423D /* OFHTTPCookieManagerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFHTTPCookieManagerTests.m; path = tests/OFHTTPCookieManagerTests.m; sourceTree = ""; }; 4B141BA215FCDF74000C21A8 /* OFSortedList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFSortedList.h; path = src/OFSortedList.h; sourceTree = ""; }; 4B141BA315FCDF74000C21A8 /* OFSortedList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFSortedList.m; path = src/OFSortedList.m; sourceTree = ""; }; 4B1473CA17E6391900B46BB8 /* OFAutoreleasePool+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OFAutoreleasePool+Private.h"; path = "src/OFAutoreleasePool+Private.h"; sourceTree = ""; }; 4B14EAC51E9A8292005E8BFD /* OFSandboxActivationFailedException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFSandboxActivationFailedException.h; path = src/exceptions/OFSandboxActivationFailedException.h; sourceTree = ""; }; 4B14EAC61E9A8292005E8BFD /* OFSandboxActivationFailedException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFSandboxActivationFailedException.m; path = src/exceptions/OFSandboxActivationFailedException.m; sourceTree = ""; }; @@ -1347,10 +1354,12 @@ 4B60259C19B76A5C00694BCC /* OFSHA384Or512Hash.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFSHA384Or512Hash.m; path = src/OFSHA384Or512Hash.m; sourceTree = ""; }; 4B60259D19B76A5C00694BCC /* OFSHA512Hash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFSHA512Hash.h; path = src/OFSHA512Hash.h; sourceTree = ""; }; 4B60259E19B76A5C00694BCC /* OFSHA512Hash.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFSHA512Hash.m; path = src/OFSHA512Hash.m; sourceTree = ""; }; 4B6025A519B76B5000694BCC /* OFSHA384HashTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFSHA384HashTests.m; path = tests/OFSHA384HashTests.m; sourceTree = ""; }; 4B6025A619B76B5000694BCC /* OFSHA512HashTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFSHA512HashTests.m; path = tests/OFSHA512HashTests.m; sourceTree = ""; }; + 4B6255141EC274FA003D49F4 /* OFHTTPCookieManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFHTTPCookieManager.h; path = src/OFHTTPCookieManager.h; sourceTree = ""; }; + 4B6255151EC274FA003D49F4 /* OFHTTPCookieManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFHTTPCookieManager.m; path = src/OFHTTPCookieManager.m; sourceTree = ""; }; 4B62ED1318566FCA0004E0E3 /* OFCopyItemFailedException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFCopyItemFailedException.h; path = src/exceptions/OFCopyItemFailedException.h; sourceTree = ""; }; 4B62ED1418566FCA0004E0E3 /* OFCopyItemFailedException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFCopyItemFailedException.m; path = src/exceptions/OFCopyItemFailedException.m; sourceTree = ""; }; 4B6736111E2B2F6F00681F2C /* codepage_858.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = codepage_858.m; path = src/encodings/codepage_858.m; sourceTree = ""; }; 4B6743EB163C384A00EB1E59 /* OFLockFailedException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFLockFailedException.h; path = src/exceptions/OFLockFailedException.h; sourceTree = ""; }; 4B6743EC163C384A00EB1E59 /* OFLockFailedException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFLockFailedException.m; path = src/exceptions/OFLockFailedException.m; sourceTree = ""; }; @@ -2015,10 +2024,12 @@ 4B6D0A881D4459D900901D8D /* OFHMAC.m */, 4BB4B53F16775FF4002A2DCE /* OFHTTPClient.h */, 4BB4B54016775FF4002A2DCE /* OFHTTPClient.m */, 4B2610C11D863007001F16C9 /* OFHTTPCookie.h */, 4B2610C21D863007001F16C9 /* OFHTTPCookie.m */, + 4B6255141EC274FA003D49F4 /* OFHTTPCookieManager.h */, + 4B6255151EC274FA003D49F4 /* OFHTTPCookieManager.m */, 4B99250F12E0780000215DBE /* OFHTTPRequest.h */, 4B99251012E0780000215DBE /* OFHTTPRequest.m */, 4B7161AB17A6FC7600B74970 /* OFHTTPResponse.h */, 4B7161AC17A6FC7600B74970 /* OFHTTPResponse.m */, 4BB4B54116775FF4002A2DCE /* OFHTTPServer.h */, @@ -2253,10 +2264,11 @@ 4BE5F0E512DF4259005C7A0C /* OFDateTests.m */, 4B6EF6701235358D0076B512 /* OFDictionaryTests.m */, 4BF0DD731D44645D001D9949 /* OFHMACTests.m */, 4BB4B54916776094002A2DCE /* OFHTTPClientTests.m */, 4B2610C51D86305C001F16C9 /* OFHTTPCookieTests.m */, + 4B11D09B1EC901440094423D /* OFHTTPCookieManagerTests.m */, 4B5B02C018B2897500CE6AE4 /* OFINIFileTests.m */, 4BAA60C714D09699006F068D /* OFJSONTests.m */, 4B22BE1F1AE594A000CD320A /* OFKernelEventObserverTests.m */, 4B6EF6721235358D0076B512 /* OFListTests.m */, 4B6EF6731235358D0076B512 /* OFMD5HashTests.m */, @@ -2405,10 +2417,11 @@ 4B2C21EC1DA292BE00735907 /* OFFileManager.h in Headers */, 4B2C21ED1DA292BE00735907 /* OFGZIPStream.h in Headers */, 4B2C21EE1DA292BE00735907 /* OFHMAC.h in Headers */, 4B2C21EF1DA292BE00735907 /* OFHTTPClient.h in Headers */, 4B2C21F01DA292BE00735907 /* OFHTTPCookie.h in Headers */, + 4B6255171EC274FA003D49F4 /* OFHTTPCookieManager.h in Headers */, 4B2C21F11DA292BE00735907 /* OFHTTPRequest.h in Headers */, 4B2C21F21DA292BE00735907 /* OFHTTPResponse.h in Headers */, 4B2C21F31DA292BE00735907 /* OFHTTPServer.h in Headers */, 4B2C21F41DA292BE00735907 /* OFINICategory.h in Headers */, 4B2C21F51DA292BE00735907 /* OFINIFile.h in Headers */, @@ -2627,10 +2640,11 @@ 4B2C728C1B888B8700717583 /* OFFileManager.h in Headers */, 4BD112621CCB73A90076FDB9 /* OFGZIPStream.h in Headers */, 4B6D0A891D4459D900901D8D /* OFHMAC.h in Headers */, 4BB4B54416775FF4002A2DCE /* OFHTTPClient.h in Headers */, 4B2610C31D863007001F16C9 /* OFHTTPCookie.h in Headers */, + 4B6255161EC274FA003D49F4 /* OFHTTPCookieManager.h in Headers */, 4B3D23CA1337FCB000DD29B8 /* OFHTTPRequest.h in Headers */, 4B7161AD17A6FC7600B74970 /* OFHTTPResponse.h in Headers */, 4BB4B54616775FF4002A2DCE /* OFHTTPServer.h in Headers */, 4B06855318B2AD3800FC731A /* OFINICategory.h in Headers */, 4B5B02BE18B288A400CE6AE4 /* OFINIFile.h in Headers */, @@ -3167,10 +3181,11 @@ 4B2C21341DA292BE00735907 /* OFFile.m in Sources */, 4B2C21351DA292BE00735907 /* OFFileManager.m in Sources */, 4B2C21361DA292BE00735907 /* OFHMAC.m in Sources */, 4B2C21371DA292BE00735907 /* OFHTTPClient.m in Sources */, 4B2C21381DA292BE00735907 /* OFHTTPCookie.m in Sources */, + 4B6255191EC274FA003D49F4 /* OFHTTPCookieManager.m in Sources */, 4B2C21391DA292BE00735907 /* OFHTTPRequest.m in Sources */, 4B2C213A1DA292BE00735907 /* OFHTTPResponse.m in Sources */, 4B2C213B1DA292BE00735907 /* OFHTTPServer.m in Sources */, 4B2C213C1DA292BE00735907 /* OFINICategory.m in Sources */, 4B2C213D1DA292BE00735907 /* OFINIFile.m in Sources */, @@ -3365,10 +3380,11 @@ 4B3D23961337FC0D00DD29B8 /* OFFile.m in Sources */, 4B2C728B1B888B8300717583 /* OFFileManager.m in Sources */, 4B6D0A8A1D4459D900901D8D /* OFHMAC.m in Sources */, 4BB4B54516775FF4002A2DCE /* OFHTTPClient.m in Sources */, 4B2610C41D863007001F16C9 /* OFHTTPCookie.m in Sources */, + 4B6255181EC274FA003D49F4 /* OFHTTPCookieManager.m in Sources */, 4B3D23981337FC0D00DD29B8 /* OFHTTPRequest.m in Sources */, 4B7161AE17A6FC7600B74970 /* OFHTTPResponse.m in Sources */, 4BB4B54716775FF4002A2DCE /* OFHTTPServer.m in Sources */, 4B06855418B2AD3800FC731A /* OFINICategory.m in Sources */, 4B5B02BF18B288A400CE6AE4 /* OFINIFile.m in Sources */, @@ -3555,10 +3571,11 @@ 4BD9CA041DA2C60700E5AD52 /* OFDateTests.m in Sources */, 4BD9CA051DA2C60900E5AD52 /* OFDictionaryTests.m in Sources */, 4BD9CA061DA2C60B00E5AD52 /* OFHMACTests.m in Sources */, 4BD9CA071DA2C60E00E5AD52 /* OFHTTPClientTests.m in Sources */, 4BD9CA081DA2C61900E5AD52 /* OFHTTPCookieTests.m in Sources */, + 4B11D09D1EC901440094423D /* OFHTTPCookieManagerTests.m in Sources */, 4BD9CA091DA2C61E00E5AD52 /* OFINIFileTests.m in Sources */, 4BD9CA0A1DA2C62000E5AD52 /* OFJSONTests.m in Sources */, 4BD9CA0B1DA2C62200E5AD52 /* OFKernelEventObserverTests.m in Sources */, 4BD9CA0C1DA2C62500E5AD52 /* OFListTests.m in Sources */, 4BD9CA0D1DA2C62800E5AD52 /* OFMD5HashTests.m in Sources */, @@ -3600,10 +3617,11 @@ 4BF33AFF133807A20059CEF7 /* OFDateTests.m in Sources */, 4BF33B00133807A20059CEF7 /* OFDictionaryTests.m in Sources */, 4BF0DD741D44645D001D9949 /* OFHMACTests.m in Sources */, 4BB4B54A16776094002A2DCE /* OFHTTPClientTests.m in Sources */, 4B2610C61D86305C001F16C9 /* OFHTTPCookieTests.m in Sources */, + 4B11D09C1EC901440094423D /* OFHTTPCookieManagerTests.m in Sources */, 4B5B02C118B2897500CE6AE4 /* OFINIFileTests.m in Sources */, 4BAA60C814D09699006F068D /* OFJSONTests.m in Sources */, 4B22BE201AE594A000CD320A /* OFKernelEventObserverTests.m in Sources */, 4BF33B03133807A20059CEF7 /* OFListTests.m in Sources */, 4BF33B04133807A20059CEF7 /* OFMD5HashTests.m in Sources */, Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -24,10 +24,14 @@ OFDeflate64Stream.m \ OFDictionary.m \ OFEnumerator.m \ OFGZIPStream.m \ OFHMAC.m \ + OFHTTPCookie.m \ + OFHTTPCookieManager.m \ + OFHTTPRequest.m \ + OFHTTPResponse.m \ OFIntrospection.m \ OFList.m \ OFLocalization.m \ OFMapTable.m \ OFMD5Hash.m \ @@ -100,13 +104,10 @@ OFINICategory.m \ OFINIFile.m \ OFSettings.m SRCS_PLUGINS = OFPlugin.m SRCS_SOCKETS = OFHTTPClient.m \ - OFHTTPCookie.m \ - OFHTTPRequest.m \ - OFHTTPResponse.m \ OFHTTPServer.m \ OFKernelEventObserver.m \ OFStreamSocket.m \ OFTCPSocket.m \ OFUDPSocket.m \ Index: src/OFHTTPCookie.h ================================================================== --- src/OFHTTPCookie.h +++ src/OFHTTPCookie.h @@ -46,21 +46,21 @@ @property (nonatomic, copy) OFString *value; /*! * The date when the cookie expires. */ -@property (nonatomic, copy) OFDate *expires; +@property OF_NULLABLE_PROPERTY (nonatomic, copy) OFDate *expires; /*! * The domain for the cookie. */ -@property (nonatomic, copy) OFString *domain; +@property OF_NULLABLE_PROPERTY (nonatomic, copy) OFString *domain; /*! * The path for the cookie. */ -@property (nonatomic, copy) OFString *path; +@property OF_NULLABLE_PROPERTY (nonatomic, copy) OFString *path; /*! * Whether the cookie is only to be used with HTTPS. */ @property (nonatomic, getter=isSecure) bool secure; ADDED src/OFHTTPCookieManager.h Index: src/OFHTTPCookieManager.h ================================================================== --- src/OFHTTPCookieManager.h +++ src/OFHTTPCookieManager.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 + * Jonathan Schleifer + * + * 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. + */ + +#import "OFObject.h" + +OF_ASSUME_NONNULL_BEGIN + +@class OFArray OF_GENERIC(ObjectType); +@class OFHTTPCookie; +@class OFMutableArray OF_GENERIC(ObjectType); +@class OFURL; + +/*! + * @class OFHTTPCookieManager OFHTTPCookieManager.h ObjFW/OFHTTPCookieManager.h + * + * @brief A class for managing cookies for multiple domains. + */ +@interface OFHTTPCookieManager: OFObject +{ + OFMutableArray OF_GENERIC(OFHTTPCookie *) *_cookies; +} + +/*! + * All cookies known to the cookie manager. + */ +@property (readonly, nonatomic) OFArray OF_GENERIC(OFHTTPCookie *) *cookies; + +/*! + * @brief Create a new cookie manager. + * + * @return A new, autoreleased OFHTTPCookieManager + */ ++ (instancetype)manager; + +/*! + * @brief Adds the specified cookie for the specified URL. + * + * @warning This modifies the cookie (e.g. it sets the domain if it is unset)! + * If you do not want this, pass a copy! + * + * @param cookie The cookie to add to the manager + * @param URL The URL for which the cookie should be added + */ +- (void)addCookie: (OFHTTPCookie *)cookie + forURL: (OFURL *)URL; + +/*! + * @brief Adds the specified cookies for the specified URL. + * + * @warning This modifies the cookies (e.g. it sets the domain if it is unset)! + * If you do not want this, pass copies! + * + * @param cookies An array of cookies to add to the manager + * @param URL The URL for which the cookies should be added + */ +- (void)addCookies: (OFArray OF_GENERIC(OFHTTPCookie *) *)cookies + forURL: (OFURL *)URL; + +/*! + * @brief Returns the cookies for the specified URL. + * + * @param URL The URL for which the cookies should be returned + * @return The cookies for the specified URL + */ +- (OFArray OF_GENERIC(OFHTTPCookie *) *)cookiesForURL: (OFURL *)URL; + +/*! + * @brief Purges all expired cookies. + */ +- (void)purgeExpiredCookies; +@end + +OF_ASSUME_NONNULL_END ADDED src/OFHTTPCookieManager.m Index: src/OFHTTPCookieManager.m ================================================================== --- src/OFHTTPCookieManager.m +++ src/OFHTTPCookieManager.m @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 + * Jonathan Schleifer + * + * 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 "OFHTTPCookie.h" +#import "OFURL.h" + +@implementation OFHTTPCookieManager ++ (instancetype)manager +{ + return [[[self alloc] init] autorelease]; +} + +- 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 domain] == nil) + [cookie setDomain: [URL host]]; + + if ([cookie path] == nil || ![[cookie path] hasPrefix: @"/"]) + [cookie setPath: @"/"]; + + if ([cookie isSecure] && ![[URL scheme] isEqual: @"https"]) { + objc_autoreleasePoolPop(pool); + return; + } + + cookieDomain = [[cookie domain] lowercaseString]; + [cookie setDomain: 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 isSecure] && ![[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 Index: src/OFHTTPRequest.h ================================================================== --- src/OFHTTPRequest.h +++ src/OFHTTPRequest.h @@ -16,14 +16,10 @@ #import "OFObject.h" #import "OFString.h" #import "OFHTTPCookie.h" -#ifndef OF_HAVE_SOCKETS -# error No sockets available! -#endif - OF_ASSUME_NONNULL_BEGIN @class OFURL; @class OFDictionary OF_GENERIC(KeyType, ObjectType); @class OFDataArray; Index: src/ObjFW.h ================================================================== --- src/ObjFW.h +++ src/ObjFW.h @@ -62,14 +62,16 @@ # import "OFStreamSocket.h" # import "OFTCPSocket.h" # import "OFUDPSocket.h" # import "OFTLSSocket.h" # import "OFKernelEventObserver.h" - -# import "OFHTTPRequest.h" -# import "OFHTTPResponse.h" -# import "OFHTTPCookie.h" +#endif +#import "OFHTTPCookie.h" +#import "OFHTTPCookieManager.h" +#import "OFHTTPRequest.h" +#import "OFHTTPResponse.h" +#ifdef OF_HAVE_SOCKETS # import "OFHTTPClient.h" # import "OFHTTPServer.h" #endif #ifdef OF_HAVE_PROCESSES Index: tests/Makefile ================================================================== --- tests/Makefile +++ tests/Makefile @@ -12,10 +12,12 @@ OFArrayTests.m \ ${OFBLOCKTESTS_M} \ OFDataArrayTests.m \ OFDateTests.m \ OFDictionaryTests.m \ + OFHTTPCookieTests.m \ + OFHTTPCookieManagerTests.m \ OFJSONTests.m \ OFListTests.m \ OFNumberTests.m \ OFObjectTests.m \ OFSetTests.m \ @@ -43,12 +45,11 @@ OFSHA224HashTests.m \ OFSHA256HashTests.m \ OFSHA384HashTests.m \ OFSHA512HashTests.m SRCS_PLUGINS = OFPluginTests.m -SRCS_SOCKETS = OFHTTPCookieTests.m \ - OFKernelEventObserverTests.m \ +SRCS_SOCKETS = OFKernelEventObserverTests.m \ OFTCPSocketTests.m \ OFUDPSocketTests.m SRCS_THREADS = OFThreadTests.m IOS_USER ?= mobile ADDED tests/OFHTTPCookieManagerTests.m Index: tests/OFHTTPCookieManagerTests.m ================================================================== --- tests/OFHTTPCookieManagerTests.m +++ tests/OFHTTPCookieManagerTests.m @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 + * Jonathan Schleifer + * + * 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 "OFHTTPCookie.h" +#import "OFURL.h" +#import "OFAutoreleasePool.h" + +#import "TestsAppDelegate.h" + +static OFString *module = @"OFHTTPCookieManager"; + +@implementation TestsAppDelegate (OFHTTPCookieManagerTests) +- (void)HTTPCookieManagerTests +{ + OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; + OFHTTPCookieManager *manager = [OFHTTPCookieManager manager]; + OFURL *URL[4]; + OFHTTPCookie *cookie[5]; + + URL[0] = [OFURL URLWithString: @"http://heap.zone/foo"]; + URL[1] = [OFURL URLWithString: @"https://heap.zone/foo/bar"]; + URL[2] = [OFURL URLWithString: @"https://test.heap.zone/foo/bar"]; + URL[3] = [OFURL URLWithString: @"http://webkeks.org/foo/bar"]; + + cookie[0] = [OFHTTPCookie cookieWithName: @"test" + value: @"1"]; + TEST(@"-[addCookie:forURL:] #1", R([manager addCookie: cookie[0] + forURL: URL[0]])) + + TEST(@"-[cookiesForURL:] #1", + [[manager cookiesForURL: URL[0]] isEqual: + [OFArray arrayWithObject: cookie[0]]]) + + cookie[1] = [OFHTTPCookie cookieWithName: @"test" + value: @"2"]; + [cookie[1] setDomain: @"webkeks.org"]; + TEST(@"-[addCookie:forURL:] #2", R([manager addCookie: cookie[1] + forURL: URL[0]])) + + TEST(@"-[cookiesForURL:] #2", + [[manager cookiesForURL: URL[0]] isEqual: + [OFArray arrayWithObject: cookie[0]]] && + [[manager cookiesForURL: URL[3]] isEqual: [OFArray array]]) + + cookie[2] = [OFHTTPCookie cookieWithName: @"test" + value: @"3"]; + [cookie[2] setSecure: true]; + TEST(@"-[addCookie:forURL:] #3", R([manager addCookie: cookie[2] + forURL: URL[1]])) + + TEST(@"-[cookiesForURL:] #3", + [[manager cookiesForURL: URL[1]] isEqual: + [OFArray arrayWithObject: cookie[2]]] && + [[manager cookiesForURL: URL[0]] isEqual: [OFArray array]]) + + [cookie[2] setExpires: [OFDate dateWithTimeIntervalSinceNow: -1]]; + cookie[3] = [OFHTTPCookie cookieWithName: @"test" + value: @"4"]; + [cookie[3] setDomain: @".heap.zone"]; + TEST(@"-[addCookie:forURL:] #4", R([manager addCookie: cookie[3] + forURL: URL[1]])) + + TEST(@"-[cookiesForURL:] #4", + [[manager cookiesForURL: URL[1]] isEqual: + [OFArray arrayWithObject: cookie[3]]] && + [[manager cookiesForURL: URL[2]] isEqual: + [OFArray arrayWithObject: cookie[3]]]) + + cookie[4] = [OFHTTPCookie cookieWithName: @"bar" + value: @"5"]; + [cookie[4] setDomain: @"test.heap.zone"]; + TEST(@"-[addCookie:forURL:] #5", R([manager addCookie: cookie[4] + forURL: URL[0]])) + + TEST(@"-[cookiesForURL:] #5", + [[manager cookiesForURL: URL[0]] isEqual: + [OFArray arrayWithObject: cookie[3]]] && + [[manager cookiesForURL: URL[2]] isEqual: + [OFArray arrayWithObjects: cookie[3], cookie[4], nil]]) + + TEST(@"-[purgeExpiredCookies]", + [[manager cookies] isEqual: + [OFArray arrayWithObjects: cookie[2], cookie[3], cookie[4], nil]] && + R([manager purgeExpiredCookies]) && + [[manager cookies] isEqual: + [OFArray arrayWithObjects: cookie[3], cookie[4], nil]]) + + [pool drain]; +} +@end Index: tests/TestsAppDelegate.h ================================================================== --- tests/TestsAppDelegate.h +++ tests/TestsAppDelegate.h @@ -101,10 +101,14 @@ @end @interface TestsAppDelegate (OFHTTPCookieTests) - (void)HTTPCookieTests; @end + +@interface TestsAppDelegate (OFHTTPCookieManagerTests) +- (void)HTTPCookieManagerTests; +@end @interface TestsAppDelegate (OFINIFileTests) - (void)INIFileTests; @end Index: tests/TestsAppDelegate.m ================================================================== --- tests/TestsAppDelegate.m +++ tests/TestsAppDelegate.m @@ -419,13 +419,12 @@ #endif [self URLTests]; #if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_THREADS) [self HTTPClientTests]; #endif -#ifdef OF_HAVE_SOCKETS [self HTTPCookieTests]; -#endif + [self HTTPCookieManagerTests]; [self XMLParserTests]; [self XMLNodeTests]; [self XMLElementBuilderTests]; #ifdef OF_HAVE_FILES [self serializationTests];