Fossil SCM

fossil-scm / src / url.c
Blame History Raw 862 lines
1
/*
2
** Copyright (c) 2007 D. Richard Hipp
3
**
4
** This program is free software; you can redistribute it and/or
5
** modify it under the terms of the Simplified BSD License (also
6
** known as the "2-Clause License" or "FreeBSD License".)
7
8
** This program is distributed in the hope that it will be useful,
9
** but without any warranty; without even the implied warranty of
10
** merchantability or fitness for a particular purpose.
11
**
12
** Author contact information:
13
** [email protected]
14
** http://www.hwaci.com/drh/
15
**
16
*******************************************************************************
17
**
18
** This file contains code for parsing URLs that appear on the command-line
19
*/
20
#include "config.h"
21
#include "url.h"
22
#include <stdio.h>
23
24
#ifdef _WIN32
25
#include <io.h>
26
#endif
27
28
#if INTERFACE
29
/*
30
** Flags for url_parse()
31
*/
32
#define URL_PROMPT_PW 0x0001 /* Prompt for password if needed */
33
#define URL_REMEMBER 0x0002 /* Remember the url for later reuse */
34
#define URL_ASK_REMEMBER_PW 0x0004 /* Ask whether to remember prompted pw */
35
#define URL_REMEMBER_PW 0x0008 /* Should remember pw */
36
#define URL_PROMPTED 0x0010 /* Prompted for PW already */
37
#define URL_OMIT_USER 0x0020 /* Omit the user name from URL */
38
#define URL_USE_CONFIG 0x0040 /* Use remembered URLs from CONFIG table */
39
#define URL_USE_PARENT 0x0080 /* Use the URL of the parent project */
40
#define URL_SSH_PATH 0x0100 /* Include PATH= on SSH syncs */
41
#define URL_SSH_RETRY 0x0200 /* This a retry of an SSH */
42
#define URL_SSH_EXE 0x0400 /* ssh: URL contains fossil= query param*/
43
44
/*
45
** The URL related data used with this subsystem.
46
*/
47
struct UrlData {
48
int isFile; /* True if a "file:" url */
49
int isHttps; /* True if a "https:" url */
50
int isSsh; /* True if an "ssh:" url */
51
int isAlias; /* Input URL was an alias */
52
char *name; /* Hostname for http: or filename for file: */
53
char *hostname; /* The HOST: parameter on http headers */
54
const char *protocol; /* "http" or "https" or "ssh" or "file" */
55
int port; /* TCP port number for http: or https: */
56
int dfltPort; /* The default port for the given protocol */
57
char *path; /* Pathname for http: */
58
char *user; /* User id for http: */
59
char *passwd; /* Password for http: */
60
char *canonical; /* Canonical representation of the URL */
61
char *proxyAuth; /* Proxy-Authorizer: string */
62
char *fossil; /* The fossil query parameter on ssh: */
63
char *subpath; /* Secondary HTTP request path for ssh: and file: */
64
char *pwConfig; /* CONFIG table entry that gave us the password */
65
unsigned flags; /* Boolean flags controlling URL processing */
66
int useProxy; /* Used to remember that a proxy is in use */
67
int proxyOrigPort; /* Tunneled port number for https through proxy */
68
char *proxyUrlPath; /* Remember path when proxy is use */
69
char *proxyUrlCanonical; /* Remember canonical path when proxy is use */
70
};
71
#endif /* INTERFACE */
72
73
74
/*
75
** Parse the URL in the zUrl argument. Store results in the pUrlData object.
76
** Populate members of pUrlData as follows:
77
**
78
** isFile True if FILE:
79
** isHttps True if HTTPS:
80
** isSsh True if SSH:
81
** protocol "http" or "https" or "file" or "ssh"
82
** name Hostname for HTTP:, HTTPS:, SSH:. Filename for FILE:
83
** port TCP port number for HTTP or HTTPS.
84
** dfltPort Default TCP port number (80 or 443).
85
** path Path name for HTTP or HTTPS.
86
** user Userid.
87
** passwd Password.
88
** hostname HOST:PORT or just HOST if port is the default.
89
** canonical The URL in canonical form, omitting the password
90
**
91
** If URL_USECONFIG is set and zUrl is NULL or "default", then parse the
92
** URL stored in last-sync-url and last-sync-pw of the CONFIG table. Or if
93
** URL_USE_PARENT is also set, then use parent-project-url and
94
** parent-project-pw from the CONFIG table instead of last-sync-url
95
** and last-sync-pw.
96
**
97
** If URL_USE_CONFIG is set and zUrl is a symbolic name, then look up
98
** the URL in sync-url:%Q and sync-pw:%Q elements of the CONFIG table where
99
** %Q is the symbolic name.
100
**
101
** This routine differs from url_parse() in that this routine stores the
102
** results in pUrlData and does not change the values of global variables.
103
** The url_parse() routine puts its result in g.url.
104
*/
105
void url_parse_local(
106
const char *zUrl,
107
unsigned int urlFlags,
108
UrlData *pUrlData
109
){
110
int i, j, c;
111
char *zFile = 0;
112
113
memset(pUrlData, 0, sizeof(*pUrlData));
114
if( urlFlags & URL_USE_CONFIG ){
115
if( zUrl==0 || strcmp(zUrl,"default")==0 ){
116
const char *zPwConfig = "last-sync-pw";
117
if( urlFlags & URL_USE_PARENT ){
118
zUrl = db_get("parent-project-url", 0);
119
if( zUrl==0 ){
120
zUrl = db_get("last-sync-url",0);
121
}else{
122
zPwConfig = "parent-project-pw";
123
}
124
}else{
125
zUrl = db_get("last-sync-url", 0);
126
}
127
if( zUrl==0 ) return;
128
if( pUrlData->passwd==0 ){
129
pUrlData->passwd = unobscure(db_get(zPwConfig, 0));
130
pUrlData->pwConfig = fossil_strdup(zPwConfig);
131
}
132
pUrlData->isAlias = 1;
133
}else{
134
char *zKey = sqlite3_mprintf("sync-url:%q", zUrl);
135
char *zAlt = db_get(zKey, 0);
136
if( zAlt ){
137
pUrlData->pwConfig = mprintf("sync-pw:%q", zUrl);
138
pUrlData->passwd = unobscure(
139
db_text(0, "SELECT value FROM config WHERE name='sync-pw:%q'",zUrl)
140
);
141
zUrl = zAlt;
142
urlFlags |= URL_REMEMBER_PW;
143
pUrlData->isAlias = 1;
144
}else{
145
pUrlData->isAlias = 0;
146
}
147
sqlite3_free(zKey);
148
}
149
}else{
150
if( zUrl==0 ) return;
151
}
152
153
if( strncmp(zUrl, "http://", 7)==0
154
|| strncmp(zUrl, "https://", 8)==0
155
|| strncmp(zUrl, "ssh://", 6)==0
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;
166
iStart = 8;
167
}else if( zUrl[0]=='s' ){
168
pUrlData->isSsh = 1;
169
pUrlData->protocol = "ssh";
170
pUrlData->dfltPort = 22;
171
pUrlData->fossil = fossil_strdup("fossil");
172
iStart = 6;
173
}else{
174
pUrlData->isHttps = 0;
175
pUrlData->protocol = "http";
176
pUrlData->dfltPort = 80;
177
iStart = 7;
178
}
179
for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!='@'; i++){}
180
if( c=='@' ){
181
/* Parse up the user-id and password */
182
for(j=iStart; j<i && zUrl[j]!=':'; j++){}
183
pUrlData->user = mprintf("%.*s", j-iStart, &zUrl[iStart]);
184
dehttpize(pUrlData->user);
185
if( j<i ){
186
if( ( urlFlags & URL_REMEMBER ) && pUrlData->isSsh==0 ){
187
urlFlags |= URL_ASK_REMEMBER_PW;
188
}
189
pUrlData->passwd = mprintf("%.*s", i-j-1, &zUrl[j+1]);
190
dehttpize(pUrlData->passwd);
191
}
192
if( pUrlData->isSsh ){
193
urlFlags &= ~URL_ASK_REMEMBER_PW;
194
}
195
if( urlFlags & URL_OMIT_USER ){
196
zLogin = mprintf("");
197
}else{
198
zLogin = mprintf("%t@", pUrlData->user);
199
}
200
for(j=i+1; (c=zUrl[j])!=0 && c!='/' && c!=':'; j++){}
201
pUrlData->name = mprintf("%.*s", j-i-1, &zUrl[i+1]);
202
i = j;
203
}else{
204
int inSquare = 0;
205
int n;
206
for(i=iStart; (c=zUrl[i])!=0 && c!='/' && (inSquare || c!=':'); i++){
207
if( c=='[' ) inSquare = 1;
208
if( c==']' ) inSquare = 0;
209
}
210
pUrlData->name = mprintf("%.*s", i-iStart, &zUrl[iStart]);
211
n = strlen(pUrlData->name);
212
if( pUrlData->name[0]=='[' && n>2 && pUrlData->name[n-1]==']' ){
213
pUrlData->name++;
214
pUrlData->name[n-2] = 0;
215
}
216
zLogin = mprintf("");
217
}
218
fossil_strtolwr(pUrlData->name);
219
if( c==':' ){
220
pUrlData->port = 0;
221
i++;
222
while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){
223
pUrlData->port = pUrlData->port*10 + c - '0';
224
i++;
225
}
226
if( c!=0 && c!='/' ) fossil_fatal("url missing '/' after port number");
227
pUrlData->hostname = mprintf("%s:%d", pUrlData->name, pUrlData->port);
228
}else{
229
pUrlData->port = pUrlData->dfltPort;
230
pUrlData->hostname = pUrlData->name;
231
}
232
dehttpize(pUrlData->name);
233
pUrlData->path = fossil_strdup(&zUrl[i]);
234
for(i=0; pUrlData->path[i] && pUrlData->path[i]!='?'; i++){}
235
if( pUrlData->path[i] ){
236
pUrlData->path[i] = 0;
237
i++;
238
}
239
zExe = fossil_strdup("");
240
while( pUrlData->path[i]!=0 ){
241
char *zName, *zValue;
242
zName = &pUrlData->path[i];
243
zValue = zName;
244
while( pUrlData->path[i] && pUrlData->path[i]!='=' ){ i++; }
245
if( pUrlData->path[i]=='=' ){
246
pUrlData->path[i] = 0;
247
i++;
248
zValue = &pUrlData->path[i];
249
while( pUrlData->path[i] && pUrlData->path[i]!='&' ){ i++; }
250
}
251
if( pUrlData->path[i] ){
252
pUrlData->path[i] = 0;
253
i++;
254
}
255
if( fossil_strcmp(zName,"fossil")==0 ){
256
fossil_free(pUrlData->fossil);
257
pUrlData->fossil = fossil_strdup(zValue);
258
dehttpize(pUrlData->fossil);
259
fossil_free(zExe);
260
zExe = mprintf("%cfossil=%T", cQuerySep, pUrlData->fossil);
261
cQuerySep = '&';
262
urlFlags |= URL_SSH_EXE;
263
}
264
}
265
266
dehttpize(pUrlData->path);
267
if( pUrlData->dfltPort==pUrlData->port ){
268
pUrlData->canonical = mprintf(
269
"%s://%s%T%T%z",
270
pUrlData->protocol, zLogin, pUrlData->name, pUrlData->path, zExe
271
);
272
}else{
273
pUrlData->canonical = mprintf(
274
"%s://%s%T:%d%T%z",
275
pUrlData->protocol, zLogin, pUrlData->name, pUrlData->port,
276
pUrlData->path, zExe
277
);
278
}
279
if( pUrlData->isSsh && pUrlData->path[1] ){
280
char *zOld = pUrlData->path;
281
pUrlData->path = fossil_strdup(zOld+1);
282
fossil_free(zOld);
283
}
284
free(zLogin);
285
}else if( strncmp(zUrl, "file:", 5)==0 ){
286
pUrlData->isFile = 1;
287
if( zUrl[5]=='/' && zUrl[6]=='/' ){
288
i = 7;
289
}else{
290
i = 5;
291
}
292
zFile = fossil_strdup(&zUrl[i]);
293
}else if( file_isfile(zUrl, ExtFILE) ){
294
pUrlData->isFile = 1;
295
zFile = fossil_strdup(zUrl);
296
}else if( file_isdir(zUrl, ExtFILE)==1 ){
297
zFile = mprintf("%s/FOSSIL", zUrl);
298
if( file_isfile(zFile, ExtFILE) ){
299
pUrlData->isFile = 1;
300
}else{
301
free(zFile);
302
zFile = 0;
303
fossil_fatal("unknown repository: %s", zUrl);
304
}
305
}else{
306
fossil_fatal("unknown repository: %s", zUrl);
307
}
308
if( urlFlags ) pUrlData->flags = urlFlags;
309
if( pUrlData->isFile ){
310
Blob cfile;
311
dehttpize(zFile);
312
file_canonical_name(zFile, &cfile, 0);
313
free(zFile);
314
zFile = 0;
315
pUrlData->protocol = "file";
316
pUrlData->path = mprintf("");
317
pUrlData->name = mprintf("%b", &cfile);
318
pUrlData->canonical = mprintf("file://%T", pUrlData->name);
319
blob_reset(&cfile);
320
}else if( pUrlData->user!=0 && pUrlData->passwd==0
321
&& (urlFlags & URL_PROMPT_PW)!=0 ){
322
url_prompt_for_password_local(pUrlData);
323
}else if( pUrlData->user!=0 && ( urlFlags & URL_ASK_REMEMBER_PW ) ){
324
if( fossil_isatty(fossil_fileno(stdin))
325
&& ( urlFlags & URL_REMEMBER_PW )==0 ){
326
if( save_password_prompt(pUrlData->passwd) ){
327
pUrlData->flags = urlFlags |= URL_REMEMBER_PW;
328
}else{
329
pUrlData->flags = urlFlags &= ~URL_REMEMBER_PW;
330
}
331
}
332
}
333
}
334
335
/*
336
** Construct the complete URL for a UrlData object, including the
337
** login name and password, into memory obtained from fossil_malloc()
338
** and return a pointer to that URL text.
339
*/
340
char *url_full(const UrlData *p){
341
Blob x = BLOB_INITIALIZER;
342
if( p->isFile || p->user==0 || p->user[0]==0 ){
343
return fossil_strdup(p->canonical);
344
}
345
blob_appendf(&x, "%s://", p->protocol);
346
if( p->user && p->user[0] ){
347
blob_appendf(&x, "%t", p->user);
348
if( p->passwd && p->passwd[0] ){
349
blob_appendf(&x, ":%t", p->passwd);
350
}
351
blob_appendf(&x, "@");
352
}
353
blob_appendf(&x, "%T", p->name);
354
if( p->dfltPort!=p->port ){
355
blob_appendf(&x, ":%d", p->port);
356
}
357
blob_appendf(&x, "%T", p->path);
358
(void)blob_str(&x);
359
return x.aData;
360
}
361
362
/*
363
** Construct a URL for a UrlData object that omits the
364
** login name and password, into memory obtained from fossil_malloc()
365
** and return a pointer to that URL text.
366
*/
367
char *url_nouser(const UrlData *p){
368
Blob x = BLOB_INITIALIZER;
369
if( p->isFile || p->user==0 || p->user[0]==0 ){
370
return fossil_strdup(p->canonical);
371
}
372
blob_appendf(&x, "%s://", p->protocol);
373
blob_appendf(&x, "%T", p->name);
374
if( p->dfltPort!=p->port ){
375
blob_appendf(&x, ":%d", p->port);
376
}
377
blob_appendf(&x, "%T", p->path);
378
(void)blob_str(&x);
379
return x.aData;
380
}
381
382
/*
383
** SQL function to remove the username/password from a URL
384
*/
385
void url_nouser_func(
386
sqlite3_context *context,
387
int argc,
388
sqlite3_value **argv
389
){
390
const char *zOrig = (const char*)sqlite3_value_text(argv[0]);
391
UrlData x;
392
if( zOrig==0 ) return;
393
memset(&x, 0, sizeof(x));
394
url_parse_local(zOrig, URL_OMIT_USER, &x);
395
sqlite3_result_text(context, x.canonical, -1, SQLITE_TRANSIENT);
396
url_unparse(&x);
397
}
398
399
/*
400
** Reclaim malloced memory from a UrlData object
401
*/
402
void url_unparse(UrlData *p){
403
if( p==0 ){
404
p = &g.url;
405
}
406
fossil_free(p->canonical);
407
fossil_free(p->name);
408
fossil_free(p->path);
409
fossil_free(p->user);
410
fossil_free(p->passwd);
411
fossil_free(p->fossil);
412
fossil_free(p->subpath);
413
fossil_free(p->pwConfig);
414
memset(p, 0, sizeof(*p));
415
}
416
417
/*
418
** Move a URL parse from one UrlData object to another.
419
*/
420
void url_move_parse(UrlData *pTo, UrlData *pFrom){
421
url_unparse(pTo);
422
memcpy(pTo, pFrom, sizeof(*pTo));
423
memset(pFrom, 0, sizeof(*pFrom));
424
}
425
426
/*
427
** Parse the given URL, which describes a sync server. Populate variables
428
** in the global "g.url" structure as shown below. If zUrl is NULL, then
429
** parse the URL given in the last-sync-url setting, taking the password
430
** form last-sync-pw.
431
**
432
** g.url.isFile True if FILE:
433
** g.url.isHttps True if HTTPS:
434
** g.url.isSsh True if SSH:
435
** g.url.protocol "http" or "https" or "file" or "ssh"
436
** g.url.name Hostname for HTTP:, HTTPS:, SSH:. Filename for FILE:
437
** g.url.port TCP port number for HTTP or HTTPS.
438
** g.url.dfltPort Default TCP port number (80 or 443).
439
** g.url.path Path name for HTTP or HTTPS.
440
** g.url.user Userid.
441
** g.url.passwd Password.
442
** g.url.hostname HOST:PORT or just HOST if port is the default.
443
** g.url.canonical The URL in canonical form, omitting the password
444
** g.url.pwConfig Name of CONFIG table entry containing the password
445
**
446
** HTTP url format as follows (HTTPS is the same with a different scheme):
447
**
448
** http://userid:password@host:port/path
449
**
450
** SSH url format is:
451
**
452
** ssh://userid@host:port/path?fossil=path/to/fossil.exe
453
**
454
** If URL_USE_CONFIG is set then the URL and password might be pulled from
455
** the CONFIG table rather than from the zUrl parameter. If zUrl is NULL
456
** or "default" then the URL is given by the "last-sync-url" setting and
457
** the password comes form the "last-sync-pw" setting. If zUrl is a symbolic
458
** name, then the URL comes from "sync-url:NAME" and the password from
459
** "sync-pw:NAME" where NAME is the input zUrl string. Whenever the
460
** password is taken from the CONFIG table, the g.url.pwConfig field is
461
** set to the CONFIG.NAME value from which that password is taken. Otherwise,
462
** g.url.pwConfig is NULL.
463
*/
464
void url_parse(const char *zUrl, unsigned int urlFlags){
465
url_parse_local(zUrl, urlFlags, &g.url);
466
}
467
468
/*
469
** Print the content of g.url
470
*/
471
void urlparse_print(int showPw){
472
fossil_print("g.url.isFile = %d\n", g.url.isFile);
473
fossil_print("g.url.isHttps = %d\n", g.url.isHttps);
474
fossil_print("g.url.isSsh = %d\n", g.url.isSsh);
475
fossil_print("g.url.protocol = %s\n", g.url.protocol);
476
fossil_print("g.url.name = %s\n", g.url.name);
477
fossil_print("g.url.port = %d\n", g.url.port);
478
fossil_print("g.url.dfltPort = %d\n", g.url.dfltPort);
479
fossil_print("g.url.hostname = %s\n", g.url.hostname);
480
fossil_print("g.url.path = %s\n", g.url.path);
481
fossil_print("g.url.user = %s\n", g.url.user);
482
if( showPw || g.url.pwConfig==0 ){
483
fossil_print("g.url.passwd = %s\n", g.url.passwd);
484
}else{
485
fossil_print("g.url.passwd = ************\n");
486
}
487
fossil_print("g.url.pwConfig = %s\n", g.url.pwConfig);
488
fossil_print("g.url.canonical = %s\n", g.url.canonical);
489
fossil_print("g.url.fossil = %s\n", g.url.fossil);
490
fossil_print("g.url.subpath = %s\n", g.url.subpath);
491
fossil_print("g.url.flags = 0x%04x\n", g.url.flags);
492
fossil_print("url_full(g.url) = %z\n", url_full(&g.url));
493
}
494
495
/*
496
** COMMAND: test-urlparser
497
**
498
** Usage: %fossil test-urlparser URL ?options?
499
**
500
** --prompt-pw Prompt for password if missing
501
** --remember Store results in last-sync-url
502
** --show-pw Show the CONFIG-derived password in the output
503
** --use-config Pull URL and password from the CONFIG table
504
** --use-parent Use the parent project URL
505
*/
506
void cmd_test_urlparser(void){
507
int i;
508
unsigned fg = 0;
509
int showPw = 0;
510
db_must_be_within_tree();
511
url_proxy_options();
512
if( find_option("remember",0,0) ) fg |= URL_REMEMBER;
513
if( find_option("prompt-pw",0,0) ) fg |= URL_PROMPT_PW;
514
if( find_option("use-parent",0,0) ) fg |= URL_USE_PARENT|URL_USE_CONFIG;
515
if( find_option("use-config",0,0) ) fg |= URL_USE_CONFIG;
516
if( find_option("show-pw",0,0) ) showPw = 1;
517
if( (fg & URL_USE_CONFIG)==0 ) showPw = 1;
518
if( g.argc!=3 && g.argc!=4 ){
519
usage("URL");
520
}
521
url_parse(g.argv[2], fg);
522
for(i=0; i<2; i++){
523
urlparse_print(showPw);
524
if( g.url.isFile || g.url.isSsh ) break;
525
if( i==0 ){
526
fossil_print("********\n");
527
url_enable_proxy("Using proxy: ");
528
}
529
url_unparse(0);
530
}
531
}
532
533
/*
534
** Proxy specified on the command-line using the --proxy option.
535
** If there is no --proxy option on the command-line then this
536
** variable holds a NULL pointer.
537
*/
538
static const char *zProxyOpt = 0;
539
540
/*
541
** Extract any proxy options from the command-line.
542
**
543
** --proxy URL|off
544
**
545
** The original purpose of this routine is the above. But this
546
** also happens to be a convenient place to look for other
547
** network-related options:
548
**
549
** --nosync Temporarily disable "autosync"
550
**
551
** --ipv4 Disallow IPv6. Use only IPv4.
552
**
553
** --ipv6 Disallow IPv4. Use only IPv6.
554
**
555
** --accept-any-cert Disable server SSL cert validation. Accept
556
** any SSL cert that the server provides.
557
** WARNING: this option opens you up to
558
** forged-DNS and man-in-the-middle attacks!
559
*/
560
void url_proxy_options(void){
561
zProxyOpt = find_option("proxy", 0, 1);
562
if( find_option("nosync",0,0) ) g.fNoSync = 1;
563
if( find_option("ipv4",0,0) ) g.eIPvers = 1;
564
if( find_option("ipv6",0,0) ) g.eIPvers = 2;
565
#ifdef FOSSIL_ENABLE_SSL
566
if( find_option("accept-any-cert",0,0) ){
567
ssl_disable_cert_verification();
568
}
569
#endif /* FOSSIL_ENABLE_SSL */
570
}
571
572
/*
573
** If the "proxy" setting is defined, then change the URL settings
574
** (initialized by a prior call to url_parse()) so that the HTTP
575
** header will be appropriate for the proxy and so that the TCP/IP
576
** connection will be opened to the proxy rather than to the server.
577
**
578
** If zMsg is not NULL and a proxy is used, then print zMsg followed
579
** by the canonical name of the proxy (with userid and password suppressed).
580
*/
581
void url_enable_proxy(const char *zMsg){
582
const char *zProxy;
583
zProxy = zProxyOpt;
584
if( zProxy==0 ){
585
zProxy = db_get("proxy", "system");
586
if( fossil_strcmp(zProxy, "system")==0 ){
587
zProxy = fossil_getenv("http_proxy");
588
}
589
}
590
if( zProxy && zProxy[0] && !is_false(zProxy)
591
&& !g.url.isSsh && !g.url.isFile ){
592
char *zOriginalUrl = g.url.canonical;
593
char *zOriginalHost = g.url.hostname;
594
int fOriginalIsHttps = g.url.isHttps;
595
char *zOriginalUser = g.url.user;
596
char *zOriginalPasswd = g.url.passwd;
597
char *zOriginalUrlPath = g.url.path;
598
int iOriginalPort = g.url.port;
599
unsigned uOriginalFlags = g.url.flags;
600
g.url.user = 0;
601
g.url.passwd = "";
602
url_parse(zProxy, 0);
603
if( zMsg ) fossil_print("%s%s\n", zMsg, g.url.canonical);
604
g.url.path = zOriginalUrl;
605
g.url.hostname = zOriginalHost;
606
if( g.url.user ){
607
char *zCredentials1 = mprintf("%s:%s", g.url.user, g.url.passwd);
608
char *zCredentials2 = encode64(zCredentials1, -1);
609
g.url.proxyAuth = mprintf("Basic %z", zCredentials2);
610
free(zCredentials1);
611
}
612
g.url.user = zOriginalUser;
613
g.url.passwd = zOriginalPasswd;
614
g.url.isHttps = fOriginalIsHttps;
615
g.url.useProxy = 1;
616
g.url.proxyUrlCanonical = zOriginalUrl;;
617
g.url.proxyUrlPath = zOriginalUrlPath;
618
g.url.proxyOrigPort = iOriginalPort;
619
g.url.flags = uOriginalFlags;
620
}
621
}
622
623
#if INTERFACE
624
/*
625
** An instance of this object is used to build a URL with query parameters.
626
*/
627
struct HQuery {
628
Blob url; /* The URL */
629
const char *zBase; /* The base URL */
630
int nParam; /* Number of parameters. */
631
int nAlloc; /* Number of allocated slots */
632
const char **azName; /* Parameter names */
633
const char **azValue; /* Parameter values */
634
};
635
#endif
636
637
/*
638
** Initialize the URL object.
639
*/
640
void url_initialize(HQuery *p, const char *zBase){
641
memset(p, 0, sizeof(*p));
642
blob_zero(&p->url);
643
p->zBase = zBase;
644
}
645
646
/*
647
** Resets the given URL object, deallocating any memory
648
** it uses.
649
*/
650
void url_reset(HQuery *p){
651
blob_reset(&p->url);
652
fossil_free((void *)p->azName);
653
fossil_free((void *)p->azValue);
654
url_initialize(p, p->zBase);
655
}
656
657
/*
658
** Add a fixed parameter to an HQuery. Or remove the parameters if zValue==0.
659
*/
660
void url_add_parameter(HQuery *p, const char *zName, const char *zValue){
661
int i;
662
for(i=0; i<p->nParam; i++){
663
if( fossil_strcmp(p->azName[i],zName)==0 ){
664
if( zValue==0 ){
665
p->nParam--;
666
p->azValue[i] = p->azValue[p->nParam];
667
p->azName[i] = p->azName[p->nParam];
668
}else{
669
p->azValue[i] = zValue;
670
}
671
return;
672
}
673
}
674
assert( i==p->nParam );
675
if( zValue==0 ) return;
676
if( i>=p->nAlloc ){
677
p->nAlloc = p->nAlloc*2 + 10;
678
p->azName = fossil_realloc((void *)p->azName,
679
sizeof(p->azName[0])*p->nAlloc);
680
p->azValue = fossil_realloc((void *)p->azValue,
681
sizeof(p->azValue[0])*p->nAlloc);
682
}
683
p->azName[i] = zName;
684
p->azValue[i] = zValue;
685
p->nParam++;
686
}
687
688
/*
689
** Render the URL with a parameter override.
690
**
691
** Returned memory is transient and is overwritten on the next call to this
692
** routine for the same HQuery, or until the HQuery object is destroyed.
693
*/
694
char *url_render(
695
HQuery *p, /* Base URL */
696
const char *zName1, /* First override */
697
const char *zValue1, /* First override value */
698
const char *zName2, /* Second override */
699
const char *zValue2 /* Second override value */
700
){
701
const char *zSep = "?";
702
int i;
703
704
blob_reset(&p->url);
705
blob_appendf(&p->url, "%R/%s", p->zBase);
706
for(i=0; i<p->nParam; i++){
707
const char *z = p->azValue[i];
708
if( zName1 && fossil_strcmp(zName1,p->azName[i])==0 ){
709
zName1 = 0;
710
z = zValue1;
711
if( z==0 ) continue;
712
}
713
if( zName2 && fossil_strcmp(zName2,p->azName[i])==0 ){
714
zName2 = 0;
715
z = zValue2;
716
if( z==0 ) continue;
717
}
718
blob_appendf(&p->url, "%s%s", zSep, p->azName[i]);
719
if( z && z[0] ) blob_appendf(&p->url, "=%T", z);
720
zSep = "&";
721
}
722
if( zName1 && zValue1 ){
723
blob_appendf(&p->url, "%s%s", zSep, zName1);
724
if( zValue1[0] ) blob_appendf(&p->url, "=%T", zValue1);
725
zSep = "&";
726
}
727
if( zName2 && zValue2 ){
728
blob_appendf(&p->url, "%s%s", zSep, zName2);
729
if( zValue2[0] ) blob_appendf(&p->url, "=%T", zValue2);
730
}
731
return blob_str(&p->url);
732
}
733
734
/*
735
** Prompt the user for the password that corresponds to the "user" member of
736
** the provided UrlData structure. Store the result into the "passwd" member
737
** of the provided UrlData structure.
738
*/
739
void url_prompt_for_password_local(UrlData *pUrlData){
740
if( pUrlData->isSsh || pUrlData->isFile ) return;
741
if( fossil_isatty(fossil_fileno(stdin))
742
&& (pUrlData->flags & URL_PROMPT_PW)!=0
743
&& (pUrlData->flags & URL_PROMPTED)==0
744
){
745
pUrlData->flags |= URL_PROMPTED;
746
pUrlData->passwd = prompt_for_user_password(pUrlData->canonical);
747
if( pUrlData->passwd[0]
748
&& (pUrlData->flags & (URL_REMEMBER|URL_ASK_REMEMBER_PW))!=0
749
){
750
if( save_password_prompt(pUrlData->passwd) ){
751
pUrlData->flags |= URL_REMEMBER_PW;
752
}else{
753
pUrlData->flags &= ~URL_REMEMBER_PW;
754
}
755
}
756
}else{
757
fossil_fatal("missing or incorrect password for user \"%s\"",
758
pUrlData->user);
759
}
760
}
761
762
/*
763
** Prompt the user for the password for g.url.user. Store the result
764
** in g.url.passwd.
765
*/
766
void url_prompt_for_password(void){
767
url_prompt_for_password_local(&g.url);
768
}
769
770
/*
771
** Remember the URL and password if requested.
772
*/
773
void url_remember(void){
774
if( g.url.flags & URL_REMEMBER ){
775
const char *url;
776
if( g.url.useProxy ){
777
url = g.url.proxyUrlCanonical;
778
}else{
779
url = g.url.canonical;
780
}
781
if( g.url.flags & URL_USE_PARENT ){
782
db_set("parent-project-url", url, 0);
783
}else{
784
db_set("last-sync-url", url, 0);
785
}
786
if( g.url.user!=0 && g.url.passwd!=0 && ( g.url.flags & URL_REMEMBER_PW ) ){
787
if( g.url.flags & URL_USE_PARENT ){
788
db_set("parent-project-pw", obscure(g.url.passwd), 0);
789
}else{
790
db_set("last-sync-pw", obscure(g.url.passwd), 0);
791
}
792
}
793
}
794
}
795
796
/* Preemptively prompt for a password if a username is given in the
797
** URL but no password.
798
*/
799
void url_get_password_if_needed(void){
800
if( (g.url.user && g.url.user[0])
801
&& (g.url.passwd==0 || g.url.passwd[0]==0)
802
&& fossil_isatty(fossil_fileno(stdin))
803
){
804
url_prompt_for_password();
805
}
806
}
807
808
/*
809
** Given a URL for a remote repository clone point, try to come up with a
810
** reasonable basename of a local clone of that repository.
811
**
812
** * If the URL has a path, use the tail of the path, with any suffix
813
** elided.
814
**
815
** * If the URL is just a domain name, without a path, then use the
816
** first element of the domain name, except skip over "www." if
817
** present and if there is a ".com" or ".org" or similar suffix.
818
**
819
** The string returned is obtained from fossil_malloc(). NULL might be
820
** returned if there is an error.
821
*/
822
char *url_to_repo_basename(const char *zUrl){
823
const char *zTail = 0;
824
int i;
825
if( zUrl==0 ) return 0;
826
for(i=0; zUrl[i]; i++){
827
if( zUrl[i]=='?' ) break;
828
if( (zUrl[i]=='/' || zUrl[i]=='@') && zUrl[i+1]!=0 ) zTail = &zUrl[i+1];
829
}
830
if( zTail==0 ) return 0;
831
if( sqlite3_strnicmp(zTail, "www.", 4)==0 && strchr(zTail+4,'.')!=0 ){
832
/* Remove the "www." prefix if there are more "." characters later.
833
** But don't remove the "www." prefix if what follows is the suffix.
834
** forum:/forumpost/74e111a2ee */
835
zTail += 4;
836
}
837
if( zTail[0]==0 ) return 0;
838
for(i=0; zTail[i] && zTail[i]!='.' && zTail[i]!='?' &&
839
zTail[i]!=':' && zTail[i]!='/'; i++){}
840
if( i==0 ) return 0;
841
return mprintf("%.*s", i, zTail);
842
}
843
844
/*
845
** COMMAND: test-url-basename
846
** Usage: %fossil test-url-basenames URL ...
847
**
848
** This command is used for unit testing of the url_to_repo_basename()
849
** routine. The command-line arguments are URL, presumably for remote
850
** Fossil repositories. This command runs url_to_repo_basename() on each
851
** of those inputs and displays the result.
852
*/
853
void cmd_test_url_basename(void){
854
int i;
855
char *z;
856
for(i=2; i<g.argc; i++){
857
z = url_to_repo_basename(g.argv[i]);
858
fossil_print("%s -> %s\n", g.argv[i], z);
859
fossil_free(z);
860
}
861
}
862

Keyboard Shortcuts

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