diff --git a/README.md b/README.md index f54003a..99fc59d 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![MIT License](https://img.shields.io/github/license/lbussy/LCBUrl?style=plastic)](https://github.com/lbussy/LCBUrl/blob/master/LICENSE) [![GitHub Issues](https://img.shields.io/github/issues/lbussy/LCBUrl?style=plastic)](http://github.com/lbussy/LCBUrl/issues) [![GitHub Pull Requests](https://img.shields.io/github/issues-pr/lbussy/LCBUrl?style=plastic)](http://github.com/lbussy/LCBUrl/pulls) -[![Contributors Welsome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=plastic)](#Contributing) +[![Contributors Welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=plastic)](#Contributing) This library was written by and for a non-programmer. If you find some use out of it, that will make me happy, but if not, I'm still using it in my projects. @@ -28,14 +28,14 @@ If you are using mDNS in your projects, you may have discovered many microcontro - getAfterPath() = ?foo=bar#frag - getQuery() = foo=bar - getFragment() = frag -- isMDNS :exclamation: IMPORTANT: Using any of the IP-based methods in a timer will crash or hang your program. This hang is not a shortcoming of this library; it is how radio-functions (networking being one) work. ## Public Methods +### Core Methods + - `bool setUrl(String)` - Pass the URL to be handled to the class -- `bool isMDNS(String)` - Return true/false based on whether URL is mDNS-based (*.local) or not - `String getUrl()` - Return a processed URI in the following format: `scheme:[//authority]path[?query][#fragment]` - `String getIPUrl()` - Return a processed URI with the host replaced by the IP address in the following format: `scheme:[//authority]path[?query][#fragment]` (useful for mDNS URLs) - `String getScheme()` - Get the scheme (currently only handles http and https) @@ -43,7 +43,6 @@ If you are using mDNS in your projects, you may have discovered many microcontro - `String getUserName()` - Returns username (if present) - `String getPassword()` - Returns password (if present) - `String getHost()` - Return host name -- `String getIP()` - Return IP address of host (always does lookup) - `word getPort()` - Return port (if present) if non-standard - `String getAuthority()` - Return the authority (if present) in the following format: `[userinfo@]host[:port]` - `String getIPAuthority()` - Return the authority (if present) in the following format: `[userinfo@]XXX.XXX.XXX.XXX[:port]` (useful for mDNS URLs, will use cached IPs if they exist) @@ -52,6 +51,18 @@ If you are using mDNS in your projects, you may have discovered many microcontro - `String getQuery()` - Returns query (if present) - `String getFragment()` - Returns fragment (if present) +### Utility Methods + +- `bool isMDNS()` - (deprecated) +- `bool isMDNS(const char *hostName)` - Returns true if hostname is a valid mDNS name +- `IPAddress getIP()` - (deprecated) Return IP address of class' host (always does lookup) +- `IPAddress getIP()` - (deprecated) Return IP address of class' host (always does lookup) +- `bool isValidIP(const char * hostName` - Returns true if hostName represents a valid IP address string +- `int labelCount(const char * hostName)` - Integer of the number of labels in the hostname +- `bool isANumber(const char * str)` - Returns true if string is a valid number +- `bool isValidLabel(const char *label)` - Returns true if the string is a valid DNS label +- `bool isValidHostName(const char *hostName)` - Return true if the hostname passed is a valid DNS, mDNS or IP hostname + ## Progress: - [X] Convert percent-encoded triplets to uppercase @@ -61,10 +72,11 @@ If you are using mDNS in your projects, you may have discovered many microcontro - [X] Convert an empty path to a "/" path - [X] Remove the default port - [X] ~~Add a trailing "/" to a non-empty path (may remove this)~~ (removed this after some thought) +- [X] Add validity check functions (not yet part of the initialization) ## Installation -Instalation is particular to the platform with which you are developing: +Installation is particular to the platform with which you are developing: ### PlatformIO @@ -85,7 +97,7 @@ When installed, this library should contain the following files: ./lib/targets/libraries/LCBUrl (this library's folder) ./lib/targets/libraries/LCBUrl/examples (the examples in the "open" menu) ./lib/targets/libraries/LCBUrl/keywords.txt (the syntax coloring file) -./lib/targets/libraries/LCBUrl/library.properties (properties of this libraary) +./lib/targets/libraries/LCBUrl/library.properties (properties of this library) ./lib/targets/libraries/LCBUrl/LICENSE (the license for this library) ./lib/targets/libraries/LCBUrl/README.md (this file) ./lib/targets/libraries/LCBUrl/src/LCBUrl.cpp (the library implementation file) diff --git a/examples/Basic/Basic.ino b/examples/Basic/Basic.ino index 50dad37..0ab6545 100644 --- a/examples/Basic/Basic.ino +++ b/examples/Basic/Basic.ino @@ -31,45 +31,67 @@ #include #include +#ifdef ESP8266 +#include +#include +#endif +#ifdef ESP32 +#include +#include +#endif + +// LCBUrl does not require ArduinoLog. It is used to +// facilitate the examples. +#include +#define LOG_LEVEL LOG_LEVEL_VERBOSE + +const char* ssid = "ssid"; +const char* password = "password"; + void setup() { - Serial.begin(74880); - delay(1000); - Serial.println(F("Starting test run of LCBUrl:")); + Serial.begin(BAUD); + Serial.println(); + Serial.flush(); + Log.begin(LOG_LEVEL, &Serial, false); + Log.notice(F("Starting test run of LCBUrl." CR CR)); - String myUrl = "http://%7EFoo:%7Ep@$$wOrd@Servername.local:80/%7EthIs/is/A/./Path/test.php?foo=bar#frag"; + WiFi.begin(ssid, password); + while (WiFi.status() != WL_CONNECTED) + { + delay(1000); + Log.notice(F("Establishing connection to WiFi." CR)); + } + Log.notice(F("Connected, IP address: %s" CR), WiFi.localIP().toString().c_str()); + + MDNS.begin(WiFi.getHostname()); + + String myUrl = "http://%7EFoo:%7Ep@$$wOrd@brewpi.local:8000/%7EthIs/is/A/./Path/test.php?foo=bar#frag"; LCBUrl url; if (!url.setUrl(myUrl)) { - Serial.println(F("Failure in setUrl();")); + Log.fatal(F("Failure in setUrl();" CR CR)); } else { - Serial.println(F("Return from setUrl():")); - Serial.print(F("\tgetUrl(); = ")); - Serial.println(url.getUrl().c_str()); - Serial.print(F("\tgetScheme(); = ")); - Serial.println(url.getScheme().c_str()); - Serial.print(F("\tgetUserInfo(); = ")); - Serial.println(url.getUserInfo().c_str()); - Serial.print(F("\tgetUserName(); = ")); - Serial.println(url.getUserName().c_str()); - Serial.print(F("\tgetPassword(); = ")); - Serial.println(url.getPassword().c_str()); - Serial.print(F("\tgetHost(); = ")); - Serial.println(url.getHost().c_str()); - Serial.print(F("\tgetPort(); = ")); - Serial.println(url.getPort()); - Serial.print(F("\tgetAuthority(); = ")); - Serial.println(url.getAuthority().c_str()); - Serial.print(F("\tgetPath(); = ")); - Serial.println(url.getPath().c_str()); - Serial.print(F("\tgetAfterPath(); = ")); - Serial.println(url.getAfterPath().c_str()); - Serial.print(F("\tgetQuery(); = ")); - Serial.println(url.getQuery().c_str()); - Serial.print(F("\tgetFragment(); = ")); - Serial.println(url.getFragment().c_str()); + Log.notice(F("Return from setUrl():" CR CR)); + + Log.notice(F("\tgetUrl(); = %s" CR), url.getUrl().c_str()); + Log.notice(F("\tgetScheme(); = %s" CR), url.getScheme().c_str()); + Log.notice(F("\tgetUserInfo(); = %s" CR), url.getUserInfo().c_str()); + Log.notice(F("\tgetUserName(); = %s" CR), url.getUserName().c_str()); + Log.notice(F("\tgetPassword(); = %s" CR), url.getPassword().c_str()); + Log.notice(F("\tgetHost(); = %s" CR), url.getHost().c_str()); + Log.notice(F("\tgetPort(); = %l" CR), url.getPort()); + Log.notice(F("\tgetAuthority(); = %s" CR), url.getAuthority().c_str()); + Log.notice(F("\tgetPath(); = %s" CR), url.getPath().c_str()); + Log.notice(F("\tgetAfterPath(); = %s" CR), url.getAfterPath().c_str()); + Log.notice(F("\tgetQuery(); = %s" CR), url.getQuery().c_str()); + Log.notice(F("\tgetFragment(); = %s" CR CR), url.getFragment().c_str()); + + Log.notice(F("\tHostname isMDNS(): %T" CR), url.isMDNS(url.getHost().c_str())); + Log.notice(F("\tHostname isValidHostName(): %T" CR), url.isValidHostName(url.getHost().c_str())); + Log.notice(F("\tHostname isValidIP(): %T" CR CR), url.isValidIP(url.getHost().c_str())); } - Serial.println(F("LCBUrl test run complete.")); + Log.notice(F("LCBUrl test run complete." CR)); } void loop() { diff --git a/platformio.ini b/platformio.ini index ed0af6e..ce2e84c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -31,6 +31,7 @@ build_flags = ; -DPIO_FRAMEWORK_ARDUINO_LWIP2_IPV6_HIGHER_BANDWIDTH ; v2 IPv6 Higher Bandwidth ; -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH ; v1.4 Higher Bandwidth lib_deps = + ArduinoLog extra_scripts = build_type = debug diff --git a/src/LCBUrl.cpp b/src/LCBUrl.cpp index 1ff4a1d..de1ea40 100644 --- a/src/LCBUrl.cpp +++ b/src/LCBUrl.cpp @@ -34,30 +34,11 @@ // Constructor/Destructor //////////////////////////////////////////////// // Handle the creation, setup, and destruction of instances -LCBUrl::LCBUrl() +LCBUrl::LCBUrl(const String &newUrl) { - rawurl = ""; - url = ""; - ipurl = ""; - workingurl = ""; - scheme = ""; - stripscheme = ""; - rawauthority = ""; - afterauth = ""; - userinfo = ""; - username = ""; - password = ""; - host = ""; - ipaddress = INADDR_NONE; - port = 65535; - authority = ""; - ipauthority = ""; - pathsegment = ""; - path = ""; - removedotsegments = ""; - afterpath = ""; - query = ""; - fragment = ""; + initRegisters(); + if (newUrl.length() > 0) + setUrl(newUrl); } // Public Methods ////////////////////////////////////////////////////////////// @@ -65,18 +46,23 @@ LCBUrl::LCBUrl() bool LCBUrl::setUrl(const String &newUrl) { - rawurl = newUrl; - if (getUrl().length() == 0) + bool retVal = false; + if (newUrl.length() > 0) { - return false; + initRegisters(); + rawurl = newUrl; } - else + if (rawurl.length() != 0) { - return true; + if (getUrl().length() != 0) + { + retVal = true; + } } + return retVal; } -String LCBUrl::getUrl() +String LCBUrl::getUrl() // Returned parsed/normalized URL { if (url.length() == 0) { @@ -104,7 +90,7 @@ String LCBUrl::getUrl() return url; } -String LCBUrl::getIPUrl() +String LCBUrl::getIPUrl() // Return cleaned URL with IP instead of FQDN { if (ipurl.length() == 0) { @@ -132,13 +118,7 @@ String LCBUrl::getIPUrl() return ipurl; } -bool LCBUrl::isMDNS() -{ - return getHost().endsWith(".local"); -} - - -String LCBUrl::getScheme() +String LCBUrl::getScheme() // Returns URL scheme { // Currrently only finds http and https as scheme if (scheme.length() == 0) { @@ -157,7 +137,7 @@ String LCBUrl::getScheme() return scheme; } -String LCBUrl::getUserInfo() +String LCBUrl::getUserInfo() // Return username:passsword { // UserInfo will be anything to the left of the last @ in authority if (userinfo.length() == 0) @@ -176,7 +156,7 @@ String LCBUrl::getUserInfo() return userinfo; } -String LCBUrl::getUserName() +String LCBUrl::getUserName() // Return username from authority { // User Name will be anything to the left of : in userinfo if (username.length() == 0) @@ -195,7 +175,7 @@ String LCBUrl::getUserName() return username; } -String LCBUrl::getPassword() +String LCBUrl::getPassword() // Return password from authority { // Password will be anything to the right of : in userinfo if (password.length() == 0) @@ -214,7 +194,7 @@ String LCBUrl::getPassword() return password; } -String LCBUrl::getHost() +String LCBUrl::getHost() // Return FQDN { // Host will be anything between @ and : or / in authority if (host.length() == 0) @@ -240,63 +220,10 @@ String LCBUrl::getHost() return host; } -IPAddress LCBUrl::getIP() -{ - IPAddress returnIP = INADDR_NONE; - - // First try to resolve the address fresh - if (getHost().endsWith(".local")) - { // Host is an mDNS name - String hostname = getHost(); - hostname.remove(hostname.lastIndexOf(".local")); - -#ifdef ESP8266 - int result = WiFi.hostByName(hostname.c_str(), returnIP); - - if (result == 1) - { - if (returnIP != INADDR_NONE) - { - ipaddress = returnIP; - } - } -#else - struct ip4_addr addr; - addr.addr = 0; - esp_err_t err = mdns_query_a(hostname.c_str(), 2000, &addr); - - if (err == ESP_OK) - { - char ipstring[16]; - snprintf(ipstring, sizeof(ipstring), IPSTR, IP2STR(&addr)); - returnIP.fromString(ipstring); - if (returnIP != INADDR_NONE) - { - ipaddress = returnIP; - } - } -#endif - } - else - { // Host is not an mDNS name - if (WiFi.hostByName(getHost().c_str(), returnIP) == 1) - { - ipaddress = returnIP; - } - } - - // If we got a new IP address, we will use it. Otherwise - // we will use last known good (if there is one), falls back - // to INADDR_NONE - return ipaddress; -} - -word LCBUrl::getPort() +unsigned int LCBUrl::getPort() // Port will be any integer between : and / in authority { - // Port will be any integer between : and / in authority - if (port == 65535) + if (port == 0) { - port = 0; String tempUrl = getRawAuthority(); if (tempUrl.length() > 0) { @@ -306,8 +233,8 @@ word LCBUrl::getPort() tempUrl = tempUrl.substring(startloc + 1); if (endloc != -1) tempUrl = tempUrl.substring(0, endloc - 1); - if ((startloc != -1) && (endloc != -1)) - port = tempUrl.toDouble(); + if (startloc != -1) + port = tempUrl.toInt(); } } if (port == 0) @@ -320,7 +247,7 @@ word LCBUrl::getPort() return port; } -String LCBUrl::getAuthority() +String LCBUrl::getAuthority() // Return username:password@fqdn:port { if (authority.length() == 0) { @@ -353,7 +280,7 @@ String LCBUrl::getAuthority() return authority; } -String LCBUrl::getIPAuthority() +String LCBUrl::getIPAuthority() // Returns {username (optional)}:{password (optional)}@{fqdn} { if (ipauthority.length() == 0) { @@ -371,9 +298,9 @@ String LCBUrl::getIPAuthority() { ipauthority.concat(F("@")); } - if (ipaddress == INADDR_NONE) + if (ipaddress == (IPAddress)INADDR_NONE) { - ipauthority.concat(getIP().toString()); + ipauthority.concat(getIP(getHost().c_str()).toString()); } else { @@ -393,18 +320,17 @@ String LCBUrl::getIPAuthority() return ipauthority; } -String LCBUrl::getPath() +String LCBUrl::getPath() // Get all after host and port, before query and frag { if (path.length() == 0) { path = getPathSegment(); // TODO: Remove dot segments per 5.2.4 } - //path.toLowerCase(); // Remove per #11 return path; } -String LCBUrl::getQuery() +String LCBUrl::getQuery() // Get text after '?' and before '#' { if (query.length() == 0) { @@ -419,7 +345,7 @@ String LCBUrl::getQuery() return query; } -String LCBUrl::getFragment() +String LCBUrl::getFragment() // Get all after '#' { if (fragment.length() == 0) { @@ -437,8 +363,8 @@ String LCBUrl::getFragment() // Private Methods ///////////////////////////////////////////////////////////// // Functions only available to other functions in this library -String LCBUrl::getStripScheme() -{ // Remove scheme and "://" discriminately +String LCBUrl::getStripScheme() // Remove scheme and "://" discriminately +{ if (stripscheme.length() == 0) { stripscheme = ""; @@ -467,9 +393,8 @@ String LCBUrl::getStripScheme() return stripscheme; } -String LCBUrl::getRawAuthority() +String LCBUrl::getRawAuthority() // Authority is similar to "lbussy@raspberrypi.local:80" { - // Authority is similar to "lbussy@raspberrypi.local:80" if (rawauthority.length() == 0) { rawauthority = ""; @@ -490,8 +415,14 @@ String LCBUrl::getRawAuthority() return rawauthority; } -String LCBUrl::getAfterAuth() -{ // Get anything after the authority +String getDotSegmentsClear() +{ + // TODO: https://tools.ietf.org/html/rfc3986#section-5.2.4 + return "TODO"; +} + +String LCBUrl::getAfterAuth() // Get anything after the authority +{ if (afterauth.length() == 0) { afterauth = ""; @@ -507,8 +438,8 @@ String LCBUrl::getAfterAuth() return afterauth; } -String LCBUrl::getAfterPath() -{ // Get anything after the path +String LCBUrl::getAfterPath() // Get anything after the path +{ if (afterpath.length() == 0) { afterpath = ""; @@ -530,53 +461,49 @@ String LCBUrl::getAfterPath() return afterpath; } -String LCBUrl::getCleanTriplets() +String LCBUrl::getCleanTriplets() // Convert URL encoded triplets { - if (workingurl.length() == 0) + workingurl = rawurl; + unsigned int i = workingurl.length(); + while (i != 0) { - workingurl = rawurl; - unsigned int i = workingurl.length(); - while (i != 0) + int loc = workingurl.lastIndexOf(F("%"), i); + if (loc != -1) { - int loc = workingurl.lastIndexOf(F("%"), i); - if (loc != -1) - { - String triplet = rawurl.substring(loc + 1, loc + 3); - triplet.toUpperCase(); - const char *hex = triplet.c_str(); + String triplet = rawurl.substring(loc + 1, loc + 3); + triplet.toUpperCase(); + const char *hex = triplet.c_str(); - // Convert hex string to a character - int x; - char *endptr; - x = strtol(hex, &endptr, 16); - char character = char(x); + // Convert hex string to a character + int x; + char *endptr; + x = strtol(hex, &endptr, 16); + char character = char(x); - // Check for characters which should be decoded - if ( - (isAlphaNumeric(character)) || - (character == '-') || - (character == '.') || - (character == '_') || - (character == '~')) - { - String before = workingurl.substring(0, loc); - String after = workingurl.substring(loc + 3); - workingurl = before + character + after; - } - i--; - } - else + // Check for characters which should be decoded + if ( + (isAlphaNumeric(character)) || + (character == '-') || + (character == '.') || + (character == '_') || + (character == '~')) { - i = 0; + String before = workingurl.substring(0, loc); + String after = workingurl.substring(loc + 3); + workingurl = before + character + after; } + i--; + } + else + { + i = 0; } } return workingurl; } -String LCBUrl::getPathSegment() +String LCBUrl::getPathSegment() // Path will be between the / after host and ? { - // Path will be between the / after host and ? if (pathsegment.length() == 0) { String tempUrl = getStripScheme(); @@ -606,5 +533,216 @@ String LCBUrl::getPathSegment() } pathsegment = tempUrl; } + // TODO: Remove dot segments per 5.2.4 return pathsegment; } + +void LCBUrl::initRegisters() // Clear out the internals to allow the object to be re-used +{ + rawurl = ""; + url = ""; + ipurl = ""; + workingurl = ""; + scheme = ""; + stripscheme = ""; + rawauthority = ""; + afterauth = ""; + userinfo = ""; + username = ""; + password = ""; + host = ""; + ipaddress = INADDR_NONE; + port = 0; + authority = ""; + ipauthority = ""; + pathsegment = ""; + path = ""; + afterpath = ""; + query = ""; + fragment = ""; +} + +// Utility Methods ////////////////////////////////////////////////////////////// +// These do not directly influence or change the core library properties + +bool LCBUrl::isMDNS() // (deprecated) Determine if FQDN is mDNS +{ + return isMDNS(getHost().c_str()); +} + +bool LCBUrl::isMDNS(const char *hostName) // Determine if FQDN is mDNS +{ + // Check for a valid mDNS name + + // Split and check labels + char * label; + char * lastLabel = '\0'; + int labelCount = 0; + char hn[strlen(hostName) + 1]; + strlcpy(hn, hostName, strlen(hostName) + 1); + label = strtok(hn, "."); + while (label != NULL) + { + labelCount++; + lastLabel = label; + if (! isValidLabel(label)) + return false; + label = strtok (NULL, "."); + } + + // Cannot have more than two labels (plus "local") + // https://github.com/lathiat/nss-mdns/blob/master/README.md#etcmdnsallow + if (labelCount > 3) + return false; + + // Must end in ".local" + if (strcmp(lastLabel, "local") != 0) + return false; + + return true; +} + +IPAddress LCBUrl::getIP() // (deprecated) Return IP address of FQDN (helpful for mDNS) +{ + return getIP(getHost().c_str()); +} + +IPAddress LCBUrl::getIP(const char * hostName) // Return IP address of FQDN (helpful for mDNS) +{ + IPAddress returnIP = INADDR_NONE; + + // First try to resolve the address fresh + if (isMDNS(hostName)) + { // Host is an mDNS name + char hn[strlen(hostName) + 1]; + strlcpy(hn, hostName, sizeof(hn)); + hn[strlen(hn)-6] = 0; + +#ifdef ESP8266 + int result = WiFi.hostByName(hostName, returnIP); + + if (result == 1) + { + if (returnIP != INADDR_NONE) + { + ipaddress = returnIP; + } + } +#else + struct ip4_addr addr; + addr.addr = 0; + esp_err_t err = mdns_query_a(hn, 2000, &addr); + + if (err == ESP_OK) + { + char ipstring[16]; + snprintf(ipstring, sizeof(ipstring), IPSTR, IP2STR(&addr)); + returnIP.fromString(ipstring); + if (returnIP != INADDR_NONE) + { + ipaddress = returnIP; + } + } +#endif + } + else + { // Host is not an mDNS name + if (WiFi.hostByName(hostName, returnIP) == 1) + { + ipaddress = returnIP; + } + } + + // If we got a new IP address, we will use it. Otherwise + // we will use last known good (if there is one), falls back + // to INADDR_NONE + return ipaddress; +} + +bool LCBUrl::isValidIP(const char * hostName) +{ + // Check if hostName is a valid IP address + return IPAddress().fromString(hostName); +} + +int LCBUrl::labelCount(const char * hostName) +{ + // Return count of labels in a hostname + char * label; + int labelCount = 0; + char hn[strlen(hostName)]; + strlcpy(hn, hostName, strlen(hostName)); + label = strtok(hn, "."); + while (label != NULL) + { + labelCount++; + label = strtok (NULL, "."); + } + return labelCount; +} + +bool LCBUrl::isANumber(const char * str) +{ + char* p; + strtol(str, &p, 10); + if (*p) { + return false; + } + return true; +} + +bool LCBUrl::isValidLabel(const char *label) +{ + // Check that hostname label is valid + + // Is at least 1 and no more than 63 + if (strlen(label) < 1 || strlen(label) > 63) + return false; + + // Does not begin or end with hyphen + if (label[0] == '-' || label[strlen(label) - 1] == '-') + return false; + + // Does not contain all numbers + if (isANumber(label)) + return false; + + // Contains only letters, numbers and hyphen + for (int i = 0; i < strlen(label); i++) + { + if (! isalnum(label[i]) && label[i] != '-') + return false; + } + return true; +} + +bool LCBUrl::isValidHostName(const char *hostName) +{ + // This will generally follow RFC1123 and RFC1034 + + // Check for min/max length (remember root label and octet count) + if (strlen(hostName) < 1 || strlen(hostName) > 253) + return false; + + // Check if this is a valid IP address + if (isValidIP(hostName)) + return true; + + // Next check for mDNS + if (isMDNS(hostName)) + return true; + + // Next, check to see if each label is valid + char * label; + char hn[strlen(hostName)]; + strlcpy(hn, hostName, strlen(hostName)); + label = strtok(hn, "."); + while (label != NULL) + { + if (! isValidLabel(label)) + return false; + label = strtok (NULL, "."); + } + + return true; +} diff --git a/src/LCBUrl.h b/src/LCBUrl.h index 8d47292..292ade2 100644 --- a/src/LCBUrl.h +++ b/src/LCBUrl.h @@ -40,18 +40,20 @@ #include #endif +#include #include #include +#include // DEBUG + // Library interface description class LCBUrl { // User-accessible "public" interface public: - LCBUrl(); + LCBUrl(const String &newUrl = ""); ~LCBUrl(){}; - bool setUrl(const String &); - bool isMDNS(); + bool setUrl(const String &newUrl); String getUrl(); String getIPUrl(); String getScheme(); @@ -59,8 +61,7 @@ class LCBUrl String getUserName(); String getPassword(); String getHost(); - IPAddress getIP(); - word getPort(); + unsigned int getPort(); String getAuthority(); String getIPAuthority(); String getPath(); @@ -68,14 +69,27 @@ class LCBUrl String getQuery(); String getFragment(); + // Utility functions + bool isMDNS() __attribute__ ((deprecated)); + bool isMDNS(const char *hostName); + IPAddress getIP() __attribute__ ((deprecated)); + IPAddress getIP(const char * hostName); + bool isValidIP(const char * hostName); + int labelCount(const char * hostName); + bool isANumber(const char * str); + bool isValidLabel(const char *label); + bool isValidHostName(const char *hostName); + // Library-accessible "private" interface private: String getRawUrl(); String getCleanTriplets(); + String getDotSegmentsClear(); String getStripScheme(); String getRawAuthority(); String getAfterAuth(); String getPathSegment(); + void initRegisters(); String rawurl; String url; String ipurl; @@ -89,12 +103,11 @@ class LCBUrl String password; String host; IPAddress ipaddress; - word port; + unsigned int port; String authority; String ipauthority; String pathsegment; String path; - String removedotsegments; String afterpath; String query; String fragment;