[1mdiff --git a/src/app.c b/src/app.c[m
[1mindex 3b4c24f0..73cc35ee 100644[m
[1m--- a/src/app.c[m
[1m+++ b/src/app.c[m
[36m@@ -2791,12 +2791,6 @@[m [miBool handleCommand_App(const char *cmd) {[m
setRedirectCount_DocumentWidget(doc, redirectCount);[m
setOrigin_DocumentWidget(doc, origin);[m
showCollapsed_Widget(findWidget_App("document.progress"), iFalse);[m
[31m- if (prefs_App()->decodeUserVisibleURLs) {[m
[31m- urlDecodePath_String(url);[m
[31m- }[m
[31m- else {[m
[31m- urlEncodePath_String(url);[m
[31m- } [m
setUrlFlags_DocumentWidget(doc, url,[m
isHistory ? useCachedContentIfAvailable_DocumentWidgetSetUrlFlag : 0);[m
/* Optionally, jump to a text in the document. This will only work if the document[m
[1mdiff --git a/src/gmrequest.c b/src/gmrequest.c[m
[1mindex a9c5919d..c23e8499 100644[m
[1m--- a/src/gmrequest.c[m
[1m+++ b/src/gmrequest.c[m
[36m@@ -585,8 +585,10 @@[m [mvoid setUrl_GmRequest(iGmRequest *d, const iString *url) {[m
/* TODO: Gemini spec allows UTF-8 encoded URLs, but still need to percent-encode non-ASCII[m
characters? Could be a server-side issue, e.g., if they're using a URL parser meant for[m
the web. */[m
[31m- urlEncodePath_String(&d->url);[m
[31m- urlEncodeSpaces_String(&d->url);[m
[32m+[m[32m /* Encode everything except already-percent encoded characters. */[m
[32m+[m[32m iString *enc = urlEncodeExclude_String(&d->url, "%" URL_RESERVED_CHARS);[m
[32m+[m[32m set_String(&d->url, enc);[m
[32m+[m[32m delete_String(enc);[m
d->identity = identityForUrl_GmCerts(d->certs, &d->url);[m
}[m
[m
[1mdiff --git a/src/gmutil.c b/src/gmutil.c[m
[1mindex 79462e41..98e4d4d6 100644[m
[1m--- a/src/gmutil.c[m
[1m+++ b/src/gmutil.c[m
[36m@@ -330,6 +330,28 @@[m [mvoid urlEncodePath_String(iString *d) {[m
delete_String(encoded);[m
}[m
[m
[32m+[m[32mvoid urlEncodeQuery_String(iString *d) {[m
[32m+[m[32m iUrl url;[m
[32m+[m[32m init_Url(&url, d);[m
[32m+[m[32m if (isEmpty_Range(&url.query)) {[m
[32m+[m[32m return;[m
[32m+[m[32m }[m
[32m+[m[32m iString encoded;[m
[32m+[m[32m init_String(&encoded);[m
[32m+[m[32m appendRange_String(&encoded, (iRangecc){ constBegin_String(d), url.query.start });[m
[32m+[m[32m iString query;[m
[32m+[m[32m url.query.start++; /* omit the question mark */[m
[32m+[m[32m initRange_String(&query, url.query);[m
[32m+[m[32m iString encQuery = urlEncode_String(&query); / fully encoded */[m
[32m+[m[32m appendCStr_String(&encoded, "?");[m
[32m+[m[32m append_String(&encoded, encQuery);[m[41m [m
[32m+[m[32m delete_String(encQuery);[m
[32m+[m[32m deinit_String(&query);[m
[32m+[m[32m appendRange_String(&encoded, (iRangecc){ url.query.end, constEnd_String(d) });[m
[32m+[m[32m set_String(d, &encoded);[m
[32m+[m[32m deinit_String(&encoded);[m
[32m+[m[32m}[m
[32m+[m
iBool isKnownScheme_Rangecc(iRangecc scheme) {[m
if (isKnownUrlScheme_Rangecc(scheme)) {[m
return iTrue;[m
[36m@@ -667,20 +689,20 @@[m [mconst iString *canonicalUrl_String(const iString *d) {[m
iString *canon = NULL;[m
iUrl parts;[m
init_Url(&parts, d);[m
[31m- /* Colons are in decoded form in the URL path. */[m
[32m+[m[32m /* Colons (0x3a) are in decoded form in the URL path. */[m
if (iStrStrN(parts.path.start, "%3A", size_Range(&parts.path)) ||[m
iStrStrN(parts.path.start, "%3a", size_Range(&parts.path))) {[m
/* This is done separately to avoid the copy if %3A is not present; it's rare. */[m
canon = copy_String(d);[m
urlDecodePath_String(canon);[m
[31m- iString dec = maybeUrlDecodeExclude_String(canon, "%/?:;#&+= "); / decode everything else in all parts */[m
[32m+[m[32m iString dec = maybeUrlDecodeExclude_String(canon, "% " URL_RESERVED_CHARS); / decode everything else in all parts */[m
if (dec) {[m
set_String(canon, dec);[m
delete_String(dec);[m
}[m
}[m
else {[m
[31m- canon = maybeUrlDecodeExclude_String(d, "%/?:;#&+= ");[m
[32m+[m[32m canon = maybeUrlDecodeExclude_String(d, "% " URL_RESERVED_CHARS);[m
}[m
/* `canon` may now be NULL if nothing was decoded. */[m
if (indexOfCStr_String(canon ? canon : d, " ") != iInvalidPos ||[m
[36m@@ -689,7 +711,7 @@[m [mconst iString *canonicalUrl_String(const iString *d) {[m
canon = copy_String(d);[m
}[m
urlEncodeSpaces_String(canon);[m
[31m- }[m
[32m+[m[32m }[m[41m [m
return canon ? collect_String(canon) : d;[m
}[m
[m
[1mdiff --git a/src/gmutil.h b/src/gmutil.h[m
[1mindex 6d337eeb..15bb7b2e 100644[m
[1m--- a/src/gmutil.h[m
[1m+++ b/src/gmutil.h[m
[36m@@ -100,6 +100,7 @@[m [miRegExp * newGemtextLink_RegExp (void);[m
[m
#define GEMINI_DEFAULT_PORT ((uint16_t) 1965)[m
#define GEMINI_DEFAULT_PORT_CSTR "1965"[m
[32m+[m[32m#define URL_RESERVED_CHARS ":/?#[]@!$&'()+,;=" / RFC 3986 */[m
[m
struct Impl_Url {[m
iRangecc scheme;[m
[36m@@ -131,6 +132,7 @@[m [mconst iString * urlFragmentStripped_String(const iString *);[m
const iString * urlQueryStripped_String (const iString *);[m
void urlDecodePath_String (iString *);[m
void urlEncodePath_String (iString *);[m
[32m+[m[32mvoid urlEncodeQuery_String (iString *);[m
iString * makeFileUrl_String (const iString *localFilePath);[m
const char * makeFileUrl_CStr (const char *localFilePath);[m
iString * localFilePathFromUrl_String(const iString *);[m
[1mdiff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c[m
[1mindex b94e0c27..24983d69 100644[m
[1m--- a/src/ui/inputwidget.c[m
[1m+++ b/src/ui/inputwidget.c[m
[36m@@ -1100,9 +1100,15 @@[m [mstatic void updateBuffered_InputWidget_(iInputWidget *d) {[m
void setText_InputWidget(iInputWidget *d, const iString *text) {[m
if (!d) return;[m
if (d->inFlags & isUrl_InputWidgetFlag) {[m
[31m- /* If user wants URLs encoded, also Punycode the domain. */[m
[31m- if (!prefs_App()->decodeUserVisibleURLs) {[m
[32m+[m[32m if (prefs_App()->decodeUserVisibleURLs) {[m
iString *enc = collect_String(copy_String(text));[m
[32m+[m[32m urlDecodePath_String(enc);[m
[32m+[m[32m text = enc;[m
[32m+[m[32m }[m
[32m+[m[32m else {[m
[32m+[m[32m /* The user wants URLs encoded, also Punycode the domain. */[m
[32m+[m[32m iString *enc = collect_String(copy_String(text));[m
[32m+[m[32m urlEncodePath_String(enc);[m
/* Prevent address bar spoofing (mentioned as IDN homograph attack in[m
https://github.com/skyjake/lagrange/issues/73) */[m
punyEncodeUrlHost_String(enc);[m
text/plain
This content has been proxied by September (ba2dc).