f664e77c7e67c0342ba78b140386c4644d451a32
max
Thu Sep 17 01:42:56 2026 -0700
hgHubConnect, lib: name the hub URL when a hub fails to load
#Preview2 week - bugs introduced now will need a build patch to fix
A hub that could not be opened showed up in the connected-hubs table as an
error message and nothing else: the name, description and assembly cells were
all empty, because they are only filled in from the trackHub struct that the
failed load never produced. The URL was in the row only inside the javascript
of the Disconnect and Retry buttons, so there was no way to tell which hub the
error was about. Print the URL in the name cell instead.
netParseUrl chops up its local copy of the URL as it parses, so none of its
errAborts could report what they were looking at. Keep the caller's string
and name it in all of them. A URL written with a single slash, https:/host/f,
then reported a non-numeric port, because with no :// the protocol defaults to
http and the host becomes "https" with an empty port - say what is actually
wrong with it.
Also tighten the encoding of the hub tables' output and of javascript string
literals, and use htmlEncode, not javaScriptLiteralEncode, for the option
labels in cgiMakeSelectDropList, which are HTML text.
diff --git src/lib/cheapcgi.c src/lib/cheapcgi.c
index b739b4becfc..04a74bb05f1 100644
--- src/lib/cheapcgi.c
+++ src/lib/cheapcgi.c
@@ -1364,79 +1364,89 @@
/* Complain about a variable that's not there. */
{
if (varName == NULL) varName = "";
errAbort("Sorry, didn't find CGI input variable '%s'", varName);
}
static char *mustFindVarData(char *varName)
/* Find variable and associated data or die trying. */
{
char *res = findVarData(varName);
if (res == NULL)
cgiBadVar(varName);
return res;
}
+static boolean jsLiteralBackslash(char c)
+/* Is this a character that we put a backslash in front of in a javascript string literal? */
+{
+return (c == '\''
+ || c == '\"'
+ || c == '&'
+ || c == '\\'
+ || c == '\n'
+ || c == '\r'
+ || c == '\t'
+ || c == '\b'
+ || c == '\f');
+}
+
char *javaScriptLiteralEncode(char *inString)
/* Use backslash escaping on newline
* and quote chars, backslash and others.
* Intended that the encoded string will be
* put between quotes at a higher level and
* then interpreted by Javascript. */
{
char c;
int outSize = 0;
char *outString, *out, *in;
if (inString == NULL)
return(cloneString(""));
/* Count up how long it will be */
in = inString;
while ((c = *in++) != 0)
{
- if (c == '\''
- || c == '\"'
- || c == '&'
- || c == '\\'
- || c == '\n'
- || c == '\r'
- || c == '\t'
- || c == '\b'
- || c == '\f'
- )
+ if (c == '<')
+ outSize += 4; // "\x3C", see below
+ else if (jsLiteralBackslash(c))
outSize += 2;
else
outSize += 1;
}
outString = needMem(outSize+1);
/* Encode string */
in = inString;
out = outString;
while ((c = *in++) != 0)
{
- if (c == '\''
- || c == '\"'
- || c == '&'
- || c == '\\'
- || c == '\n'
- || c == '\r'
- || c == '\t'
- || c == '\b'
- || c == '\f'
- )
+ if (c == '<')
+ {
+ /* These literals end up inside an inline " in there before javascript ever sees the text. A backslash does
+ * not hide the < from the parser, but \x3C is the same character to javascript and
+ * leaves no < to be found. */
+ *out++ = '\\';
+ *out++ = 'x';
+ *out++ = '3';
+ *out++ = 'C';
+ continue;
+ }
+ if (jsLiteralBackslash(c))
*out++ = '\\';
*out++ = c;
}
*out++ = 0;
return outString;
}
/* NOTE: Where in the URL to use which of these functions:
*
* Parts of a URL:
* protocol://user:password@server.com:port/path/filename?var1=val1&var2=val2
*
* Note that a space should only be encoded to a plus and decoded from a plus
@@ -2618,53 +2628,54 @@
val = cloneString(anyAll);
label = strchr(val,','); // again because this is new mem
*label = '\0';
label = label+1;
}
else
label = val;
checked = TRUE; // The default case
if (selected != NULL)
{
if (multiple)
checked = (findWordByDelimiter(val,',', selected) != NULL);
else
checked = sameString(val,selected);
}
+ // the label is HTML text here, not a javascript literal
dyStringPrintf(output, "\n",(checked ? " SELECTED" : ""),
- val, javaScriptLiteralEncode(label));
+ val, htmlEncode(label));
if (label != val)
freeMem(val);
}
// All other options
struct slPair *valPair = valsAndLabels;
for (; valPair != NULL; valPair = valPair->next)
{
checked = FALSE;
if (selected != NULL)
{
if (multiple)
checked = (findWordByDelimiter(valPair->name,',', selected) != NULL);
else
checked = sameString(valPair->name,selected);
}
char *label = valPair->name;
if (valPair->val != NULL)
label = valPair->val;
dyStringPrintf(output, "\n",(checked ? " SELECTED" : ""),
- (char *)valPair->name, javaScriptLiteralEncode(label));
+ (char *)valPair->name, htmlEncode(label));
}
dyStringPrintf(output,"\n");
return dyStringCannibalize(&output);
}
void cgiMakeDropListWithVals(char *name, char *menu[], char *values[],
int menuSize, char *checked)
/* Make a drop-down list with names and values. In this case checked
* corresponds to a value, not a menu. */
{
int i;
char *selString;
if (checked == NULL) checked = values[0];