Index: ObjFW.xcodeproj/project.pbxproj ================================================================== --- ObjFW.xcodeproj/project.pbxproj +++ ObjFW.xcodeproj/project.pbxproj @@ -349,10 +349,12 @@ 4B90B7A3133AD87D00BD33CB /* OFBindFailedException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B90B799133AD87D00BD33CB /* OFBindFailedException.m */; }; 4B90B7A4133AD87D00BD33CB /* OFConnectionFailedException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B90B79A133AD87D00BD33CB /* OFConnectionFailedException.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B90B7A5133AD87D00BD33CB /* OFConnectionFailedException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B90B79B133AD87D00BD33CB /* OFConnectionFailedException.m */; }; 4B90B7A6133AD87D00BD33CB /* OFListenFailedException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B90B79C133AD87D00BD33CB /* OFListenFailedException.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B90B7A7133AD87D00BD33CB /* OFListenFailedException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B90B79D133AD87D00BD33CB /* OFListenFailedException.m */; }; + 4B91FD12196B4F5900C5C25E /* OFUnknownXMLEntityException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B91FD10196B4F5900C5C25E /* OFUnknownXMLEntityException.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4B91FD13196B4F5900C5C25E /* OFUnknownXMLEntityException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B91FD11196B4F5900C5C25E /* OFUnknownXMLEntityException.m */; }; 4B9361A81511000C00DCD16B /* OFThreadPool.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B9361A61511000C00DCD16B /* OFThreadPool.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B9361A91511000C00DCD16B /* OFThreadPool.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B9361A71511000C00DCD16B /* OFThreadPool.m */; }; 4B989C2F13771A3700109A30 /* OFSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B989C2E13771A3700109A30 /* OFSerialization.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B9BB7BD141CDE2D000AD1CC /* OFArray_adjacentSubarray.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B9BB7B9141CDE2D000AD1CC /* OFArray_adjacentSubarray.h */; }; 4B9BB7BE141CDE2D000AD1CC /* OFArray_adjacentSubarray.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B9BB7BA141CDE2D000AD1CC /* OFArray_adjacentSubarray.m */; }; @@ -813,10 +815,12 @@ 4B90B799133AD87D00BD33CB /* OFBindFailedException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFBindFailedException.m; path = src/exceptions/OFBindFailedException.m; sourceTree = ""; }; 4B90B79A133AD87D00BD33CB /* OFConnectionFailedException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFConnectionFailedException.h; path = src/exceptions/OFConnectionFailedException.h; sourceTree = ""; }; 4B90B79B133AD87D00BD33CB /* OFConnectionFailedException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFConnectionFailedException.m; path = src/exceptions/OFConnectionFailedException.m; sourceTree = ""; }; 4B90B79C133AD87D00BD33CB /* OFListenFailedException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFListenFailedException.h; path = src/exceptions/OFListenFailedException.h; sourceTree = ""; }; 4B90B79D133AD87D00BD33CB /* OFListenFailedException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFListenFailedException.m; path = src/exceptions/OFListenFailedException.m; sourceTree = ""; }; + 4B91FD10196B4F5900C5C25E /* OFUnknownXMLEntityException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFUnknownXMLEntityException.h; path = src/exceptions/OFUnknownXMLEntityException.h; sourceTree = ""; }; + 4B91FD11196B4F5900C5C25E /* OFUnknownXMLEntityException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFUnknownXMLEntityException.m; path = src/exceptions/OFUnknownXMLEntityException.m; sourceTree = ""; }; 4B9361A61511000C00DCD16B /* OFThreadPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFThreadPool.h; path = src/OFThreadPool.h; sourceTree = ""; }; 4B9361A71511000C00DCD16B /* OFThreadPool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFThreadPool.m; path = src/OFThreadPool.m; sourceTree = ""; }; 4B981CDE116F71DD00294DB7 /* OFSeekableStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFSeekableStream.h; path = src/OFSeekableStream.h; sourceTree = ""; }; 4B981CDF116F71DD00294DB7 /* OFSeekableStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFSeekableStream.m; path = src/OFSeekableStream.m; sourceTree = ""; }; 4B989C2E13771A3700109A30 /* OFSerialization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFSerialization.h; path = src/OFSerialization.h; sourceTree = ""; }; @@ -1069,10 +1073,12 @@ 4B17FFA8133A34E1003E6DCD /* OFTruncatedDataException.m */, 4B17FFB3133A374C003E6DCD /* OFUnboundNamespaceException.h */, 4B17FFB4133A3750003E6DCD /* OFUnboundNamespaceException.m */, 4BD77FDC176E4BC40031C497 /* OFUnboundPrefixException.h */, 4BD77FDD176E4BC40031C497 /* OFUnboundPrefixException.m */, + 4B91FD10196B4F5900C5C25E /* OFUnknownXMLEntityException.h */, + 4B91FD11196B4F5900C5C25E /* OFUnknownXMLEntityException.m */, 4B6743EF163C384A00EB1E59 /* OFUnlockFailedException.h */, 4B6743F0163C384A00EB1E59 /* OFUnlockFailedException.m */, 4B17FFAF133A3658003E6DCD /* OFUnsupportedProtocolException.h */, 4B17FFB0133A365C003E6DCD /* OFUnsupportedProtocolException.m */, 4BA4846015CC9F1E00D75360 /* OFUnsupportedVersionException.h */, @@ -1675,10 +1681,11 @@ 4B55A0FF133ABEA900B58A93 /* OFThreadJoinFailedException.h in Headers */, 4B55A101133ABEA900B58A93 /* OFThreadStartFailedException.h in Headers */, 4B55A103133ABEA900B58A93 /* OFThreadStillRunningException.h in Headers */, 4B17FFB5133A375B003E6DCD /* OFUnboundNamespaceException.h in Headers */, 4BD77FDE176E4BC40031C497 /* OFUnboundPrefixException.h in Headers */, + 4B91FD12196B4F5900C5C25E /* OFUnknownXMLEntityException.h in Headers */, 4B6743F5163C384A00EB1E59 /* OFUnlockFailedException.h in Headers */, 4B17FFB1133A3664003E6DCD /* OFUnsupportedProtocolException.h in Headers */, 4BA4846215CC9F1E00D75360 /* OFUnsupportedVersionException.h in Headers */, 4B55A116133AC24600B58A93 /* OFWriteFailedException.h in Headers */, 4B55A109133AC05100B58A93 /* common.h in Headers */, @@ -2053,10 +2060,11 @@ 4B55A102133ABEA900B58A93 /* OFThreadStartFailedException.m in Sources */, 4B55A104133ABEA900B58A93 /* OFThreadStillRunningException.m in Sources */, 4B17FFAA133A34E7003E6DCD /* OFTruncatedDataException.m in Sources */, 4B17FFB6133A375B003E6DCD /* OFUnboundNamespaceException.m in Sources */, 4BD77FDF176E4BC40031C497 /* OFUnboundPrefixException.m in Sources */, + 4B91FD13196B4F5900C5C25E /* OFUnknownXMLEntityException.m in Sources */, 4B6743F6163C384A00EB1E59 /* OFUnlockFailedException.m in Sources */, 4B17FFB2133A3664003E6DCD /* OFUnsupportedProtocolException.m in Sources */, 4BA4846315CC9F1E00D75360 /* OFUnsupportedVersionException.m in Sources */, 4B55A117133AC24600B58A93 /* OFWriteFailedException.m in Sources */, 4B5C112F17E9AB3E003C917F /* forwarding.S in Sources */, Index: src/OFString+XMLUnescaping.m ================================================================== --- src/OFString+XMLUnescaping.m +++ src/OFString+XMLUnescaping.m @@ -19,10 +19,11 @@ #include #import "OFString.h" #import "OFInvalidFormatException.h" +#import "OFUnknownXMLEntityException.h" int _OFString_XMLUnescaping_reference; static OF_INLINE OFString* parseNumericEntity(const char *entity, size_t length) @@ -151,12 +152,12 @@ stringWithUTF8String: entity length: entityLength]; tmp = lookup(context, self, name); if (tmp == nil) - @throw [OFInvalidFormatException - exception]; + @throw [OFUnknownXMLEntityException + exceptionWithEntityName: name]; [ret appendString: tmp]; objc_autoreleasePoolPop(pool); } Index: src/OFXMLParser.m ================================================================== --- src/OFXMLParser.m +++ src/OFXMLParser.m @@ -31,10 +31,11 @@ # import "OFFile.h" #endif #import "OFSystemInfo.h" #import "OFInitializationFailedException.h" +#import "OFInvalidFormatException.h" #import "OFMalformedXMLException.h" #import "OFUnboundPrefixException.h" typedef void (*state_function_t)(id, SEL); static SEL selectors[OF_XMLPARSER_NUM_STATES]; @@ -57,12 +58,12 @@ objc_autoreleasePoolPop(pool); } } static OFString* -transformString(OFDataArray *buffer, size_t cut, bool unescape, - id delegate) +transformString(OFXMLParser *parser, OFDataArray *buffer, size_t cut, + bool unescape) { char *items; size_t i, length; bool hasEntities = false; OFString *ret; @@ -85,12 +86,18 @@ } ret = [OFString stringWithUTF8String: items length: length]; - if (unescape && hasEntities) - return [ret stringByXMLUnescapingWithDelegate: delegate]; + if (unescape && hasEntities) { + @try { + return [ret stringByXMLUnescapingWithDelegate: parser]; + } @catch (OFInvalidFormatException *e) { + @throw [OFMalformedXMLException + exceptionWithParser: parser]; + } + } return ret; } static OFString* @@ -349,11 +356,11 @@ if ((length = _i - _last) > 0) appendToBuffer(_buffer, _data + _last, _encoding, length); if ([_buffer count] > 0) { void *pool = objc_autoreleasePoolPush(); - OFString *characters = transformString(_buffer, 0, true, self); + OFString *characters = transformString(self, _buffer, 0, true); if ([_delegate respondsToSelector: @selector(parser:foundCharacters:)]) [_delegate parser: self foundCharacters: characters]; @@ -511,11 +518,11 @@ else if (_level == 1 && _data[_i] == '>') { void *pool = objc_autoreleasePoolPush(); OFString *PI; appendToBuffer(_buffer, _data + _last, _encoding, _i - _last); - PI = transformString(_buffer, 1, false, nil); + PI = transformString(self, _buffer, 1, false); if ([PI isEqual: @"xml"] || [PI hasPrefix: @"xml "] || [PI hasPrefix: @"xml\t"] || [PI hasPrefix: @"xml\r"] || [PI hasPrefix: @"xml\n"]) if (![self OF_parseXMLProcessingInstructions: PI]) @@ -854,11 +861,11 @@ if ((length = _i - _last) > 0) appendToBuffer(_buffer, _data + _last, _encoding, length); pool = objc_autoreleasePoolPush(); - attributeValue = transformString(_buffer, 0, true, self); + attributeValue = transformString(self, _buffer, 0, true); if (_attributePrefix == nil && [_attributeName isEqual: @"xmlns"]) [[_namespaces lastObject] setObject: attributeValue forKey: @""]; if ([_attributePrefix isEqual: @"xmlns"]) @@ -944,11 +951,11 @@ else if (_data[_i] == '>' && _level >= 2) { void *pool = objc_autoreleasePoolPush(); OFString *CDATA; appendToBuffer(_buffer, _data + _last, _encoding, _i - _last); - CDATA = transformString(_buffer, 2, false, nil); + CDATA = transformString(self, _buffer, 2, false); if ([_delegate respondsToSelector: @selector(parser:foundCDATA:)]) [_delegate parser: self foundCDATA: CDATA]; @@ -994,11 +1001,11 @@ @throw [OFMalformedXMLException exceptionWithParser: self]; pool = objc_autoreleasePoolPush(); appendToBuffer(_buffer, _data + _last, _encoding, _i - _last); - comment = transformString(_buffer, 2, false, nil); + comment = transformString(self, _buffer, 2, false); if ([_delegate respondsToSelector: @selector(parser:foundComment:)]) [_delegate parser: self foundComment: comment]; Index: src/exceptions/Makefile ================================================================== --- src/exceptions/Makefile +++ src/exceptions/Makefile @@ -36,10 +36,11 @@ OFSetOptionFailedException.m \ OFStillLockedException.m \ OFTruncatedDataException.m \ OFUnboundNamespaceException.m \ OFUnboundPrefixException.m \ + OFUnknownXMLEntityException.m \ OFUnlockFailedException.m \ OFUnsupportedProtocolException.m \ OFUnsupportedVersionException.m \ OFWriteFailedException.m \ ${USE_SRCS_SOCKETS} \ ADDED src/exceptions/OFUnknownXMLEntityException.h Index: src/exceptions/OFUnknownXMLEntityException.h ================================================================== --- src/exceptions/OFUnknownXMLEntityException.h +++ src/exceptions/OFUnknownXMLEntityException.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 + * 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 "OFException.h" + +/*! + * @class OFUnknownXMLEntityException \ + * OFUnknownXMLEntityException.h ObjFW/OFUnknownXMLEntityException.h + * + * @brief An exception indicating that a parser encountered an unknown XML + * entity. + */ +@interface OFUnknownXMLEntityException: OFException +{ + OFString *_entityName; +} + +#ifdef OF_HAVE_PROPERTIES +@property (readonly, copy) OFString *entityName; +#endif + +/*! + * @brief Creates a new, autoreleased unknown XML entity exception. + * + * @param entityName The name of the unknown XML entity + * @return A new, autoreleased unknown XML entity exception + */ ++ (instancetype)exceptionWithEntityName: (OFString*)entityName; + +/*! + * @brief Initializes an already allocated unknown XML entity exception. + * + * @param entityName The name of the unknown XML entity + * @return An initialized unknown XML entity exception + */ +- initWithEntityName: (OFString*)entityName; + +/*! + * @brief Returns the name of the unknown XML entity + * + * @return The name of the unknown XML entity + */ +- (OFString*)entityName; +@end ADDED src/exceptions/OFUnknownXMLEntityException.m Index: src/exceptions/OFUnknownXMLEntityException.m ================================================================== --- src/exceptions/OFUnknownXMLEntityException.m +++ src/exceptions/OFUnknownXMLEntityException.m @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 + * 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 "OFUnknownXMLEntityException.h" +#import "OFString.h" + +#import "common.h" + +@implementation OFUnknownXMLEntityException ++ (instancetype)exceptionWithEntityName: (OFString*)entityName +{ + return [[[self alloc] initWithEntityName: entityName] autorelease]; +} + +- initWithEntityName: (OFString*)entityName +{ + self = [super init]; + + @try { + _entityName = [entityName copy]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_entityName release]; + + [super dealloc]; +} + +- (OFString*)description +{ + return [OFString stringWithFormat: + @"A parser encountered an unknown XML entity named %@!", + _entityName]; +} + +- (OFString*)entityName +{ + OF_GETTER(_entityName, true) +} +@end Index: tests/OFStringTests.m ================================================================== --- tests/OFStringTests.m +++ tests/OFStringTests.m @@ -28,10 +28,11 @@ #import "OFInvalidArgumentException.h" #import "OFInvalidEncodingException.h" #import "OFInvalidFormatException.h" #import "OFOutOfRangeException.h" +#import "OFUnknownXMLEntityException.h" #import "macros.h" #import "TestsAppDelegate.h" @@ -663,22 +664,22 @@ [[@"y" stringByXMLUnescaping] isEqual: @"y"] && [[@"ä" stringByXMLUnescaping] isEqual: @"ä"] && [[@"€" stringByXMLUnescaping] isEqual: @"€"] && [[@"𝄞" stringByXMLUnescaping] isEqual: @"𝄞"]) - EXPECT_EXCEPTION(@"Detect invalid entities in -[stringByXMLUnescaping] " - @"#1", OFInvalidFormatException, [@"&foo;" stringByXMLUnescaping]) - EXPECT_EXCEPTION(@"Detect invalid entities in -[stringByXMLUnescaping] " - @"#2", OFInvalidFormatException, [@"x&" stringByXMLUnescaping]) - EXPECT_EXCEPTION(@"Detect invalid entities in -[stringByXMLUnescaping] " - @"#3", OFInvalidFormatException, [@"&#;" stringByXMLUnescaping]) - EXPECT_EXCEPTION(@"Detect invalid entities in -[stringByXMLUnescaping] " - @"#4", OFInvalidFormatException, [@"&#x;" stringByXMLUnescaping]) - EXPECT_EXCEPTION(@"Detect invalid entities in -[stringByXMLUnescaping] " - @"#5", OFInvalidFormatException, [@"&#g;" stringByXMLUnescaping]) - EXPECT_EXCEPTION(@"Detect invalid entities in -[stringByXMLUnescaping] " - @"#6", OFInvalidFormatException, [@"&#xg;" stringByXMLUnescaping]) + EXPECT_EXCEPTION(@"Detect unknown entities in -[stringByXMLUnescaping]", + OFUnknownXMLEntityException, [@"&foo;" stringByXMLUnescaping]) + EXPECT_EXCEPTION(@"Detect invalid entities in -[stringByXMLUnescaping] " + @"#1", OFInvalidFormatException, [@"x&" stringByXMLUnescaping]) + EXPECT_EXCEPTION(@"Detect invalid entities in -[stringByXMLUnescaping] " + @"#2", OFInvalidFormatException, [@"&#;" stringByXMLUnescaping]) + EXPECT_EXCEPTION(@"Detect invalid entities in -[stringByXMLUnescaping] " + @"#3", OFInvalidFormatException, [@"&#x;" stringByXMLUnescaping]) + EXPECT_EXCEPTION(@"Detect invalid entities in -[stringByXMLUnescaping] " + @"#4", OFInvalidFormatException, [@"&#g;" stringByXMLUnescaping]) + EXPECT_EXCEPTION(@"Detect invalid entities in -[stringByXMLUnescaping] " + @"#5", OFInvalidFormatException, [@"&#xg;" stringByXMLUnescaping]) TEST(@"-[stringByXMLUnescapingWithDelegate:]", (h = [[[EntityHandler alloc] init] autorelease]) && [[@"x&foo;y" stringByXMLUnescapingWithDelegate: h] isEqual: @"xbary"])