Skip to content

Commit 81627ee

Browse files
authored
Merge pull request #1055 from ychin/mvim-protocol-fixes-double-encoding
Fix mvim:// protocol double-encoding behavior handling properly
2 parents 6ebe4ee + 653366f commit 81627ee

File tree

2 files changed

+47
-31
lines changed

2 files changed

+47
-31
lines changed

runtime/doc/gui_mac.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,15 @@ For example, the link >
604604
mvim://open?url=file:///etc/profile&line=20
605605
will open the file /etc/profile on line 20 when clicked in a web browser.
606606

607+
Note: A caveat in MacVim's implementation is that it expects special
608+
characters to be encoded twice. For example, a space should be encoded into
609+
"%2520" instead of "%20". A file "/tmp/file name?.txt" would need the
610+
following link: >
611+
mvim://open?url=file:///tmp/file%2520name%253F.txt
612+
613+
MacVim will try to be smart and detect cases where a user has erroneously only
614+
encoded once, but for best results use double-encoding as described above.
615+
607616
Note that url has to be a file:// url pointing to an existing local file.
608617

609618
==============================================================================

src/MacVim/MMAppController.m

Lines changed: 38 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1767,29 +1767,39 @@ - (void)handleGetURLEvent:(NSAppleEventDescriptor *)event
17671767
// parse value
17681768
NSString *v = [arr objectAtIndex:1];
17691769

1770-
// Ideally we don't decode anything here. The input parameters
1771-
// should be used as-in as there would be no reason for caller
1772-
// to encoder anything. For the line component it's a simple
1773-
// string, and the URL should already be a proper file:// URL
1774-
// with all the necessary characters (e.g. space) encoded and
1775-
// we can just pass it to NSURL as-in below.
1776-
// However, iTerm2 appears to encode the slashes as well
1777-
// resulting in URL that looks like
1778-
// file://%2Fsome%2Ffolder/file%20with%20space which is wrong
1779-
// as this doesn't form a valid URL. To accommodate that, we
1780-
// decode the URL, and later on manually parse it instead of
1781-
// relying on NSURL.
1782-
// See: https://github.com/macvim-dev/macvim/issues/1020.
1783-
1784-
// BOOL decode = ![f isEqualToString:@"url"];
1785-
const BOOL decode = YES;
1786-
1787-
if (decode)
1788-
{
1770+
// We need to decode the parameters here because most URL
1771+
// parsers treat the query component as needing to be decoded
1772+
// instead of treating it as is. It does mean that a link to
1773+
// open file "/tmp/file name.txt" will be
1774+
// mvim://open?url=file:///tmp/file%2520name.txt to encode a
1775+
// URL of file:///tmp/file%20name.txt. This double-encoding is
1776+
// intentional to follow the URL spec.
17891777
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11
1790-
v = [v stringByRemovingPercentEncoding];
1778+
v = [v stringByRemovingPercentEncoding];
17911779
#else
1792-
v = [v stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
1780+
v = [v stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
1781+
#endif
1782+
1783+
if ([f isEqualToString:@"url"]) {
1784+
// Since the URL scheme uses a double-encoding due to a
1785+
// file:// URL encoded in another mvim: one, existing tools
1786+
// like iTerm2 could sometimes erroneously only do a single
1787+
// encode. To maximize compatiblity, we re-encode invalid
1788+
// characters if we detect them as they would not work
1789+
// later on when we pass this string to URLWithString.
1790+
//
1791+
// E.g. mvim://open?uri=file:///foo%20bar => "file:///foo bar"
1792+
// which is not a valid URL, so we re-encode it to
1793+
// file:///foo%20bar here. The only important case is to
1794+
// not touch the "%" character as it introduces ambiguity
1795+
// and the re-encoding is a nice compatibility feature, but
1796+
// the canonical form should be double-encoded, i.e.
1797+
// mvim://open?uri=file:///foo%2520bar
1798+
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11
1799+
NSMutableCharacterSet *charSet = [NSMutableCharacterSet characterSetWithCharactersInString:@"%"];
1800+
[charSet formUnionWithCharacterSet:NSCharacterSet.URLHostAllowedCharacterSet];
1801+
[charSet formUnionWithCharacterSet:NSCharacterSet.URLPathAllowedCharacterSet];
1802+
v = [v stringByAddingPercentEncodingWithAllowedCharacters:charSet];
17931803
#endif
17941804
}
17951805

@@ -1800,12 +1810,9 @@ - (void)handleGetURLEvent:(NSAppleEventDescriptor *)event
18001810
// Actually open the file.
18011811
NSString *file = [dict objectForKey:@"url"];
18021812
if (file != nil) {
1803-
// Instead of passing "file" to NSURL directly, we just manually
1804-
// parse the URL because the URL is already decoded and NSURL will
1805-
// get confused by special chars like spaces. See above
1806-
// explanation.
1807-
if ([file hasPrefix:@"file:///"]) {
1808-
NSString *filePath = [file substringFromIndex:7];
1813+
NSURL *fileUrl = [NSURL URLWithString:file];
1814+
if ([fileUrl isFileURL]) {
1815+
NSString *filePath = [fileUrl path];
18091816
// Only opens files that already exist.
18101817
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
18111818
NSArray *filenames = [NSArray arrayWithObject:filePath];
@@ -1847,11 +1854,11 @@ - (void)handleGetURLEvent:(NSAppleEventDescriptor *)event
18471854
[alert addButtonWithTitle:NSLocalizedString(@"OK",
18481855
@"Dialog button")];
18491856

1850-
[alert setMessageText:NSLocalizedString(@"Unknown File Protocol",
1851-
@"Unknown File Protocol dialog, title")];
1857+
[alert setMessageText:NSLocalizedString(@"Invalid File URL",
1858+
@"Unknown Fie URL dialog, title")];
18521859
[alert setInformativeText:[NSString stringWithFormat:NSLocalizedString(
1853-
@"Unknown protocol in \"%@\"",
1854-
@"Unknown File Protocol dialog, text"),
1860+
@"Unknown file URL in \"%@\"",
1861+
@"Unknown file URL dialog, text"),
18551862
file]];
18561863

18571864
[alert setAlertStyle:NSAlertStyleWarning];

0 commit comments

Comments
 (0)