Check-in [b19f25fe87]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Initial code to implement synchronization via ssh.
Timelines: family | ancestors | descendants | both | experimental
Files: files | file ages | folders
SHA1: b19f25fe87273e1666255d9a6053020f8a8f832c
User & Date: drh 2010-08-25 14:03:01.000
Context
2010-08-25
16:03
Continuing work on the ssh:// sync protocol. check-in: 958f596637 user: drh tags: experimental
14:03
Initial code to implement synchronization via ssh. check-in: b19f25fe87 user: drh tags: experimental
2010-08-24
01:24
Fix a potential sigfault that can occur in the graph generator if the child is older than its parent. check-in: 7503f98779 user: drh tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/cgi.c.
1015
1016
1017
1018
1019
1020
1021












1022
1023
1024
1025
1026
1027
1028
  cgi_set_status(501, "Not Implemented");
  cgi_printf(
    "<html><body>Unrecognized HTTP Request</body></html>\n"
  );
  cgi_reply();
  fossil_exit(0);
}













/*
** Panic and die while processing a webpage.
*/
void cgi_panic(const char *zFormat, ...){
  va_list ap;
  cgi_reset_content();







>
>
>
>
>
>
>
>
>
>
>
>







1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
  cgi_set_status(501, "Not Implemented");
  cgi_printf(
    "<html><body>Unrecognized HTTP Request</body></html>\n"
  );
  cgi_reply();
  fossil_exit(0);
}

/*
** Send a reply indicating that the HTTP request is forbidden
*/
static void forbidden_request(void){
  cgi_set_status(403, "Forbidden");
  cgi_printf(
    "<html><body>Access Denied</body></html>\n"
  );
  cgi_reply();
  fossil_exit(0);
}

/*
** Panic and die while processing a webpage.
*/
void cgi_panic(const char *zFormat, ...){
  va_list ap;
  cgi_reset_content();
1071
1072
1073
1074
1075
1076
1077

1078
1079
1080
1081
1082
1083
1084
** and subsequent code handles the actual generation of the webpage.
*/
void cgi_handle_http_request(const char *zIpAddr){
  char *z, *zToken;
  int i;
  struct sockaddr_in remoteName;
  size_t size = sizeof(struct sockaddr_in);

  char zLine[2000];     /* A single line of input. */

  g.fullHttpReply = 1;
  if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
    malformed_request();
  }
  zToken = extract_token(zLine, &z);







>







1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
** and subsequent code handles the actual generation of the webpage.
*/
void cgi_handle_http_request(const char *zIpAddr){
  char *z, *zToken;
  int i;
  struct sockaddr_in remoteName;
  size_t size = sizeof(struct sockaddr_in);
  int accessTokenSeen = 0;
  char zLine[2000];     /* A single line of input. */

  g.fullHttpReply = 1;
  if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
    malformed_request();
  }
  zToken = extract_token(zLine, &z);
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138




1139
1140
1141
1142










1143
1144
1145





1146
1147
1148
1149
1150
1151
1152
    zFieldName = extract_token(zLine,&zVal);
    if( zFieldName==0 || *zFieldName==0 ) break;
    while( isspace(*zVal) ){ zVal++; }
    i = strlen(zVal);
    while( i>0 && isspace(zVal[i-1]) ){ i--; }
    zVal[i] = 0;
    for(i=0; zFieldName[i]; i++){ zFieldName[i] = tolower(zFieldName[i]); }
    if( strcmp(zFieldName,"user-agent:")==0 ){
      cgi_setenv("HTTP_USER_AGENT", zVal);
    }else if( strcmp(zFieldName,"content-length:")==0 ){
      cgi_setenv("CONTENT_LENGTH", zVal);
    }else if( strcmp(zFieldName,"referer:")==0 ){
      cgi_setenv("HTTP_REFERER", zVal);
    }else if( strcmp(zFieldName,"host:")==0 ){
      cgi_setenv("HTTP_HOST", zVal);
    }else if( strcmp(zFieldName,"content-type:")==0 ){
      cgi_setenv("CONTENT_TYPE", zVal);
    }else if( strcmp(zFieldName,"cookie:")==0 ){
      cgi_setenv("HTTP_COOKIE", zVal);




    }else if( strcmp(zFieldName,"if-none-match:")==0 ){
      cgi_setenv("HTTP_IF_NONE_MATCH", zVal);
    }else if( strcmp(zFieldName,"if-modified-since:")==0 ){
      cgi_setenv("HTTP_IF_MODIFIED_SINCE", zVal);










    }else if( strcmp(zFieldName,"https:")==0 ){
      cgi_setenv("HTTPS", zVal);
    }





  }

  cgi_init();
}

/*
** Maximum number of child processes that we can have running







<
<
|

<
<
<
<




>
>
>
>




>
>
>
>
>
>
>
>
>
>
|
|

>
>
>
>
>







1133
1134
1135
1136
1137
1138
1139


1140
1141




1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
    zFieldName = extract_token(zLine,&zVal);
    if( zFieldName==0 || *zFieldName==0 ) break;
    while( isspace(*zVal) ){ zVal++; }
    i = strlen(zVal);
    while( i>0 && isspace(zVal[i-1]) ){ i--; }
    zVal[i] = 0;
    for(i=0; zFieldName[i]; i++){ zFieldName[i] = tolower(zFieldName[i]); }


    if( strcmp(zFieldName,"content-length:")==0 ){
      cgi_setenv("CONTENT_LENGTH", zVal);




    }else if( strcmp(zFieldName,"content-type:")==0 ){
      cgi_setenv("CONTENT_TYPE", zVal);
    }else if( strcmp(zFieldName,"cookie:")==0 ){
      cgi_setenv("HTTP_COOKIE", zVal);
    }else if( strcmp(zFieldName,"https:")==0 ){
      cgi_setenv("HTTPS", zVal);
    }else if( strcmp(zFieldName,"host:")==0 ){
      cgi_setenv("HTTP_HOST", zVal);
    }else if( strcmp(zFieldName,"if-none-match:")==0 ){
      cgi_setenv("HTTP_IF_NONE_MATCH", zVal);
    }else if( strcmp(zFieldName,"if-modified-since:")==0 ){
      cgi_setenv("HTTP_IF_MODIFIED_SINCE", zVal);
    }else if( strcmp(zFieldName,"x-fossil-security-token:")==0 ){
      if( g.zAccessToken ){
        if( strcmp(zVal,g.zAccessToken)==0 ){
          accessTokenSeen = 1;
        }
      }
    }
#if 0
    else if( strcmp(zFieldName,"referer:")==0 ){
      cgi_setenv("HTTP_REFERER", zVal);
    }else if( strcmp(zFieldName,"user-agent:")==0 ){
      cgi_setenv("HTTP_USER_AGENT", zVal);
    }
#endif
  }

  if( g.zAccessToken && !accessTokenSeen ){
    forbidden_request();
  }

  cgi_init();
}

/*
** Maximum number of child processes that we can have running
Changes to src/http.c.
109
110
111
112
113
114
115
116




117
118
119
120
121
122
123
  blob_appendf(pHdr, "Host: %s\r\n", g.urlHostname);
  blob_appendf(pHdr, "User-Agent: Fossil/" MANIFEST_VERSION "\r\n");
  if( g.fHttpTrace ){
    blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n");
  }else{
    blob_appendf(pHdr, "Content-Type: application/x-fossil\r\n");
  }
  blob_appendf(pHdr, "Content-Length: %d\r\n\r\n", blob_size(pPayload));




}

/*
** Sign the content in pSend, compress it, and send it to the server
** via HTTP or HTTPS.  Get a reply, uncompress the reply, and store the reply
** in pRecv.  pRecv is assumed to be uninitialized when
** this routine is called - this routine will initialize it.







|
>
>
>
>







109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
  blob_appendf(pHdr, "Host: %s\r\n", g.urlHostname);
  blob_appendf(pHdr, "User-Agent: Fossil/" MANIFEST_VERSION "\r\n");
  if( g.fHttpTrace ){
    blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n");
  }else{
    blob_appendf(pHdr, "Content-Type: application/x-fossil\r\n");
  }
  blob_appendf(pHdr, "Content-Length: %d\r\n", blob_size(pPayload));
  if( g.zAccessToken ){
    blob_appendf(pHdr, "X-Fossil-Access-Token: %s\r\n", g.zAccessToken);
  }
  blob_appendf(pHdr, "\r\n");
}

/*
** Sign the content in pSend, compress it, and send it to the server
** via HTTP or HTTPS.  Get a reply, uncompress the reply, and store the reply
** in pRecv.  pRecv is assumed to be uninitialized when
** this routine is called - this routine will initialize it.
Changes to src/http_transport.c.
61
62
63
64
65
66
67









































68
69
70
71
72
73
74
  if( pnSent ) *pnSent = transport.nSent;
  if( pnRcvd ) *pnRcvd = transport.nRcvd;
  if( resetFlag ){
    transport.nSent = 0;
    transport.nRcvd = 0;
  }
}










































/*
** Open a connection to the server.  The server is defined by the following
** global variables:
**
**   g.urlName        Name of the server.  Ex: www.fossil-scm.org
**   g.urlPort        TCP/IP port.  Ex: 80







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
  if( pnSent ) *pnSent = transport.nSent;
  if( pnRcvd ) *pnRcvd = transport.nRcvd;
  if( resetFlag ){
    transport.nSent = 0;
    transport.nRcvd = 0;
  }
}

/*
** Global initialization of the transport layer
*/
void transport_global_startup(void){
  if( g.urlIsSsh ){
    char *zCmd;
    int i, j;
    char zReply[200];
    if( g.urlUser && g.urlUser[0] ){
      zCmd = mprintf(
         "ssh -L127.0.0.1:%d:127.0.0.1:%d %s@%s \"fossil sshd -P %d '%s'\"", 
         g.urlPort, g.urlPort, g.urlUser, g.urlSshHost, g.urlPort, g.urlPath
      );
    }else{
      zCmd = mprintf(
         "ssh -L127.0.0.1:%d:127.0.0.1:%d %s \"fossil sshd -P %d '%s'\"", 
         g.urlPort, g.urlPort, g.urlSshHost, g.urlPort, g.urlPath
      );
    }
    printf("%s\n", zCmd);
    g.sshIn = popen(zCmd, "r");
    if( g.sshIn==0 ){
      fossil_fatal("cannot start ssh tunnel using [%s]", zCmd);
    }
    free(zCmd);
    zReply[0] = 0;
    fgets(zReply, sizeof(zReply), g.sshIn);
    if( zReply[0]==0 ){
      pclose(g.sshIn);
      fossil_fatal("unable to set up ssh tunnel");
    }
    if( memcmp(zReply, "Access-Token: ", 14)!=0 ){
      pclose(g.sshIn);
      fossil_fatal("ssh tunnel did not send back an access token");
    }
    for(i=14; isspace(zReply[i]); i++);
    for(j=i; isalnum(zReply[j]); j++);
    g.zAccessToken = mprintf("%.*s", j-i, &zReply[i]); 
  }
}

/*
** Open a connection to the server.  The server is defined by the following
** global variables:
**
**   g.urlName        Name of the server.  Ex: www.fossil-scm.org
**   g.urlPort        TCP/IP port.  Ex: 80
311
312
313
314
315
316
317




318
319
320
321
322
323
324
325
    i++;
  }
  /* printf("Got line: [%s]\n", &transport.pBuf[iStart]); */
  return &transport.pBuf[iStart];
}

void transport_global_shutdown(void){




  if( g.urlIsHttps ){
    #ifdef FOSSIL_ENABLE_SSL
    ssl_global_shutdown();
    #endif
  }else{
    socket_global_shutdown();
  }
}







>
>
>
>








352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
    i++;
  }
  /* printf("Got line: [%s]\n", &transport.pBuf[iStart]); */
  return &transport.pBuf[iStart];
}

void transport_global_shutdown(void){
  if( g.urlIsSsh && g.sshIn ){
    pclose(g.sshIn);
    g.sshIn = 0;
  }
  if( g.urlIsHttps ){
    #ifdef FOSSIL_ENABLE_SSL
    ssl_global_shutdown();
    #endif
  }else{
    socket_global_shutdown();
  }
}
Changes to src/main.c.
81
82
83
84
85
86
87


88
89
90

91

92
93
94
95

96
97
98
99
100
101
102
  Th_Interp *interp;      /* The TH1 interpreter */
  FILE *httpIn;           /* Accept HTTP input from here */
  FILE *httpOut;          /* Send HTTP output here */
  int xlinkClusterOnly;   /* Set when cloning.  Only process clusters */
  int fTimeFormat;        /* 1 for UTC.  2 for localtime.  0 not yet selected */
  int *aCommitFile;       /* Array of files to be committed */
  int markPrivate;        /* All new artifacts are private if true */



  int urlIsFile;          /* True if a "file:" url */
  int urlIsHttps;         /* True if a "https:" url */

  char *urlName;          /* Hostname for http: or filename for file: */

  char *urlHostname;      /* The HOST: parameter on http headers */
  char *urlProtocol;      /* "http" or "https" */
  int urlPort;            /* TCP port number for http: or https: */
  int urlDfltPort;        /* The default port for the given protocol */

  char *urlPath;          /* Pathname for http: */
  char *urlUser;          /* User id for http: */
  char *urlPasswd;        /* Password for http: */
  char *urlCanonical;     /* Canonical representation of the URL */
  char *urlProxyAuth;     /* Proxy-Authorizer: string */
  int dontKeepUrl;        /* Do not persist the URL */








>
>



>

>




>







81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
  Th_Interp *interp;      /* The TH1 interpreter */
  FILE *httpIn;           /* Accept HTTP input from here */
  FILE *httpOut;          /* Send HTTP output here */
  int xlinkClusterOnly;   /* Set when cloning.  Only process clusters */
  int fTimeFormat;        /* 1 for UTC.  2 for localtime.  0 not yet selected */
  int *aCommitFile;       /* Array of files to be committed */
  int markPrivate;        /* All new artifacts are private if true */
  char *zAccessToken;     /* X-Fossil-Access-Token HTTP header field */
  FILE *sshIn;            /* Result of popen("ssh") */

  int urlIsFile;          /* True if a "file:" url */
  int urlIsHttps;         /* True if a "https:" url */
  int urlIsSsh;           /* True if an "ssh:" url */
  char *urlName;          /* Hostname for http: or filename for file: */
  char *urlSshHost;       /* Hostname for ssh: tunnels */
  char *urlHostname;      /* The HOST: parameter on http headers */
  char *urlProtocol;      /* "http" or "https" */
  int urlPort;            /* TCP port number for http: or https: */
  int urlDfltPort;        /* The default port for the given protocol */
  int urlSshPort;         /* TCP port for SSH */
  char *urlPath;          /* Pathname for http: */
  char *urlUser;          /* User id for http: */
  char *urlPasswd;        /* Password for http: */
  char *urlCanonical;     /* Canonical representation of the URL */
  char *urlProxyAuth;     /* Proxy-Authorizer: string */
  int dontKeepUrl;        /* Do not persist the URL */

Changes to src/url.c.
15
16
17
18
19
20
21










22
23
24
25
26
27

28
29

30
31

32
33
34
35
36
37
38
39
40
41




42
43
44
45
46
47
48
**
*******************************************************************************
**
** This file contains code for parsing URLs that appear on the command-line
*/
#include "config.h"
#include "url.h"











/*
** Parse the given URL.  Populate variables in the global "g" structure.
**
**      g.urlIsFile      True if FILE:
**      g.urlIsHttps     True if HTTPS: 

**      g.urlProtocol    "http" or "https" or "file"
**      g.urlName        Hostname for HTTP: or HTTPS:.  Filename for FILE:

**      g.urlPort        TCP port number for HTTP or HTTPS.
**      g.urlDfltPort    Default TCP port number (80 or 443).

**      g.urlPath        Path name for HTTP or HTTPS.
**      g.urlUser        Userid.
**      g.urlPasswd      Password.
**      g.urlHostname    HOST:PORT or just HOST if port is the default.
**      g.urlCanonical   The URL in canonical form, omitting the password
**
** HTTP url format is:
**
**     http://userid:password@host:port/path?query#fragment
**




*/
void url_parse(const char *zUrl){
  int i, j, c;
  char *zFile = 0;
  if( strncmp(zUrl, "http://", 7)==0 || strncmp(zUrl, "https://", 8)==0 ){
    int iStart;
    char *zLogin;







>
>
>
>
>
>
>
>
>
>






>


>


>










>
>
>
>







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
**
*******************************************************************************
**
** This file contains code for parsing URLs that appear on the command-line
*/
#include "config.h"
#include "url.h"

/*
** Convert a string to lower-case.
*/
static void url_tolower(char *z){
  while( *z ){
     *z = tolower(*z);
     z++;
  }
}

/*
** Parse the given URL.  Populate variables in the global "g" structure.
**
**      g.urlIsFile      True if FILE:
**      g.urlIsHttps     True if HTTPS: 
**      g.urlIsSsh       True if SSH:
**      g.urlProtocol    "http" or "https" or "file"
**      g.urlName        Hostname for HTTP: or HTTPS:.  Filename for FILE:
**      g.urlSshHost     Hostname for SSH: tunnel
**      g.urlPort        TCP port number for HTTP or HTTPS.
**      g.urlDfltPort    Default TCP port number (80 or 443).
**      g.urlSshPort     TCP port for SSH: tunnel
**      g.urlPath        Path name for HTTP or HTTPS.
**      g.urlUser        Userid.
**      g.urlPasswd      Password.
**      g.urlHostname    HOST:PORT or just HOST if port is the default.
**      g.urlCanonical   The URL in canonical form, omitting the password
**
** HTTP url format is:
**
**     http://userid:password@host:port/path?query#fragment
**
** SSH url format is:
**
**     ssh://userid@host:port/fullpath
**
*/
void url_parse(const char *zUrl){
  int i, j, c;
  char *zFile = 0;
  if( strncmp(zUrl, "http://", 7)==0 || strncmp(zUrl, "https://", 8)==0 ){
    int iStart;
    char *zLogin;
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
        dehttpize(g.urlPasswd);
      }
      for(j=i+1; (c=zUrl[j])!=0 && c!='/' && c!=':'; j++){}
      g.urlName = mprintf("%.*s", j-i-1, &zUrl[i+1]);
      i = j;
      zLogin = mprintf("%t@", g.urlUser);
    }else{
      for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!=':'; i++){}
      g.urlName = mprintf("%.*s", i-iStart, &zUrl[iStart]);
      zLogin = mprintf("");
    }
    for(j=0; g.urlName[j]; j++){ g.urlName[j] = tolower(g.urlName[j]); }
    if( c==':' ){
      g.urlPort = 0;
      i++;
      while( (c = zUrl[i])!=0 && isdigit(c) ){
        g.urlPort = g.urlPort*10 + c - '0';
        i++;
      }







<



|







85
86
87
88
89
90
91

92
93
94
95
96
97
98
99
100
101
102
        dehttpize(g.urlPasswd);
      }
      for(j=i+1; (c=zUrl[j])!=0 && c!='/' && c!=':'; j++){}
      g.urlName = mprintf("%.*s", j-i-1, &zUrl[i+1]);
      i = j;
      zLogin = mprintf("%t@", g.urlUser);
    }else{

      g.urlName = mprintf("%.*s", i-iStart, &zUrl[iStart]);
      zLogin = mprintf("");
    }
    url_tolower(g.urlName);
    if( c==':' ){
      g.urlPort = 0;
      i++;
      while( (c = zUrl[i])!=0 && isdigit(c) ){
        g.urlPort = g.urlPort*10 + c - '0';
        i++;
      }
99
100
101
102
103
104
105




































106
107
108
109
110
111
112
      );
    }else{
      g.urlCanonical = mprintf(
        "%s://%s%T:%d%T",
        g.urlProtocol, zLogin, g.urlName, g.urlPort, g.urlPath
      );
    }




































    free(zLogin);
  }else if( strncmp(zUrl, "file:", 5)==0 ){
    g.urlIsFile = 1;
    if( zUrl[5]=='/' && zUrl[6]=='/' ){
      i = 7;
    }else{
      i = 5;







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
      );
    }else{
      g.urlCanonical = mprintf(
        "%s://%s%T:%d%T",
        g.urlProtocol, zLogin, g.urlName, g.urlPort, g.urlPath
      );
    }
    free(zLogin);
  }else if( strncmp(zUrl, "ssh://", 6)==0 ){
    char *zLogin;
    int r;
    g.urlIsFile = 0;
    g.urlIsSsh = 1;
    g.urlProtocol = "ssh";
    sqlite3_randomness(sizeof(r), &r);
    g.urlPort = 18800 + (r & 0x7fffff)%2000;
    g.urlDfltPort = 80;
    g.urlName = "127.0.0.1";
    g.urlHostname = g.urlName;
    for(i=6; (c=zUrl[i])!=0 && c!='/' && c!='@'; i++){}
    if( c=='@' ){
      for(j=6; j<i && zUrl[j]!=':'; j++){}
      g.urlUser = mprintf("%.*s", j-6, &zUrl[6]);
      dehttpize(g.urlUser);
      if( j<i ){
        g.urlPasswd = mprintf("%.*s", i-j-1, &zUrl[j+1]);
        dehttpize(g.urlPasswd);
      }
      for(j=i+1; (c=zUrl[j])!=0 && c!='/'; j++){}
      g.urlSshHost = mprintf("%.*s", j-i-1, &zUrl[i+1]);
      i = j;
      zLogin = mprintf("%t@", g.urlUser);
    }else{
      g.urlSshHost = mprintf("%.*s", i-6, &zUrl[6]);
      zLogin = mprintf("");
    }
    url_tolower(g.urlSshHost);
    g.urlPath = mprintf(&zUrl[i+1]);
    dehttpize(g.urlPath);
    g.urlCanonical = mprintf(
        "ssh://%s%T/%T", 
        zLogin, g.urlSshHost, g.urlPath
    );
    free(zLogin);
  }else if( strncmp(zUrl, "file:", 5)==0 ){
    g.urlIsFile = 1;
    if( zUrl[5]=='/' && zUrl[6]=='/' ){
      i = 7;
    }else{
      i = 5;
148
149
150
151
152
153
154

155
156

157
158
159
160
161
162
163

164
165
166
167
168
169
170
  if( g.argc!=3 && g.argc!=4 ){
    usage("URL");
  }
  url_parse(g.argv[2]);
  for(i=0; i<2; i++){
    printf("g.urlIsFile    = %d\n", g.urlIsFile);
    printf("g.urlIsHttps   = %d\n", g.urlIsHttps);

    printf("g.urlProtocol  = %s\n", g.urlProtocol);
    printf("g.urlName      = %s\n", g.urlName);

    printf("g.urlPort      = %d\n", g.urlPort);
    printf("g.urlDfltPort  = %d\n", g.urlDfltPort);
    printf("g.urlHostname  = %s\n", g.urlHostname);
    printf("g.urlPath      = %s\n", g.urlPath);
    printf("g.urlUser      = %s\n", g.urlUser);
    printf("g.urlPasswd    = %s\n", g.urlPasswd);
    printf("g.urlCanonical = %s\n", g.urlCanonical);

    if( i==0 ){
      printf("********\n");
      url_enable_proxy("Using proxy: ");
    }
  }
}








>


>







>







200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
  if( g.argc!=3 && g.argc!=4 ){
    usage("URL");
  }
  url_parse(g.argv[2]);
  for(i=0; i<2; i++){
    printf("g.urlIsFile    = %d\n", g.urlIsFile);
    printf("g.urlIsHttps   = %d\n", g.urlIsHttps);
    printf("g.urlIsSsh     = %d\n", g.urlIsSsh);
    printf("g.urlProtocol  = %s\n", g.urlProtocol);
    printf("g.urlName      = %s\n", g.urlName);
    printf("g.urlSshHost   = %s\n", g.urlSshHost);
    printf("g.urlPort      = %d\n", g.urlPort);
    printf("g.urlDfltPort  = %d\n", g.urlDfltPort);
    printf("g.urlHostname  = %s\n", g.urlHostname);
    printf("g.urlPath      = %s\n", g.urlPath);
    printf("g.urlUser      = %s\n", g.urlUser);
    printf("g.urlPasswd    = %s\n", g.urlPasswd);
    printf("g.urlCanonical = %s\n", g.urlCanonical);
    if( g.urlIsFile || g.urlIsSsh ) break;
    if( i==0 ){
      printf("********\n");
      url_enable_proxy("Using proxy: ");
    }
  }
}

Changes to src/xfer.c.
984
985
986
987
988
989
990

991
992
993
994
995
996
997
    nCardSent++;
  }
  if( pushFlag ){
    blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
    nCardSent++;
  }
  manifest_crosslink_begin();

  fossil_print(zLabelFormat, "", "Bytes", "Cards", "Artifacts", "Deltas");

  while( go ){
    int newPhantom = 0;
    char *zRandomness;

    /* Send make the most recently received cookie.  Let the server







>







984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
    nCardSent++;
  }
  if( pushFlag ){
    blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
    nCardSent++;
  }
  manifest_crosslink_begin();
  transport_global_startup();
  fossil_print(zLabelFormat, "", "Bytes", "Cards", "Artifacts", "Deltas");

  while( go ){
    int newPhantom = 0;
    char *zRandomness;

    /* Send make the most recently received cookie.  Let the server