Skip to content

Commit

Permalink
Make checked exception for URI parsing failures.
Browse files Browse the repository at this point in the history
  • Loading branch information
dkocher committed Feb 3, 2019
1 parent b6c20b5 commit 4a861f1
Show file tree
Hide file tree
Showing 16 changed files with 223 additions and 153 deletions.
14 changes: 10 additions & 4 deletions cli/src/main/java/ch/cyberduck/cli/CommandLinePathParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import ch.cyberduck.core.PathKindDetector;
import ch.cyberduck.core.PathNormalizer;
import ch.cyberduck.core.ProtocolFactory;
import ch.cyberduck.core.exception.HostParserException;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.lang3.StringUtils;
Expand All @@ -47,10 +48,15 @@ public CommandLinePathParser(final CommandLine input, final ProtocolFactory fact
}

public Path parse(final String uri) {
final Host host = new HostParser(factory).get(uri);
if(StringUtils.isBlank(host.getDefaultPath())) {
return new Path(String.valueOf(Path.DELIMITER), EnumSet.of(detector.detect(host.getDefaultPath())));
try {
final Host host = new HostParser(factory).get(uri);
if(StringUtils.isBlank(host.getDefaultPath())) {
return new Path(String.valueOf(Path.DELIMITER), EnumSet.of((Path.Type.directory)));
}
return new Path(PathNormalizer.normalize(host.getDefaultPath()), EnumSet.of(detector.detect(host.getDefaultPath())));
}
catch(HostParserException e) {
return new Path(String.valueOf(Path.DELIMITER), EnumSet.of((Path.Type.directory)));
}
return new Path(PathNormalizer.normalize(host.getDefaultPath()), EnumSet.of(detector.detect(host.getDefaultPath())));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import ch.cyberduck.core.HostParser;
import ch.cyberduck.core.Protocol;
import ch.cyberduck.core.ProtocolFactory;
import ch.cyberduck.core.exception.HostParserException;
import ch.cyberduck.core.transfer.Transfer;
import ch.cyberduck.core.transfer.TransferAction;

Expand Down Expand Up @@ -138,28 +139,34 @@ protected boolean validate(final String uri) {
return false;
}
}
final Host host = new HostParser(factory).get(uri);
switch(host.getProtocol().getType()) {
case file:
case b2:
case s3:
case googlestorage:
case swift:
case azure:
case googledrive:
case dropbox:
case onedrive:
break;
default:
if(StringUtils.isBlank(host.getHostname())) {
console.printf("Missing hostname in URI %s%n", uri);
return false;
}
try {
final Host host = new HostParser(factory).get(uri);
switch(host.getProtocol().getType()) {
case file:
case b2:
case s3:
case googlestorage:
case swift:
case azure:
case googledrive:
case dropbox:
case onedrive:
break;
default:
if(StringUtils.isBlank(host.getHostname())) {
console.printf("Missing hostname in URI %s%n", uri);
return false;
}
}
if(StringUtils.isBlank(host.getDefaultPath())) {
console.printf("Missing path in URI %s%n", uri);
return false;
}
return true;
}
if(StringUtils.isBlank(host.getDefaultPath())) {
console.printf("Missing path in URI %s%n", uri);
catch(HostParserException e) {
console.printf(e.getDetail());
return false;
}
return true;
}
}
59 changes: 20 additions & 39 deletions core/src/main/java/ch/cyberduck/core/HostParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* Bug fixes, suggestions and comments should be sent to [email protected]
*/

import ch.cyberduck.core.exception.InvalidHostException;
import ch.cyberduck.core.exception.HostParserException;
import ch.cyberduck.core.preferences.Preferences;
import ch.cyberduck.core.preferences.PreferencesFactory;

Expand Down Expand Up @@ -54,7 +54,7 @@ public HostParser(final ProtocolFactory factory, final Protocol defaultScheme) {
this.defaultScheme = defaultScheme;
}

public Host get(final String url) {
public Host get(final String url) throws HostParserException {
final Host parsed = HostParser.parse(factory, defaultScheme, url);
if(log.isDebugEnabled()) {
log.debug(String.format("Parsed %s as %s", url, parsed));
Expand All @@ -68,38 +68,33 @@ public Host get(final String url) {
* @param url URL
* @return Bookmark
*/
public static Host parse(final String url) {
final Host parsed = parse(ProtocolFactory.get(), ProtocolFactory.get().forName(
preferences.getProperty("connection.protocol.default")), url);
public static Host parse(final String url) throws HostParserException {
final Host parsed = new HostParser(ProtocolFactory.get(), ProtocolFactory.get().forName(
preferences.getProperty("connection.protocol.default"))).get(url);
if(log.isDebugEnabled()) {
log.debug(String.format("Parsed %s as %s", url, parsed));
}
return parsed;
}

public static Host parse(final ProtocolFactory factory, final Protocol defaultScheme, final String url) {
private static Host parse(final ProtocolFactory factory, final Protocol defaultScheme, final String url) throws HostParserException {
final StringReader reader = new StringReader(url);

Value<String> schemeValue = new Value<>();
Protocol protocol;
if(!parseScheme(reader, schemeValue)
|| null == schemeValue.getValue()
|| (protocol = factory.forName(schemeValue.getValue())) == null) {
protocol = defaultScheme;
}

final Host host = new Host(protocol);

final URITypes uriType = findURIType(reader);
if(uriType == URITypes.Undefined) {
// scheme:
if(StringUtils.isBlank(protocol.getDefaultHostname())) {
throw new InvalidHostException(String.format("Missing hostname in URI %s", url));
throw new HostParserException(String.format("Missing hostname in URI %s", url));
}

return host;
}

if(uriType == URITypes.Authority) {
if(host.getProtocol().isHostnameConfigurable()) {
parseAuthority(reader, host);
Expand All @@ -114,7 +109,6 @@ else if(uriType == URITypes.Rootless) {
else if(uriType == URITypes.Absolute) {
parseAbsolute(reader, host);
}

return host;
}

Expand Down Expand Up @@ -158,15 +152,13 @@ else if(c == ':') {
return true; // valid. Break to return stringbuilder
}

static void parseAuthority(final StringReader reader, final Host host) {
final boolean userInfoResult = parseUserInfo(reader, host);

static void parseAuthority(final StringReader reader, final Host host) throws HostParserException {
parseUserInfo(reader, host);
parseHostname(reader, host);

parsePath(reader, host, false);
}

static void parseHostname(final StringReader reader, final Host host) {
static void parseHostname(final StringReader reader, final Host host) throws HostParserException {
final StringBuilder buffer = new StringBuilder();

boolean isPort = false;
Expand All @@ -180,7 +172,7 @@ static void parseHostname(final StringReader reader, final Host host) {
}
else if(bracketFlag) {
if(c == '[') {
throw new InvalidHostException("Illegal character '[' inside IPv6 address");
throw new HostParserException("Illegal character '[' inside IPv6 address");
}
else if(c == ']') {
bracketFlag = false;
Expand All @@ -189,12 +181,12 @@ else if(Character.isLetterOrDigit(c) || c == ':' || c == '%') {
buffer.append(c);
}
else {
throw new InvalidHostException(String.format("Illegal character '%s' at %d inside IPv6 address", c, reader.position));
throw new HostParserException(String.format("Illegal character '%s' at %d inside IPv6 address", c, reader.position));
}
}
else {
if(c == ']') {
throw new InvalidHostException("Illegal character ']' outside IPv6 address");
throw new HostParserException("Illegal character ']' outside IPv6 address");
}
else if(c == '[') {
bracketFlag = true;
Expand All @@ -211,26 +203,23 @@ else if(c == ':') {
}
}
}

if(bracketFlag) {
throw new InvalidHostException("IPv6 bracket not closed in URI");
throw new HostParserException("IPv6 bracket not closed in URI");
}

if(buffer.length() == 0) {
if(StringUtils.isEmpty(host.getHostname())) {
throw new InvalidHostException("Missing hostname in URI");
throw new HostParserException("Missing hostname in URI");
}
}
else {
host.setHostname(buffer.toString());
}

if(isPort) {
parsePort(reader, host);
}
}

static void parsePort(final StringReader reader, final Host host) {
static void parsePort(final StringReader reader, final Host host) throws HostParserException {
Integer port = null;
int tracker = reader.position;

Expand All @@ -252,10 +241,9 @@ static void parsePort(final StringReader reader, final Host host) {
break;
}
}

if(port != null && host.getProtocol().isPortConfigurable()) {
if(port <= 0 || port >= 65536) {
throw new InvalidHostException(String.format("Port %d is outside allowed range 0-65536", port));
throw new HostParserException(String.format("Port %d is outside allowed range 0-65536", port));
}

host.setPort(port);
Expand All @@ -266,7 +254,7 @@ static void parseAbsolute(final StringReader reader, final Host host) {
parsePath(reader, host, true);
}

static void parseRootless(final StringReader reader, final Host host) {
static void parseRootless(final StringReader reader, final Host host) throws HostParserException {
// This is not RFC-compliant.
// * Rootless-path must not include authentication information.
final boolean userInfoResult = parseUserInfo(reader, host);
Expand All @@ -276,11 +264,10 @@ static void parseRootless(final StringReader reader, final Host host) {
// We assume for hostconfigurable-empty-hostnames a hostname on first path segment
parseHostname(reader, host);
}

parsePath(reader, host, false);
}

static boolean parseUserInfo(final StringReader reader, final Host host) {
static boolean parseUserInfo(final StringReader reader, final Host host) throws HostParserException {
int tracker = reader.position;
final StringBuilder buffer = new StringBuilder();
final StringBuilder userBuilder = new StringBuilder();
Expand All @@ -289,7 +276,6 @@ static boolean parseUserInfo(final StringReader reader, final Host host) {
boolean atSignFlag = false;
while(!reader.endOfString()) {
final char c = (char) reader.read();

if('@' == c) {
if(atSignFlag) {
buffer.insert(0, c);
Expand All @@ -299,7 +285,7 @@ static boolean parseUserInfo(final StringReader reader, final Host host) {
for(int i = 0; i < length; i++) {
char t = buffer.charAt(i);
if(t == ' ') {
throw new InvalidHostException(String.format("Space character in user info part of URL at %d", reader.position));
throw new HostParserException(String.format("Space character in user info part of URL at %d", reader.position));
}
if(t == '%') {
t = (char) Integer.parseInt(buffer.substring(i + 1, i + 3), 16);
Expand All @@ -326,7 +312,6 @@ else if(c == '/') {
buffer.append(c);
}
}

reader.skip(tracker - reader.position);
if(host.getProtocol().isAnonymousConfigurable()) {
host.getCredentials().setUsername(preferences.getProperty("connection.login.anon.name"));
Expand Down Expand Up @@ -356,15 +341,13 @@ else if(c == '/') {
}
passwordBuilder.setLength(0);
}

return true;
}
return false;
}

static void parsePath(final StringReader reader, final Host host, final boolean assumeRoot) {
final StringBuilder pathBuilder = new StringBuilder();

if(assumeRoot) {
if(reader.peek() == '/') {
pathBuilder.append((char) reader.read());
Expand All @@ -373,7 +356,6 @@ static void parsePath(final StringReader reader, final Host host, final boolean
pathBuilder.append('/');
}
}

while(!reader.endOfString()) {
final char c = (char) reader.read();

Expand All @@ -385,7 +367,6 @@ static void parsePath(final StringReader reader, final Host host, final boolean
pathBuilder.append(c);
}
}

if(pathBuilder.length() > 0) {
if(host.getProtocol().isPathConfigurable()) {
host.setDefaultPath(pathBuilder.toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
* GNU General Public License for more details.
*/

public class InvalidHostException extends IllegalArgumentException {
public InvalidHostException(final String message) {
super(message);
public class HostParserException extends BackgroundException {

public HostParserException(final String detail) {
super("Failure parsing URI", detail);
}
}
Loading

0 comments on commit 4a861f1

Please sign in to comment.