From fa2dd9c45fbbc25cfa47265737124526ef1ae007 Mon Sep 17 00:00:00 2001 From: Andrew Lindesay Date: Sat, 9 Apr 2016 18:49:29 +1200 Subject: [PATCH] BPackageInfo::Parser: Validate URL strings. Fixes #12710. Signed-off-by: Augustin Cavalier I fixed the modifications to the Jamfiles in src/bin, they were all wrong in the patch. --- build/jam/BuildSetup | 4 +- docs/develop/build/libgnuregex.md | 12 + headers/os/net/Url.h | 25 +- src/bin/Jamfile | 2 +- src/bin/package/Jamfile | 2 +- src/build/libbe/Jamfile | 4 + src/build/libbe/network/Jamfile | 15 + src/build/libgnuregex/Jamfile | 6 +- src/build/libpackage/Jamfile | 2 + src/build/libshared/Jamfile | 7 + src/kits/network/libnetapi/Url.cpp | 102 ++++--- src/kits/package/Jamfile | 2 + src/kits/package/PackageInfoParser.cpp | 39 ++- src/kits/package/PackageInfoParser.h | 19 +- src/tests/kits/net/libnetapi/Jamfile | 1 + .../kits/net/libnetapi/NetAPITestAddon.cpp | 2 + .../kits/net/libnetapi/NetworkUrlTest.cpp | 272 ++++++++++++++++++ src/tests/kits/net/libnetapi/NetworkUrlTest.h | 42 +++ 18 files changed, 502 insertions(+), 56 deletions(-) create mode 100644 docs/develop/build/libgnuregex.md create mode 100644 src/build/libbe/network/Jamfile create mode 100644 src/tests/kits/net/libnetapi/NetworkUrlTest.cpp create mode 100644 src/tests/kits/net/libnetapi/NetworkUrlTest.h diff --git a/build/jam/BuildSetup b/build/jam/BuildSetup index c79abdf121..0781dd44ef 100644 --- a/build/jam/BuildSetup +++ b/build/jam/BuildSetup @@ -496,8 +496,8 @@ if $(HOST_PLATFORM_BEOS_COMPATIBLE) { HOST_LIBROOT += /usr/lib/libgnuregex.so ; HOST_STATIC_LIBROOT += /usr/lib/libgnuregex.so ; } else if $(HOST_PLATFORM) = darwin { - HOST_LIBROOT += libgnuregex_build.a ; - HOST_STATIC_LIBROOT += libgnuregex_build.a ; + HOST_LIBROOT += libgnuregex_build.so ; + HOST_STATIC_LIBROOT += libgnuregex_build.so ; } # The BeOS compilers define __INTEL__ respectively __POWERPC__. On the diff --git a/docs/develop/build/libgnuregex.md b/docs/develop/build/libgnuregex.md new file mode 100644 index 0000000000..7dadd290ec --- /dev/null +++ b/docs/develop/build/libgnuregex.md @@ -0,0 +1,12 @@ +# libgnuregex + +This library exists because some systems don't have a flavor of regex library +built-in which supports groups. This variant does include group-support. An +example of where this comes into play is with the `BUrl` class where URLs are +parsed, in part, using regular expressions. + +## Use with MacOS-X + +In the case of MacOS-X, the dynamic-library build product +`libgnuregex_build.so` can be configured for use by configuring the +`DYLD_INSERT_LIBRARIES` environment variable. diff --git a/headers/os/net/Url.h b/headers/os/net/Url.h index da241cc30e..560e08b845 100644 --- a/headers/os/net/Url.h +++ b/headers/os/net/Url.h @@ -1,5 +1,5 @@ /* - * Copyright 2010 Haiku Inc. All rights reserved. + * Copyright 2010-2016 Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. */ #ifndef _B_URL_H_ @@ -33,7 +33,7 @@ public: BUrl& SetPath(const BString& path); BUrl& SetRequest(const BString& request); BUrl& SetFragment(const BString& fragment); - + // URL fields access const BString& UrlString() const; const BString& Protocol() const; @@ -46,7 +46,7 @@ public: const BString& Path() const; const BString& Request() const; const BString& Fragment() const; - + // URL fields tests bool IsValid() const; bool HasProtocol() const; @@ -64,21 +64,25 @@ public: void UrlEncode(bool strict = false); void UrlDecode(bool strict = false); +#ifdef HAIKU_TARGET_PLATFORM_HAIKU status_t IDNAToAscii(); status_t IDNAToUnicode(); +#endif // Url encoding/decoding of strings - static BString UrlEncode(const BString& url, - bool strict = false, + static BString UrlEncode(const BString& url, + bool strict = false, bool directory = false); - static BString UrlDecode(const BString& url, + static BString UrlDecode(const BString& url, bool strict = false); +#ifdef HAIKU_TARGET_PLATFORM_HAIKU // utility functionality bool HasPreferredApplication() const; BString PreferredApplication() const; status_t OpenWithPreferredApplication( bool onProblemAskUser = true) const; +#endif // BArchivable members virtual status_t Archive(BMessage* into, @@ -93,19 +97,20 @@ public: const BUrl& operator=(const BUrl& other); const BUrl& operator=(const BString& string); const BUrl& operator=(const char* string); - + // URL to string conversion operator const char*() const; private: void _ResetFields(); + bool _ContainsDelimiter(const BString& url); void _ExplodeUrlString(const BString& urlString); BString _MergePath(const BString& relative) const; void _SetPathUnsafe(const BString& path); - static BString _DoUrlEncodeChunk(const BString& chunk, + static BString _DoUrlEncodeChunk(const BString& chunk, bool strict, bool directory = false); - static BString _DoUrlDecodeChunk(const BString& chunk, + static BString _DoUrlDecodeChunk(const BString& chunk, bool strict); bool _IsProtocolValid(); @@ -128,7 +133,7 @@ private: BString fPath; BString fRequest; BString fFragment; - + mutable bool fUrlStringValid : 1; mutable bool fAuthorityValid : 1; mutable bool fUserInfoValid : 1; diff --git a/src/bin/Jamfile b/src/bin/Jamfile index de947dc57e..db293aa955 100644 --- a/src/bin/Jamfile +++ b/src/bin/Jamfile @@ -134,7 +134,7 @@ StdBinCommands ramdisk.cpp : shared be [ TargetLibsupc++ ] : $(haiku-utils_rsrc) ; -# standard commands that need libbe.so, libbnetapi.solibsupc++.so +# standard commands that need libbe.so, libbnetapi.so, libsupc++.so StdBinCommands open.cpp urlwrapper.cpp diff --git a/src/bin/package/Jamfile b/src/bin/package/Jamfile index 81ae7c33b0..b42984a827 100644 --- a/src/bin/package/Jamfile +++ b/src/bin/package/Jamfile @@ -15,6 +15,6 @@ BinCommand package : PackageWriterListener.cpp PackageWritingUtils.cpp : - package be + package be bnetapi [ TargetLibsupc++ ] ; diff --git a/src/build/libbe/Jamfile b/src/build/libbe/Jamfile index 70f487ede2..0429a29261 100644 --- a/src/build/libbe/Jamfile +++ b/src/build/libbe/Jamfile @@ -11,14 +11,18 @@ BuildPlatformSharedLibrary libbe_build.so : app_kit.o icon_kit.o interface_kit.o + network_kit.o storage_kit.o support_kit.o + libshared_build.a + z $(HOST_LIBSUPC++) $(HOST_LIBSTDC++) ; SubInclude HAIKU_TOP src build libbe app ; SubInclude HAIKU_TOP src build libbe icon ; SubInclude HAIKU_TOP src build libbe interface ; +SubInclude HAIKU_TOP src build libbe network ; SubInclude HAIKU_TOP src build libbe storage ; SubInclude HAIKU_TOP src build libbe support ; diff --git a/src/build/libbe/network/Jamfile b/src/build/libbe/network/Jamfile new file mode 100644 index 0000000000..c824cec7d5 --- /dev/null +++ b/src/build/libbe/network/Jamfile @@ -0,0 +1,15 @@ +SubDir HAIKU_TOP src build libbe network; + +UseHeaders [ FDirName $(HAIKU_TOP) headers build ] : true ; +UseHeaders [ FDirName $(HAIKU_TOP) headers os net ] : true ; +UseHeaders [ FDirName $(HAIKU_TOP) headers private shared ] : true ; + +UsePrivateBuildHeaders app interface shared network ; + +USES_BE_API on network_kit.o = true ; + +SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src kits network libnetapi ] ; + +BuildPlatformMergeObjectPIC network_kit.o : + Url.cpp +; diff --git a/src/build/libgnuregex/Jamfile b/src/build/libgnuregex/Jamfile index 6f3d05bf64..cfcc8ef53f 100644 --- a/src/build/libgnuregex/Jamfile +++ b/src/build/libgnuregex/Jamfile @@ -1,3 +1,7 @@ SubDir HAIKU_TOP src build libgnuregex ; -BuildPlatformStaticLibrary libgnuregex_build.a : regex.c ; +BuildPlatformSharedLibrary libgnuregex_build.so : + regex.c + : + # no linked libraries here +; diff --git a/src/build/libpackage/Jamfile b/src/build/libpackage/Jamfile index fa1b4e2fa9..83cfa8b5db 100644 --- a/src/build/libpackage/Jamfile +++ b/src/build/libpackage/Jamfile @@ -2,6 +2,8 @@ SubDir HAIKU_TOP src build libpackage ; UsePrivateBuildHeaders kernel package shared storage support ; +UseHeaders [ FDirName $(HAIKU_TOP) headers os net ] : true ; + SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src kits package ] ; SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src kits package hpkg ] ; SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src kits package hpkg v1 ] ; diff --git a/src/build/libshared/Jamfile b/src/build/libshared/Jamfile index 44984b7097..3999b00456 100644 --- a/src/build/libshared/Jamfile +++ b/src/build/libshared/Jamfile @@ -2,6 +2,8 @@ SubDir HAIKU_TOP src build libshared ; USES_BE_API on libshared_build.a = true ; +UseHeaders [ FDirName $(HAIKU_TOP) headers private shared ] : true ; + UsePrivateBuildHeaders shared ; SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src kits shared ] ; @@ -10,4 +12,9 @@ BuildPlatformStaticLibraryPIC libshared_build.a : Keymap.cpp NaturalCompare.cpp SHA256.cpp + RegExp.cpp + + : + + # no shared libs, but will require 'libgnuregex' dynamic library on Darwin ; diff --git a/src/kits/network/libnetapi/Url.cpp b/src/kits/network/libnetapi/Url.cpp index e68b535d1e..d24f026720 100644 --- a/src/kits/network/libnetapi/Url.cpp +++ b/src/kits/network/libnetapi/Url.cpp @@ -1,9 +1,10 @@ /* - * Copyright 2010 Haiku Inc. All rights reserved. + * Copyright 2010-2016 Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Christophe Huriaux, c.huriaux@gmail.com + * Andrew Lindesay, apl@lindesay.co.nz */ @@ -17,11 +18,15 @@ #include #include -#include +#ifdef HAIKU_TARGET_PLATFORM_HAIKU + #include +#endif #include -#include -#include +#ifdef HAIKU_TARGET_PLATFORM_HAIKU + #include + #include +#endif static const char* kArchivedUrl = "be:url string"; @@ -498,8 +503,14 @@ BUrl::Fragment() const bool BUrl::IsValid() const { + if (!fHasProtocol) + return false; + + if (fProtocol == "http" || fProtocol == "https" || fProtocol == "ftp") + return fHasHost; + // TODO: Implement for real! - return fHasProtocol && (fHasHost || fHasPath); + return fHasHost || fHasPath; } @@ -598,6 +609,7 @@ BUrl::UrlDecode(bool strict) } +#ifdef HAIKU_TARGET_PLATFORM_HAIKU status_t BUrl::IDNAToAscii() { @@ -618,8 +630,10 @@ BUrl::IDNAToAscii() fHost = result; return B_OK; } +#endif +#ifdef HAIKU_TARGET_PLATFORM_HAIKU status_t BUrl::IDNAToUnicode() { @@ -640,11 +654,13 @@ BUrl::IDNAToUnicode() fHost = result; return B_OK; } +#endif // #pragma mark - utility functionality +#ifdef HAIKU_TARGET_PLATFORM_HAIKU bool BUrl::HasPreferredApplication() const { @@ -657,8 +673,10 @@ BUrl::HasPreferredApplication() const return false; } +#endif +#ifdef HAIKU_TARGET_PLATFORM_HAIKU BString BUrl::PreferredApplication() const { @@ -669,8 +687,10 @@ BUrl::PreferredApplication() const return BString(appSignature); } +#endif +#ifdef HAIKU_TARGET_PLATFORM_HAIKU status_t BUrl::OpenWithPreferredApplication(bool onProblemAskUser) const { @@ -710,6 +730,7 @@ BUrl::OpenWithPreferredApplication(bool onProblemAskUser) const return status; } +#endif // #pragma mark Url encoding/decoding of string @@ -866,6 +887,28 @@ BUrl::_ResetFields() } +bool +BUrl::_ContainsDelimiter(const BString& url) +{ + int32 len = url.Length(); + + for (int32 i = 0; i < len; i++) { + switch (url[i]) { + case ' ': + case '\n': + case '\t': + case '\r': + case '<': + case '>': + case '"': + return true; + } + } + + return false; +} + + void BUrl::_ExplodeUrlString(const BString& url) { @@ -875,6 +918,11 @@ BUrl::_ExplodeUrlString(const BString& url) _ResetFields(); + // RFC3986, Appendix C; the URL should not contain whitespace or delimiters + // by this point. + if (_ContainsDelimiter(url)) + return; // TODO error handing + RegExp::MatchResult match = urlMatcher.Match(url.String()); if (!match.HasMatched()) @@ -883,6 +931,7 @@ BUrl::_ExplodeUrlString(const BString& url) // Scheme/Protocol url.CopyInto(fProtocol, match.GroupStartOffsetAt(1), match.GroupEndOffsetAt(1) - match.GroupStartOffsetAt(1)); + if (!_IsProtocolValid()) { fHasProtocol = false; fProtocol.Truncate(0); @@ -981,6 +1030,7 @@ BUrl::SetAuthority(const BString& authority) return; int32 userInfoEnd = fAuthority.FindFirst('@'); + int16 hostAndPortStart = 0; // URL contains userinfo field if (userInfoEnd != -1) { @@ -1000,39 +1050,23 @@ BUrl::SetAuthority(const BString& authority) } else { SetUserName(fUser); } + + hostAndPortStart = userInfoEnd + 1; } + int16 hostEnd = fAuthority.FindFirst(':', hostAndPortStart); - // Extract the host part - int16 hostEnd = fAuthority.FindFirst(':', userInfoEnd); - userInfoEnd++; - - if (hostEnd < 0) { - // no ':' found, the host extends to the end of the URL - hostEnd = fAuthority.Length() + 1; + if (hostEnd != B_ERROR) { + if (hostEnd < fAuthority.Length()-1) { + fPort = atoi(&(fAuthority.String())[hostEnd+1]); + fHasPort = true; + } } + else + hostEnd = fAuthority.Length(); - // The host is likely to be present if an authority is - // defined, but in some weird cases, it's not. - if (hostEnd != userInfoEnd) { - fAuthority.CopyInto(fHost, userInfoEnd, hostEnd - userInfoEnd); - SetHost(fHost); - } - - // Extract the port part - fPort = 0; - if (fAuthority.ByteAt(hostEnd) == ':') { - hostEnd++; - int16 portEnd = fAuthority.Length(); - - BString portString; - fAuthority.CopyInto(portString, hostEnd, portEnd - hostEnd); - fPort = atoi(portString.String()); - - // Even if the port is invalid, the URL is considered to - // have a port. - fHasPort = portString.Length() > 0; - } + fAuthority.CopyInto(fHost, hostAndPortStart, hostEnd - hostAndPortStart); + SetHost(fHost); } @@ -1088,7 +1122,7 @@ BUrl::_DoUrlDecodeChunk(const BString& chunk, bool strict) result << decoded; } else result << chunk[i]; - } + } } return result; } diff --git a/src/kits/package/Jamfile b/src/kits/package/Jamfile index 013c9ee037..7d221f2cf0 100644 --- a/src/kits/package/Jamfile +++ b/src/kits/package/Jamfile @@ -129,9 +129,11 @@ for architectureObject in [ MultiArchSubDirSetup ] { SolverResult.cpp : shared + bnetapi be [ BuildFeatureAttribute curl : library ] [ TargetLibstdc++ ] + $(TARGET_NETWORK_LIBS) ; } } diff --git a/src/kits/package/PackageInfoParser.cpp b/src/kits/package/PackageInfoParser.cpp index f684815ec4..3de3a7cacb 100644 --- a/src/kits/package/PackageInfoParser.cpp +++ b/src/kits/package/PackageInfoParser.cpp @@ -1,5 +1,6 @@ /* * Copyright 2011, Oliver Tappe + * Copyright 2016, Andrew Lindesay * Distributed under the terms of the MIT License. */ @@ -13,6 +14,7 @@ #include #include +#include namespace BPackageKit { @@ -508,19 +510,22 @@ BPackageInfo::Parser::_ParseList(ListElementParser& elementParser, void BPackageInfo::Parser::_ParseStringList(BStringList* value, - bool requireResolvableName, bool convertToLowerCase) + bool requireResolvableName, bool convertToLowerCase, + StringValidator* stringValidator) { struct StringParser : public ListElementParser { BStringList* value; bool requireResolvableName; bool convertToLowerCase; + StringValidator* stringValidator; StringParser(BStringList* value, bool requireResolvableName, - bool convertToLowerCase) + bool convertToLowerCase, StringValidator* stringValidator) : value(value), requireResolvableName(requireResolvableName), - convertToLowerCase(convertToLowerCase) + convertToLowerCase(convertToLowerCase), + stringValidator(stringValidator) { } @@ -541,9 +546,13 @@ BPackageInfo::Parser::_ParseStringList(BStringList* value, if (convertToLowerCase) element.ToLower(); + if (stringValidator != NULL) + stringValidator->Validate(element, token.pos); + value->Add(element); } - } stringParser(value, requireResolvableName, convertToLowerCase); + } stringParser(value, requireResolvableName, convertToLowerCase, + stringValidator); _ParseList(stringParser, true); } @@ -1008,11 +1017,19 @@ BPackageInfo::Parser::_Parse(BPackageInfo* packageInfo) break; case B_PACKAGE_INFO_URLS: - _ParseStringList(&packageInfo->fURLList); + { + UrlStringValidator stringValidator; + _ParseStringList(&packageInfo->fURLList, + false, false, &stringValidator); + } break; case B_PACKAGE_INFO_SOURCE_URLS: - _ParseStringList(&packageInfo->fSourceURLList); + { + UrlStringValidator stringValidator; + _ParseStringList(&packageInfo->fSourceURLList, + false, false, &stringValidator); + } break; case B_PACKAGE_INFO_GLOBAL_WRITABLE_FILES: @@ -1145,5 +1162,15 @@ BPackageInfo::Parser::_IsValidResolvableName(const char* string, return true; } +void +BPackageInfo::Parser::UrlStringValidator::Validate(const BString& urlString, + const char* pos) +{ + BUrl url(urlString); + + if (!url.IsValid()) + throw ParseError("invalid url", pos); +} + } // namespace BPackageKit diff --git a/src/kits/package/PackageInfoParser.h b/src/kits/package/PackageInfoParser.h index 6824b9e349..53195587c7 100644 --- a/src/kits/package/PackageInfoParser.h +++ b/src/kits/package/PackageInfoParser.h @@ -1,5 +1,6 @@ /* * Copyright 2011, Oliver Tappe + * Copyright 2016, Andrew Lindesay * Distributed under the terms of the MIT License. */ #ifndef PACKAGE_INFO_PARSER_H @@ -31,6 +32,8 @@ public: BPackageResolvableExpression& _expression); private: + struct UrlStringValidator; + struct StringValidator; struct ParseError; struct Token; struct ListElementParser; @@ -74,7 +77,8 @@ private: bool allowSingleNonListElement); void _ParseStringList(BStringList* value, bool requireResolvableName = false, - bool convertToLowerCase = false); + bool convertToLowerCase = false, + StringValidator* stringValidator = NULL); void _ParseResolvableList( BObjectList* value); void _ParseResolvableExprList( @@ -152,6 +156,19 @@ struct BPackageInfo::Parser::ListElementParser { }; +struct BPackageInfo::Parser::StringValidator { +public: + virtual void Validate(const BString &string, const char *pos) = 0; +}; + + +struct BPackageInfo::Parser::UrlStringValidator + : public BPackageInfo::Parser::StringValidator { +public: + virtual void Validate(const BString &string, const char* pos); +}; + + } // namespace BPackageKit diff --git a/src/tests/kits/net/libnetapi/Jamfile b/src/tests/kits/net/libnetapi/Jamfile index b2e34ce9cb..6a7d8fa12d 100644 --- a/src/tests/kits/net/libnetapi/Jamfile +++ b/src/tests/kits/net/libnetapi/Jamfile @@ -5,6 +5,7 @@ UnitTestLib libnetapitest.so : NetworkAddressTest.cpp NetworkInterfaceTest.cpp + NetworkUrlTest.cpp : be bnetapi network [ TargetLibstdc++ ] [ TargetLibsupc++ ] ; diff --git a/src/tests/kits/net/libnetapi/NetAPITestAddon.cpp b/src/tests/kits/net/libnetapi/NetAPITestAddon.cpp index 593ab4a100..fd9e3989e2 100644 --- a/src/tests/kits/net/libnetapi/NetAPITestAddon.cpp +++ b/src/tests/kits/net/libnetapi/NetAPITestAddon.cpp @@ -9,6 +9,7 @@ #include "NetworkAddressTest.h" #include "NetworkInterfaceTest.h" +#include "NetworkUrlTest.h" BTestSuite* @@ -18,6 +19,7 @@ getTestSuite() NetworkAddressTest::AddTests(*suite); NetworkInterfaceTest::AddTests(*suite); + NetworkUrlTest::AddTests(*suite); return suite; } diff --git a/src/tests/kits/net/libnetapi/NetworkUrlTest.cpp b/src/tests/kits/net/libnetapi/NetworkUrlTest.cpp new file mode 100644 index 0000000000..23feb96fcd --- /dev/null +++ b/src/tests/kits/net/libnetapi/NetworkUrlTest.cpp @@ -0,0 +1,272 @@ +/* + * Copyright 2016, Andrew Lindesay, apl@lindesay.co.nz. + * Distributed under the terms of the MIT License. + */ + + +#include "NetworkUrlTest.h" + +#include + +#include +#include + + +NetworkUrlTest::NetworkUrlTest() +{ +} + + +NetworkUrlTest::~NetworkUrlTest() +{ +} + + +void +NetworkUrlTest::setUp() +{ +} + + +void +NetworkUrlTest::tearDown() +{ +} + + +// General Tests --------------------------------------------------------------- + +/* +This is the "happy days" tests that checks that a URL featuring all of the +parsed elements successfully processes and the elements are present. +*/ + +void NetworkUrlTest::TestValidFullUrl() +{ + BUrl url("http://ewe:pea@www.something.co.nz:8888/some/path?key1=value1#fragment"); + CPPUNIT_ASSERT(url.IsValid()); + CPPUNIT_ASSERT(url.Protocol() == "http"); + CPPUNIT_ASSERT(url.HasProtocol()); + CPPUNIT_ASSERT(url.UserName() == "ewe"); + CPPUNIT_ASSERT(url.HasUserName()); + CPPUNIT_ASSERT(url.Password() == "pea"); + CPPUNIT_ASSERT(url.HasPassword()); + CPPUNIT_ASSERT(url.Host() == "www.something.co.nz"); + CPPUNIT_ASSERT(url.HasHost()); + CPPUNIT_ASSERT(url.Port() == 8888); + CPPUNIT_ASSERT(url.HasPort()); + CPPUNIT_ASSERT(url.Path() == "/some/path"); + CPPUNIT_ASSERT(url.HasPath()); + CPPUNIT_ASSERT(url.Request() == "key1=value1"); + CPPUNIT_ASSERT(url.HasRequest()); + CPPUNIT_ASSERT(url.Fragment() == "fragment"); + CPPUNIT_ASSERT(url.HasFragment()); +} + + +void NetworkUrlTest::TestFileUrl() +{ + BUrl url("file:///northisland/wellington/brooklyn/windturbine"); + CPPUNIT_ASSERT(url.IsValid()); + CPPUNIT_ASSERT(url.Protocol() == "file"); + CPPUNIT_ASSERT(url.HasProtocol()); + CPPUNIT_ASSERT(!url.HasUserName()); + CPPUNIT_ASSERT(!url.HasPassword()); + CPPUNIT_ASSERT(url.Host() == ""); + CPPUNIT_ASSERT(url.HasHost()); + CPPUNIT_ASSERT(!url.HasPort()); + CPPUNIT_ASSERT(url.Path() == "/northisland/wellington/brooklyn/windturbine"); + CPPUNIT_ASSERT(url.HasPath()); + CPPUNIT_ASSERT(!url.HasRequest()); + CPPUNIT_ASSERT(!url.HasFragment()); +} + + +// Authority Tests (UserName, Password, Host, Port) ---------------------------- + + +void NetworkUrlTest::TestWithUserNameAndPasswordNoHostAndPort() +{ + BUrl url("wierd://tea:tree@/x"); + CPPUNIT_ASSERT(url.IsValid()); + CPPUNIT_ASSERT(url.Protocol() == "wierd"); + CPPUNIT_ASSERT(url.HasProtocol()); + CPPUNIT_ASSERT(url.UserName() == "tea"); + CPPUNIT_ASSERT(url.HasUserName()); + CPPUNIT_ASSERT(url.Password() == "tree"); + CPPUNIT_ASSERT(url.HasPassword()); + CPPUNIT_ASSERT(url.Host() == ""); + CPPUNIT_ASSERT(!url.HasHost()); + CPPUNIT_ASSERT(!url.HasPort()); + CPPUNIT_ASSERT(url.Path() == "/x"); + CPPUNIT_ASSERT(url.HasPath()); + CPPUNIT_ASSERT(!url.HasRequest()); + CPPUNIT_ASSERT(!url.HasFragment()); +} + + +void NetworkUrlTest::TestHostAndPortWithNoUserNameAndPassword() +{ + BUrl url("https://www.something.co.nz:443/z"); + CPPUNIT_ASSERT(url.IsValid()); + CPPUNIT_ASSERT(url.Protocol() == "https"); + CPPUNIT_ASSERT(url.HasProtocol()); + CPPUNIT_ASSERT(!url.HasUserName()); + CPPUNIT_ASSERT(!url.HasPassword()); + CPPUNIT_ASSERT(url.Port() == 443); + CPPUNIT_ASSERT(url.HasPort()); + CPPUNIT_ASSERT(url.Host() == "www.something.co.nz"); + CPPUNIT_ASSERT(url.HasHost()); + CPPUNIT_ASSERT(url.Path() == "/z"); + CPPUNIT_ASSERT(url.HasPath()); + CPPUNIT_ASSERT(!url.HasRequest()); + CPPUNIT_ASSERT(!url.HasFragment()); +} + + +void NetworkUrlTest::TestHostWithNoPortOrUserNameAndPassword() +{ + BUrl url("https://www.something.co.nz/z"); + CPPUNIT_ASSERT(url.IsValid()); + CPPUNIT_ASSERT(url.Protocol() == "https"); + CPPUNIT_ASSERT(url.HasProtocol()); + CPPUNIT_ASSERT(!url.HasUserName()); + CPPUNIT_ASSERT(!url.HasPassword()); + CPPUNIT_ASSERT(url.Host() == "www.something.co.nz"); + CPPUNIT_ASSERT(url.HasHost()); + CPPUNIT_ASSERT(!url.HasPort()); + CPPUNIT_ASSERT(url.Path() == "/z"); + CPPUNIT_ASSERT(url.HasPath()); + CPPUNIT_ASSERT(!url.HasRequest()); + CPPUNIT_ASSERT(!url.HasFragment()); +} + + +void NetworkUrlTest::TestHostWithNoPortNoPath() +{ + BUrl url("https://www.something.co.nz"); + CPPUNIT_ASSERT(url.IsValid()); + CPPUNIT_ASSERT(url.Protocol() == "https"); + CPPUNIT_ASSERT(url.HasProtocol()); + CPPUNIT_ASSERT(!url.HasUserName()); + CPPUNIT_ASSERT(!url.HasPassword()); + CPPUNIT_ASSERT(url.Host() == "www.something.co.nz"); + CPPUNIT_ASSERT(url.HasHost()); + CPPUNIT_ASSERT(!url.HasPort()); + CPPUNIT_ASSERT(!url.HasPath()); + CPPUNIT_ASSERT(!url.HasRequest()); + CPPUNIT_ASSERT(!url.HasFragment()); +} + + +void NetworkUrlTest::TestHostWithPortNoPath() +{ + BUrl url("https://www.something.co.nz:1234"); + CPPUNIT_ASSERT(url.IsValid()); + CPPUNIT_ASSERT(url.Protocol() == "https"); + CPPUNIT_ASSERT(url.HasProtocol()); + CPPUNIT_ASSERT(!url.HasUserName()); + CPPUNIT_ASSERT(!url.HasPassword()); + CPPUNIT_ASSERT(url.Host() == "www.something.co.nz"); + CPPUNIT_ASSERT(url.HasHost()); + CPPUNIT_ASSERT(url.Port() == 1234); + CPPUNIT_ASSERT(url.HasPort()); + CPPUNIT_ASSERT(!url.HasPath()); + CPPUNIT_ASSERT(!url.HasRequest()); + CPPUNIT_ASSERT(!url.HasFragment()); +} + + +void NetworkUrlTest::TestHostWithEmptyPort() +{ + BUrl url("https://www.something.co.nz:"); + CPPUNIT_ASSERT(url.IsValid()); + CPPUNIT_ASSERT(url.Protocol() == "https"); + CPPUNIT_ASSERT(url.HasProtocol()); + CPPUNIT_ASSERT(!url.HasUserName()); + CPPUNIT_ASSERT(!url.HasPassword()); + CPPUNIT_ASSERT(url.Host() == "www.something.co.nz"); + CPPUNIT_ASSERT(url.HasHost()); + CPPUNIT_ASSERT(!url.HasPort()); + CPPUNIT_ASSERT(!url.HasPath()); + CPPUNIT_ASSERT(!url.HasRequest()); + CPPUNIT_ASSERT(!url.HasFragment()); +} + + +// Invalid Forms --------------------------------------------------------------- + + +void NetworkUrlTest::TestWhitespaceBefore() +{ + BUrl url(" https://www.something.co.nz/z"); + CPPUNIT_ASSERT(!url.IsValid()); +} + + +void NetworkUrlTest::TestWhitespaceAfter() +{ + BUrl url("https://www.something.co.nz/z\t\t "); + CPPUNIT_ASSERT(!url.IsValid()); +} + + +void NetworkUrlTest::TestWhitespaceMiddle() +{ + BUrl url("https://www. something.co.nz/z"); + CPPUNIT_ASSERT(!url.IsValid()); +} + + +void NetworkUrlTest::TestHttpNoHost() +{ + BUrl url("https:///z"); + CPPUNIT_ASSERT(!url.IsValid()); +} + + +// Control --------------------------------------------------------------------- + + +/*static*/ void +NetworkUrlTest::AddTests(BTestSuite& parent) +{ + CppUnit::TestSuite& suite = *new CppUnit::TestSuite("NetworkUrlTest"); + + suite.addTest(new CppUnit::TestCaller( + "NetworkUrlTest::TestHostAndPortWithNoUserNameAndPassword", + &NetworkUrlTest::TestHostAndPortWithNoUserNameAndPassword)); + suite.addTest(new CppUnit::TestCaller( + "NetworkUrlTest::TestWithUserNameAndPasswordNoHostAndPort", + &NetworkUrlTest::TestWithUserNameAndPasswordNoHostAndPort)); + suite.addTest(new CppUnit::TestCaller( + "NetworkUrlTest::TestHostWithNoPortOrUserNameAndPassword", + &NetworkUrlTest::TestHostWithNoPortOrUserNameAndPassword)); + suite.addTest(new CppUnit::TestCaller( + "NetworkUrlTest::TestHostWithNoPortNoPath", + &NetworkUrlTest::TestHostWithNoPortNoPath)); + suite.addTest(new CppUnit::TestCaller( + "NetworkUrlTest::TestHostWithPortNoPath", + &NetworkUrlTest::TestHostWithPortNoPath)); + suite.addTest(new CppUnit::TestCaller( + "NetworkUrlTest::TestHostWithEmptyPort", + &NetworkUrlTest::TestHostWithEmptyPort)); + + suite.addTest(new CppUnit::TestCaller( + "NetworkUrlTest::TestWhitespaceBefore", + &NetworkUrlTest::TestWhitespaceBefore)); + suite.addTest(new CppUnit::TestCaller( + "NetworkUrlTest::TestWhitespaceAfter", + &NetworkUrlTest::TestWhitespaceAfter)); + suite.addTest(new CppUnit::TestCaller( + "NetworkUrlTest::TestWhitespaceMiddle", + &NetworkUrlTest::TestWhitespaceMiddle)); + + suite.addTest(new CppUnit::TestCaller( + "NetworkUrlTest::TestFileUrl", + &NetworkUrlTest::TestFileUrl)); + suite.addTest(new CppUnit::TestCaller( + "NetworkUrlTest::TestValidFullUrl", &NetworkUrlTest::TestValidFullUrl)); + + parent.addTest("NetworkUrlTest", &suite); +} diff --git a/src/tests/kits/net/libnetapi/NetworkUrlTest.h b/src/tests/kits/net/libnetapi/NetworkUrlTest.h new file mode 100644 index 0000000000..6b8b7e45e8 --- /dev/null +++ b/src/tests/kits/net/libnetapi/NetworkUrlTest.h @@ -0,0 +1,42 @@ +/* + * Copyright 2016, Andrew Lindesay, apl@lindesay.co.nz + * Distributed under the terms of the MIT License. + */ +#ifndef NETWORK_URL_TEST_H +#define NETWORK_URL_TEST_H + + +#include +#include + + +class NetworkUrlTest : public CppUnit::TestCase { +public: + NetworkUrlTest(); + virtual ~NetworkUrlTest(); + + virtual void setUp(); + virtual void tearDown(); + + void TestValidFullUrl(); + + void TestFileUrl(); + + void TestWithUserNameAndPasswordNoHostAndPort(); + void TestHostAndPortWithNoUserNameAndPassword(); + void TestHostWithNoPortOrUserNameAndPassword(); + void TestHostWithNoPortNoPath(); + void TestHostWithPortNoPath(); + void TestHostWithEmptyPort(); + + void TestWhitespaceBefore(); + void TestWhitespaceAfter(); + void TestWhitespaceMiddle(); + void TestHttpNoHost(); + + static void AddTests(BTestSuite& suite); + +}; + + +#endif // NETWORK_URL_TEST_H