Fossil SCM

Keep [square brackets] for IPv6 addresses at all times in url_parse_local(). From libfossil.

danshearer 2026-10-11 21:12 UTC trunk
Commit af496edce570b1c43b8107a054050054cf0ee50b10491412be1096b6436d8d2e
1 file changed +52 -19
+52 -19
--- src/url.c
+++ src/url.c
@@ -156,10 +156,12 @@
156156
){
157157
int iStart;
158158
char *zLogin;
159159
char *zExe;
160160
char cQuerySep = '?';
161
+ const char *zLb; /* "[" before an IPv6 address in a URL, else "" */
162
+ const char *zRb; /* "]" after an IPv6 address in a URL, else "" */
161163
162164
if( zUrl[4]=='s' ){
163165
pUrlData->isHttps = 1;
164166
pUrlData->protocol = "https";
165167
pUrlData->dfltPort = 443;
@@ -198,43 +200,61 @@
198200
if( urlFlags & URL_OMIT_USER ){
199201
zLogin = mprintf("");
200202
}else{
201203
zLogin = mprintf("%t@", pUrlData->user);
202204
}
203
- for(j=i+1; (c=zUrl[j])!=0 && c!='/' && c!='?' && c!='#' && c!=':';
204
- j++){}
205
- pUrlData->name = mprintf("%.*s", j-i-1, &zUrl[i+1]);
206
- i = j;
205
+ iStart = i+1;
207206
}else{
207
+ zLogin = mprintf("");
208
+ }
209
+ {
210
+ /* An IPv6 address is written within square brackets because it
211
+ ** contains ':'. The brackets are not part of pUrlData->name. */
208212
int inSquare = 0;
209213
int n;
210214
for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!='?' && c!='#'
211215
&& (inSquare || c!=':'); i++){
212216
if( c=='[' ) inSquare = 1;
213217
if( c==']' ) inSquare = 0;
214218
}
215
- pUrlData->name = mprintf("%.*s", i-iStart, &zUrl[iStart]);
216
- n = strlen(pUrlData->name);
217
- if( pUrlData->name[0]=='[' && n>2 && pUrlData->name[n-1]==']' ){
218
- pUrlData->name++;
219
- pUrlData->name[n-2] = 0;
219
+ n = i-iStart;
220
+ if( n>2 && zUrl[iStart]=='[' && zUrl[i-1]==']' ){
221
+ pUrlData->name = mprintf("%.*s", n-2, &zUrl[iStart+1]);
222
+ }else{
223
+ pUrlData->name = mprintf("%.*s", n, &zUrl[iStart]);
220224
}
221
- zLogin = mprintf("");
222225
}
223226
fossil_strtolwr(pUrlData->name);
227
+ if( strchr(pUrlData->name, ':')!=0 ){
228
+ zLb = "[";
229
+ zRb = "]";
230
+ }else{
231
+ zLb = zRb = "";
232
+ }
224233
if( c==':' ){
225234
pUrlData->port = 0;
226235
i++;
227236
while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){
228237
pUrlData->port = pUrlData->port*10 + c - '0';
229238
i++;
230239
}
231240
if( c!=0 && c!='/' ) fossil_fatal("url missing '/' after port number");
232
- pUrlData->hostname = mprintf("%s:%d", pUrlData->name, pUrlData->port);
241
+ /* The HTTP Host: header needs the brackets around an IPv6
242
+ ** address. For ssh:, the hostname is part of the name of the
243
+ ** use-path-for-ssh setting, which is stored without them. */
244
+ pUrlData->hostname = mprintf("%s%s%s:%d",
245
+ pUrlData->isSsh ? "" : zLb,
246
+ pUrlData->name,
247
+ pUrlData->isSsh ? "" : zRb,
248
+ pUrlData->port);
233249
}else{
234250
pUrlData->port = pUrlData->dfltPort;
235
- pUrlData->hostname = pUrlData->name;
251
+ if( zLb[0] && !pUrlData->isSsh ){
252
+ pUrlData->hostname = mprintf("[%s]", pUrlData->name);
253
+ }else{
254
+ pUrlData->hostname = pUrlData->name;
255
+ }
236256
}
237257
dehttpize(pUrlData->name);
238258
pUrlData->path = fossil_strdup(&zUrl[i]);
239259
for(i=0; pUrlData->path[i] && pUrlData->path[i]!='?'; i++){}
240260
if( pUrlData->path[i] ){
@@ -269,18 +289,19 @@
269289
}
270290
271291
dehttpize(pUrlData->path);
272292
if( pUrlData->dfltPort==pUrlData->port ){
273293
pUrlData->canonical = mprintf(
274
- "%s://%s%T%T%z",
275
- pUrlData->protocol, zLogin, pUrlData->name, pUrlData->path, zExe
294
+ "%s://%s%s%T%s%T%z",
295
+ pUrlData->protocol, zLogin, zLb, pUrlData->name, zRb,
296
+ pUrlData->path, zExe
276297
);
277298
}else{
278299
pUrlData->canonical = mprintf(
279
- "%s://%s%T:%d%T%z",
280
- pUrlData->protocol, zLogin, pUrlData->name, pUrlData->port,
281
- pUrlData->path, zExe
300
+ "%s://%s%s%T%s:%d%T%z",
301
+ pUrlData->protocol, zLogin, zLb, pUrlData->name, zRb,
302
+ pUrlData->port, pUrlData->path, zExe
282303
);
283304
}
284305
if( pUrlData->isSsh && pUrlData->path[1] ){
285306
char *zOld = pUrlData->path;
286307
pUrlData->path = fossil_strdup(zOld+1);
@@ -334,10 +355,22 @@
334355
pUrlData->flags = urlFlags &= ~URL_REMEMBER_PW;
335356
}
336357
}
337358
}
338359
}
360
+
361
+/*
362
+** Append host name zName to the URL being built in x, within square
363
+** brackets if it is an IPv6 address, which contains ':'.
364
+*/
365
+static void url_append_host(Blob *x, const char *zName){
366
+ if( strchr(zName, ':')!=0 ){
367
+ blob_appendf(x, "[%T]", zName);
368
+ }else{
369
+ blob_appendf(x, "%T", zName);
370
+ }
371
+}
339372
340373
/*
341374
** Construct the complete URL for a UrlData object, including the
342375
** login name and password, into memory obtained from fossil_malloc()
343376
** and return a pointer to that URL text.
@@ -353,11 +386,11 @@
353386
if( p->passwd && p->passwd[0] ){
354387
blob_appendf(&x, ":%t", p->passwd);
355388
}
356389
blob_appendf(&x, "@");
357390
}
358
- blob_appendf(&x, "%T", p->name);
391
+ url_append_host(&x, p->name);
359392
if( p->dfltPort!=p->port ){
360393
blob_appendf(&x, ":%d", p->port);
361394
}
362395
blob_appendf(&x, "%T", p->path);
363396
(void)blob_str(&x);
@@ -373,11 +406,11 @@
373406
Blob x = BLOB_INITIALIZER;
374407
if( p->isFile || p->user==0 || p->user[0]==0 ){
375408
return fossil_strdup(p->canonical);
376409
}
377410
blob_appendf(&x, "%s://", p->protocol);
378
- blob_appendf(&x, "%T", p->name);
411
+ url_append_host(&x, p->name);
379412
if( p->dfltPort!=p->port ){
380413
blob_appendf(&x, ":%d", p->port);
381414
}
382415
blob_appendf(&x, "%T", p->path);
383416
(void)blob_str(&x);
384417
--- src/url.c
+++ src/url.c
@@ -156,10 +156,12 @@
156 ){
157 int iStart;
158 char *zLogin;
159 char *zExe;
160 char cQuerySep = '?';
 
 
161
162 if( zUrl[4]=='s' ){
163 pUrlData->isHttps = 1;
164 pUrlData->protocol = "https";
165 pUrlData->dfltPort = 443;
@@ -198,43 +200,61 @@
198 if( urlFlags & URL_OMIT_USER ){
199 zLogin = mprintf("");
200 }else{
201 zLogin = mprintf("%t@", pUrlData->user);
202 }
203 for(j=i+1; (c=zUrl[j])!=0 && c!='/' && c!='?' && c!='#' && c!=':';
204 j++){}
205 pUrlData->name = mprintf("%.*s", j-i-1, &zUrl[i+1]);
206 i = j;
207 }else{
 
 
 
 
 
208 int inSquare = 0;
209 int n;
210 for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!='?' && c!='#'
211 && (inSquare || c!=':'); i++){
212 if( c=='[' ) inSquare = 1;
213 if( c==']' ) inSquare = 0;
214 }
215 pUrlData->name = mprintf("%.*s", i-iStart, &zUrl[iStart]);
216 n = strlen(pUrlData->name);
217 if( pUrlData->name[0]=='[' && n>2 && pUrlData->name[n-1]==']' ){
218 pUrlData->name++;
219 pUrlData->name[n-2] = 0;
220 }
221 zLogin = mprintf("");
222 }
223 fossil_strtolwr(pUrlData->name);
 
 
 
 
 
 
224 if( c==':' ){
225 pUrlData->port = 0;
226 i++;
227 while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){
228 pUrlData->port = pUrlData->port*10 + c - '0';
229 i++;
230 }
231 if( c!=0 && c!='/' ) fossil_fatal("url missing '/' after port number");
232 pUrlData->hostname = mprintf("%s:%d", pUrlData->name, pUrlData->port);
 
 
 
 
 
 
 
233 }else{
234 pUrlData->port = pUrlData->dfltPort;
235 pUrlData->hostname = pUrlData->name;
 
 
 
 
236 }
237 dehttpize(pUrlData->name);
238 pUrlData->path = fossil_strdup(&zUrl[i]);
239 for(i=0; pUrlData->path[i] && pUrlData->path[i]!='?'; i++){}
240 if( pUrlData->path[i] ){
@@ -269,18 +289,19 @@
269 }
270
271 dehttpize(pUrlData->path);
272 if( pUrlData->dfltPort==pUrlData->port ){
273 pUrlData->canonical = mprintf(
274 "%s://%s%T%T%z",
275 pUrlData->protocol, zLogin, pUrlData->name, pUrlData->path, zExe
 
276 );
277 }else{
278 pUrlData->canonical = mprintf(
279 "%s://%s%T:%d%T%z",
280 pUrlData->protocol, zLogin, pUrlData->name, pUrlData->port,
281 pUrlData->path, zExe
282 );
283 }
284 if( pUrlData->isSsh && pUrlData->path[1] ){
285 char *zOld = pUrlData->path;
286 pUrlData->path = fossil_strdup(zOld+1);
@@ -334,10 +355,22 @@
334 pUrlData->flags = urlFlags &= ~URL_REMEMBER_PW;
335 }
336 }
337 }
338 }
 
 
 
 
 
 
 
 
 
 
 
 
339
340 /*
341 ** Construct the complete URL for a UrlData object, including the
342 ** login name and password, into memory obtained from fossil_malloc()
343 ** and return a pointer to that URL text.
@@ -353,11 +386,11 @@
353 if( p->passwd && p->passwd[0] ){
354 blob_appendf(&x, ":%t", p->passwd);
355 }
356 blob_appendf(&x, "@");
357 }
358 blob_appendf(&x, "%T", p->name);
359 if( p->dfltPort!=p->port ){
360 blob_appendf(&x, ":%d", p->port);
361 }
362 blob_appendf(&x, "%T", p->path);
363 (void)blob_str(&x);
@@ -373,11 +406,11 @@
373 Blob x = BLOB_INITIALIZER;
374 if( p->isFile || p->user==0 || p->user[0]==0 ){
375 return fossil_strdup(p->canonical);
376 }
377 blob_appendf(&x, "%s://", p->protocol);
378 blob_appendf(&x, "%T", p->name);
379 if( p->dfltPort!=p->port ){
380 blob_appendf(&x, ":%d", p->port);
381 }
382 blob_appendf(&x, "%T", p->path);
383 (void)blob_str(&x);
384
--- src/url.c
+++ src/url.c
@@ -156,10 +156,12 @@
156 ){
157 int iStart;
158 char *zLogin;
159 char *zExe;
160 char cQuerySep = '?';
161 const char *zLb; /* "[" before an IPv6 address in a URL, else "" */
162 const char *zRb; /* "]" after an IPv6 address in a URL, else "" */
163
164 if( zUrl[4]=='s' ){
165 pUrlData->isHttps = 1;
166 pUrlData->protocol = "https";
167 pUrlData->dfltPort = 443;
@@ -198,43 +200,61 @@
200 if( urlFlags & URL_OMIT_USER ){
201 zLogin = mprintf("");
202 }else{
203 zLogin = mprintf("%t@", pUrlData->user);
204 }
205 iStart = i+1;
 
 
 
206 }else{
207 zLogin = mprintf("");
208 }
209 {
210 /* An IPv6 address is written within square brackets because it
211 ** contains ':'. The brackets are not part of pUrlData->name. */
212 int inSquare = 0;
213 int n;
214 for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!='?' && c!='#'
215 && (inSquare || c!=':'); i++){
216 if( c=='[' ) inSquare = 1;
217 if( c==']' ) inSquare = 0;
218 }
219 n = i-iStart;
220 if( n>2 && zUrl[iStart]=='[' && zUrl[i-1]==']' ){
221 pUrlData->name = mprintf("%.*s", n-2, &zUrl[iStart+1]);
222 }else{
223 pUrlData->name = mprintf("%.*s", n, &zUrl[iStart]);
224 }
 
225 }
226 fossil_strtolwr(pUrlData->name);
227 if( strchr(pUrlData->name, ':')!=0 ){
228 zLb = "[";
229 zRb = "]";
230 }else{
231 zLb = zRb = "";
232 }
233 if( c==':' ){
234 pUrlData->port = 0;
235 i++;
236 while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){
237 pUrlData->port = pUrlData->port*10 + c - '0';
238 i++;
239 }
240 if( c!=0 && c!='/' ) fossil_fatal("url missing '/' after port number");
241 /* The HTTP Host: header needs the brackets around an IPv6
242 ** address. For ssh:, the hostname is part of the name of the
243 ** use-path-for-ssh setting, which is stored without them. */
244 pUrlData->hostname = mprintf("%s%s%s:%d",
245 pUrlData->isSsh ? "" : zLb,
246 pUrlData->name,
247 pUrlData->isSsh ? "" : zRb,
248 pUrlData->port);
249 }else{
250 pUrlData->port = pUrlData->dfltPort;
251 if( zLb[0] && !pUrlData->isSsh ){
252 pUrlData->hostname = mprintf("[%s]", pUrlData->name);
253 }else{
254 pUrlData->hostname = pUrlData->name;
255 }
256 }
257 dehttpize(pUrlData->name);
258 pUrlData->path = fossil_strdup(&zUrl[i]);
259 for(i=0; pUrlData->path[i] && pUrlData->path[i]!='?'; i++){}
260 if( pUrlData->path[i] ){
@@ -269,18 +289,19 @@
289 }
290
291 dehttpize(pUrlData->path);
292 if( pUrlData->dfltPort==pUrlData->port ){
293 pUrlData->canonical = mprintf(
294 "%s://%s%s%T%s%T%z",
295 pUrlData->protocol, zLogin, zLb, pUrlData->name, zRb,
296 pUrlData->path, zExe
297 );
298 }else{
299 pUrlData->canonical = mprintf(
300 "%s://%s%s%T%s:%d%T%z",
301 pUrlData->protocol, zLogin, zLb, pUrlData->name, zRb,
302 pUrlData->port, pUrlData->path, zExe
303 );
304 }
305 if( pUrlData->isSsh && pUrlData->path[1] ){
306 char *zOld = pUrlData->path;
307 pUrlData->path = fossil_strdup(zOld+1);
@@ -334,10 +355,22 @@
355 pUrlData->flags = urlFlags &= ~URL_REMEMBER_PW;
356 }
357 }
358 }
359 }
360
361 /*
362 ** Append host name zName to the URL being built in x, within square
363 ** brackets if it is an IPv6 address, which contains ':'.
364 */
365 static void url_append_host(Blob *x, const char *zName){
366 if( strchr(zName, ':')!=0 ){
367 blob_appendf(x, "[%T]", zName);
368 }else{
369 blob_appendf(x, "%T", zName);
370 }
371 }
372
373 /*
374 ** Construct the complete URL for a UrlData object, including the
375 ** login name and password, into memory obtained from fossil_malloc()
376 ** and return a pointer to that URL text.
@@ -353,11 +386,11 @@
386 if( p->passwd && p->passwd[0] ){
387 blob_appendf(&x, ":%t", p->passwd);
388 }
389 blob_appendf(&x, "@");
390 }
391 url_append_host(&x, p->name);
392 if( p->dfltPort!=p->port ){
393 blob_appendf(&x, ":%d", p->port);
394 }
395 blob_appendf(&x, "%T", p->path);
396 (void)blob_str(&x);
@@ -373,11 +406,11 @@
406 Blob x = BLOB_INITIALIZER;
407 if( p->isFile || p->user==0 || p->user[0]==0 ){
408 return fossil_strdup(p->canonical);
409 }
410 blob_appendf(&x, "%s://", p->protocol);
411 url_append_host(&x, p->name);
412 if( p->dfltPort!=p->port ){
413 blob_appendf(&x, ":%d", p->port);
414 }
415 blob_appendf(&x, "%T", p->path);
416 (void)blob_str(&x);
417

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button