/* * Copyright (C) 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #if PLATFORM(IOS_FAMILY) #import "WebNSStringExtrasIOS.h" #import "WebNSURLExtras.h" #import @implementation NSString (WebNSStringExtrasIOS) - (NSArray *)_web_possibleURLsForForUserTypedString:(BOOL)isPrefix { NSMutableArray *result = [NSMutableArray array]; NSMutableString *workString = [NSMutableString stringWithString:self]; // Trim whitespace from ends (likely cut & paste artifact) CFStringTrimWhitespace((CFMutableStringRef)workString); // Strip out newlines and carriage returns (likely cut & paste artifact) CFStringFindAndReplace((CFMutableStringRef)workString, CFSTR("\n"), CFSTR(""), CFRangeMake (0, [workString length]), 0); CFStringFindAndReplace((CFMutableStringRef)workString, CFSTR("\r"), CFSTR(""), CFRangeMake (0, [workString length]), 0); if ([workString length] > 0) { // Looks like an absolute path or a ~-path if ([workString characterAtIndex:0] == '/' || [workString characterAtIndex:0] == '~') { // - treat it as a pathname NSURL *URL = [NSURL fileURLWithPath:[workString _web_stringByExpandingTildeInPath]]; if (URL != nil) { [result addObject:URL]; } } else { NSRange firstColonRange = [workString rangeOfString:@":"]; NSRange firstSlashRange = [workString rangeOfString:@"/"]; NSRange firstDotRange = [workString rangeOfString:@"."]; NSRange firstAtRange = [workString rangeOfString:@"@"]; BOOL URLHasScheme = NO; if (firstColonRange.location != NSNotFound && (firstSlashRange.location == NSNotFound || firstSlashRange.location >= firstColonRange.location)) { // Do a "sanity check" on the scheme, if it is not one of the "well-known" // schemes we support, do some work to add some possibilities. // This is so typed strings like "localhost:8080" do not get // misinterpreted as a URL with the localhost: scheme. // Perhaps this could be improved to be a dynamic list of schemes // based on loaded protocol handlers, but I do not see the need for // the extra complexity. if ([workString _web_hasCaseInsensitivePrefix:@"http:"] || [workString _web_hasCaseInsensitivePrefix:@"https:"] || [workString _web_hasCaseInsensitivePrefix:@"file:"] || [workString _web_hasCaseInsensitivePrefix:@"ftp:"] || [workString _web_hasCaseInsensitivePrefix:@"javascript:"] || [workString _web_hasCaseInsensitivePrefix:@"about:"] || [workString _web_hasCaseInsensitivePrefix:@"radar:"] || [workString _web_hasCaseInsensitivePrefix:@"rdar:"]) { URLHasScheme = YES; } else if ([workString length] > firstColonRange.location + 1) { // If the first character following the first colon is not a number // guess that this is not is a host:port combination, and that // the string preceding the colon is a scheme we don't know about. unichar c = [workString characterAtIndex:firstColonRange.location + 1]; if (c < '0' || c > '9') { URLHasScheme = YES; } } else { // If there is no character following the colon, then assume the // string preceding the colon is a scheme. URLHasScheme = YES; } } if (URLHasScheme) { // - use it unchanged NSURL *URL = [NSURL _webkit_URLWithUserTypedString:workString]; if (URL != nil) { [result addObject:URL]; } } else { NSURL *URL; // apparent hostname contains a dot and starts with "www." if ([[workString lowercaseString] hasPrefix:@"www."]) { // - just prepend "http://" [workString insertString:@"http://" atIndex:0]; URL = [NSURL _webkit_URLWithUserTypedString:workString]; if (URL != nil) { [result addObject:URL]; } // apparent hostname contains a dot and starts with "ftp." } else if ([[workString lowercaseString] hasPrefix:@"ftp."]) { // - just prepend "ftp://" [workString insertString:@"ftp://" atIndex:0]; URL = [NSURL _webkit_URLWithUserTypedString:workString]; if (URL != nil) { [result addObject:URL]; } // apparent hostname contains "@" } else if (firstAtRange.location != NSNotFound && firstAtRange.location < firstSlashRange.location) { // - justprepend "http://" [workString insertString:@"http://" atIndex:0]; URL = [NSURL _webkit_URLWithUserTypedString:workString]; if (URL != nil) { [result addObject:URL]; } // apparent hostname contains a dot and doesn't start with anything special } else if (firstDotRange.location != NSNotFound && firstDotRange.location < firstSlashRange.location) { // - try prepending "http://" [workString insertString:@"http://" atIndex:0]; URL = [NSURL _webkit_URLWithUserTypedString:workString]; if (URL != nil) { [result addObject:URL]; } // - try prepending "http://www." [workString insertString:@"www." atIndex:strlen("http://")]; URL = [NSURL _webkit_URLWithUserTypedString:workString]; if (URL != nil) { [result addObject:URL]; } // apparent hostname doesn't contain a dot but is equal to "localhost" } else if ([[workString lowercaseString] isEqualToString:@"localhost"] || [[workString lowercaseString] hasPrefix:@"localhost/"]) { // - just prepend "http://" [workString insertString:@"http://" atIndex:0]; URL = [NSURL _webkit_URLWithUserTypedString:workString]; if (URL != nil) { [result addObject:URL]; } // apparent hostname doesn't contain a dot and is nothing special } else { // - try prepending "http://" [workString insertString:@"http://" atIndex:0]; URL = [NSURL _webkit_URLWithUserTypedString:workString]; if (URL != nil) { [result addObject:URL]; } // - try prepending "http://www." and appending .com to the hostname, // but account for a port number if there is one NSRange secondColonRange = [workString rangeOfString:@":" options:NSLiteralSearch range:NSMakeRange(5, [workString length] - 5)]; // 5 is length of http: unsigned endOfHostnameOrPort = (secondColonRange.location != NSNotFound) ? secondColonRange.location : (firstSlashRange.location != NSNotFound) ? strlen("http://") + firstSlashRange.location : [workString length]; if (!(isPrefix && endOfHostnameOrPort == [workString length])) { [workString insertString:@".com" atIndex:endOfHostnameOrPort]; } [workString insertString:@"www." atIndex:strlen("http://")]; URL = [NSURL _webkit_URLWithUserTypedString:workString]; if (URL != nil) { [result addObject:URL]; } } } } } return result; } - (NSArray *)_web_possibleURLPrefixesForUserTypedString { return [self _web_possibleURLsForForUserTypedString:YES]; } - (NSArray *)_web_possibleURLsForUserTypedString { return [self _web_possibleURLsForForUserTypedString:NO]; } - (NSURL *)_web_bestURLForUserTypedString { NSArray *URLs = [self _web_possibleURLsForUserTypedString]; if ([URLs count] == 0) { return nil; } return [[URLs objectAtIndex:0] _webkit_canonicalize]; } @end #endif // PLATFORM(IOS_FAMILY)