Fossil SCM

Add support for tunneling https through a http proxy (Ticket [e854101c4f])

jan.nijtmans 2014-02-06 13:42 trunk merge
Commit 3a3343566663410c7672b52bb4b0b10d05b7ac39
+97 -23
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -173,10 +173,58 @@
173173
if( iBio!=NULL ){
174174
(void)BIO_reset(iBio);
175175
BIO_free_all(iBio);
176176
}
177177
}
178
+
179
+/* See RFC2817 for details */
180
+static int establish_proxy_tunnel(UrlData *pUrlData, BIO *bio){
181
+ int rc, httpVerMin;
182
+ char *bbuf;
183
+ Blob snd, reply;
184
+ int done=0,end=0;
185
+ blob_zero(&snd);
186
+ blob_appendf(&snd, "CONNECT %s:%d HTTP/1.1\r\n", pUrlData->hostname,
187
+ pUrlData->proxyOrigPort);
188
+ blob_appendf(&snd, "Host: %s:%d\r\n", pUrlData->hostname, pUrlData->proxyOrigPort);
189
+ if( pUrlData->proxyAuth ){
190
+ blob_appendf(&snd, "Proxy-Authorization: %s\r\n", pUrlData->proxyAuth);
191
+ }
192
+ blob_append(&snd, "Proxy-Connection: keep-alive\r\n", -1);
193
+ blob_append(&snd, "User-Agent: Fossil/" RELEASE_VERSION "\r\n", -1);
194
+ blob_append(&snd, "\r\n", 2);
195
+ BIO_write(bio, blob_buffer(&snd), blob_size(&snd));
196
+ blob_reset(&snd);
197
+
198
+ /* Wait for end of reply */
199
+ blob_zero(&reply);
200
+ do{
201
+ int len;
202
+ char buf[256];
203
+ len = BIO_read(bio, buf, sizeof(buf));
204
+ blob_append(&reply, buf, len);
205
+
206
+ bbuf = blob_buffer(&reply);
207
+ len = blob_size(&reply);
208
+ while(end < len) {
209
+ if(bbuf[end] == '\r') {
210
+ if(len - end < 4) {
211
+ /* need more data */
212
+ break;
213
+ }
214
+ if(memcmp(&bbuf[end], "\r\n\r\n", 4) == 0) {
215
+ done = 1;
216
+ break;
217
+ }
218
+ }
219
+ end++;
220
+ }
221
+ }while(!done);
222
+ sscanf(bbuf, "HTTP/1.%d %d", &httpVerMin, &rc);
223
+ blob_reset(&reply);
224
+ return rc;
225
+}
178226
179227
/*
180228
** Open an SSL connection. The identify of the server is determined
181229
** by variables that are set using url_parse():
182230
**
@@ -201,41 +249,67 @@
201249
X509_STORE_add_cert(SSL_CTX_get_cert_store(sslCtx), cert);
202250
X509_free(cert);
203251
hasSavedCertificate = 1;
204252
}
205253
206
- iBio = BIO_new_ssl_connect(sslCtx);
254
+ if( pUrlData->useProxy ){
255
+ int rc;
256
+ BIO *sBio;
257
+ char *connStr;
258
+ connStr = mprintf("%s:%d", g.urlName, pUrlData->port);
259
+ sBio = BIO_new_connect(connStr);
260
+ free(connStr);
261
+ if( BIO_do_connect(sBio)<=0 ){
262
+ ssl_set_errmsg("SSL: cannot connect to proxy %s:%d (%s)",
263
+ pUrlData->name, pUrlData->port, ERR_reason_error_string(ERR_get_error()));
264
+ ssl_close();
265
+ return 1;
266
+ }
267
+ rc = establish_proxy_tunnel(pUrlData, sBio);
268
+ if( rc<200||rc>299 ){
269
+ ssl_set_errmsg("SSL: proxy connect failed with HTTP status code %d", rc);
270
+ return 1;
271
+ }
272
+
273
+ pUrlData->path = pUrlData->proxyUrlPath;
274
+
275
+ iBio = BIO_new_ssl(sslCtx, 1);
276
+ BIO_push(iBio, sBio);
277
+ }else{
278
+ iBio = BIO_new_ssl_connect(sslCtx);
279
+ }
280
+ if( iBio==NULL ) {
281
+ ssl_set_errmsg("SSL: cannot open SSL (%s)",
282
+ ERR_reason_error_string(ERR_get_error()));
283
+ return 1;
284
+ }
207285
BIO_get_ssl(iBio, &ssl);
208286
209287
#if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT)
210
- if( !SSL_set_tlsext_host_name(ssl, pUrlData->name) ){
288
+ if( !SSL_set_tlsext_host_name(ssl, pUrlData->useProxy?pUrlData->hostname:pUrlData->name) ){
211289
fossil_warning("WARNING: failed to set server name indication (SNI), "
212290
"continuing without it.\n");
213291
}
214292
#endif
215293
216294
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
217
- if( iBio==NULL ) {
218
- ssl_set_errmsg("SSL: cannot open SSL (%s)",
219
- ERR_reason_error_string(ERR_get_error()));
220
- return 1;
221
- }
222
-
223
- BIO_set_conn_hostname(iBio, pUrlData->name);
224
- BIO_set_conn_int_port(iBio, &pUrlData->port);
225
-
226
- if( BIO_do_connect(iBio)<=0 ){
227
- ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)",
228
- pUrlData->name, pUrlData->port,
229
- ERR_reason_error_string(ERR_get_error()));
230
- ssl_close();
231
- return 1;
295
+
296
+ if( !pUrlData->useProxy ){
297
+ BIO_set_conn_hostname(iBio, pUrlData->name);
298
+ BIO_set_conn_int_port(iBio, &pUrlData->port);
299
+ if( BIO_do_connect(iBio)<=0 ){
300
+ ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)",
301
+ pUrlData->name, pUrlData->port, ERR_reason_error_string(ERR_get_error()));
302
+ ssl_close();
303
+ return 1;
304
+ }
232305
}
233306
234307
if( BIO_do_handshake(iBio)<=0 ) {
235308
ssl_set_errmsg("Error establishing SSL connection %s:%d (%s)",
236
- pUrlData->name, pUrlData->port,
309
+ pUrlData->useProxy?pUrlData->hostname:pUrlData->name,
310
+ pUrlData->useProxy?pUrlData->proxyOrigPort:pUrlData->port,
237311
ERR_reason_error_string(ERR_get_error()));
238312
ssl_close();
239313
return 1;
240314
}
241315
/* Check if certificate is valid */
@@ -283,11 +357,11 @@
283357
" certificates list\n\n"
284358
"If you are not expecting this message, answer no and "
285359
"contact your server\nadministrator.\n\n"
286360
"Accept certificate for host %s (a=always/y/N)? ",
287361
X509_verify_cert_error_string(e), desc, warning,
288
- pUrlData->name);
362
+ pUrlData->useProxy?pUrlData->hostname:pUrlData->name);
289363
BIO_free(mem);
290364
291365
prompt_user(prompt, &ans);
292366
free(prompt);
293367
cReply = blob_str(&ans)[0];
@@ -333,14 +407,14 @@
333407
334408
mem = BIO_new(BIO_s_mem());
335409
PEM_write_bio_X509(mem, cert);
336410
BIO_write(mem, "", 1); /* nul-terminate mem buffer */
337411
BIO_get_mem_data(mem, &zCert);
338
- zHost = mprintf("cert:%s", pUrlData->name);
412
+ zHost = mprintf("cert:%s", pUrlData->useProxy?pUrlData->hostname:pUrlData->name);
339413
db_set(zHost, zCert, 1);
340414
free(zHost);
341
- zHost = mprintf("trusted:%s", pUrlData->name);
415
+ zHost = mprintf("trusted:%s", pUrlData->useProxy?pUrlData->hostname:pUrlData->name);
342416
db_set_int(zHost, trusted, 1);
343417
free(zHost);
344418
BIO_free(mem);
345419
}
346420
@@ -351,18 +425,18 @@
351425
X509 *ssl_get_certificate(UrlData *pUrlData, int *pTrusted){
352426
char *zHost, *zCert;
353427
BIO *mem;
354428
X509 *cert;
355429
356
- zHost = mprintf("cert:%s", pUrlData->name);
430
+ zHost = mprintf("cert:%s", pUrlData->useProxy?pUrlData->hostname:pUrlData->name);
357431
zCert = db_get(zHost, NULL);
358432
free(zHost);
359433
if ( zCert==NULL )
360434
return NULL;
361435
362436
if ( pTrusted!=0 ){
363
- zHost = mprintf("trusted:%s", pUrlData->name);
437
+ zHost = mprintf("trusted:%s", pUrlData->useProxy?pUrlData->hostname:pUrlData->name);
364438
*pTrusted = db_get_int(zHost, 0);
365439
free(zHost);
366440
}
367441
368442
mem = BIO_new(BIO_s_mem());
369443
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -173,10 +173,58 @@
173 if( iBio!=NULL ){
174 (void)BIO_reset(iBio);
175 BIO_free_all(iBio);
176 }
177 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
179 /*
180 ** Open an SSL connection. The identify of the server is determined
181 ** by variables that are set using url_parse():
182 **
@@ -201,41 +249,67 @@
201 X509_STORE_add_cert(SSL_CTX_get_cert_store(sslCtx), cert);
202 X509_free(cert);
203 hasSavedCertificate = 1;
204 }
205
206 iBio = BIO_new_ssl_connect(sslCtx);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207 BIO_get_ssl(iBio, &ssl);
208
209 #if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT)
210 if( !SSL_set_tlsext_host_name(ssl, pUrlData->name) ){
211 fossil_warning("WARNING: failed to set server name indication (SNI), "
212 "continuing without it.\n");
213 }
214 #endif
215
216 SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
217 if( iBio==NULL ) {
218 ssl_set_errmsg("SSL: cannot open SSL (%s)",
219 ERR_reason_error_string(ERR_get_error()));
220 return 1;
221 }
222
223 BIO_set_conn_hostname(iBio, pUrlData->name);
224 BIO_set_conn_int_port(iBio, &pUrlData->port);
225
226 if( BIO_do_connect(iBio)<=0 ){
227 ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)",
228 pUrlData->name, pUrlData->port,
229 ERR_reason_error_string(ERR_get_error()));
230 ssl_close();
231 return 1;
232 }
233
234 if( BIO_do_handshake(iBio)<=0 ) {
235 ssl_set_errmsg("Error establishing SSL connection %s:%d (%s)",
236 pUrlData->name, pUrlData->port,
 
237 ERR_reason_error_string(ERR_get_error()));
238 ssl_close();
239 return 1;
240 }
241 /* Check if certificate is valid */
@@ -283,11 +357,11 @@
283 " certificates list\n\n"
284 "If you are not expecting this message, answer no and "
285 "contact your server\nadministrator.\n\n"
286 "Accept certificate for host %s (a=always/y/N)? ",
287 X509_verify_cert_error_string(e), desc, warning,
288 pUrlData->name);
289 BIO_free(mem);
290
291 prompt_user(prompt, &ans);
292 free(prompt);
293 cReply = blob_str(&ans)[0];
@@ -333,14 +407,14 @@
333
334 mem = BIO_new(BIO_s_mem());
335 PEM_write_bio_X509(mem, cert);
336 BIO_write(mem, "", 1); /* nul-terminate mem buffer */
337 BIO_get_mem_data(mem, &zCert);
338 zHost = mprintf("cert:%s", pUrlData->name);
339 db_set(zHost, zCert, 1);
340 free(zHost);
341 zHost = mprintf("trusted:%s", pUrlData->name);
342 db_set_int(zHost, trusted, 1);
343 free(zHost);
344 BIO_free(mem);
345 }
346
@@ -351,18 +425,18 @@
351 X509 *ssl_get_certificate(UrlData *pUrlData, int *pTrusted){
352 char *zHost, *zCert;
353 BIO *mem;
354 X509 *cert;
355
356 zHost = mprintf("cert:%s", pUrlData->name);
357 zCert = db_get(zHost, NULL);
358 free(zHost);
359 if ( zCert==NULL )
360 return NULL;
361
362 if ( pTrusted!=0 ){
363 zHost = mprintf("trusted:%s", pUrlData->name);
364 *pTrusted = db_get_int(zHost, 0);
365 free(zHost);
366 }
367
368 mem = BIO_new(BIO_s_mem());
369
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -173,10 +173,58 @@
173 if( iBio!=NULL ){
174 (void)BIO_reset(iBio);
175 BIO_free_all(iBio);
176 }
177 }
178
179 /* See RFC2817 for details */
180 static int establish_proxy_tunnel(UrlData *pUrlData, BIO *bio){
181 int rc, httpVerMin;
182 char *bbuf;
183 Blob snd, reply;
184 int done=0,end=0;
185 blob_zero(&snd);
186 blob_appendf(&snd, "CONNECT %s:%d HTTP/1.1\r\n", pUrlData->hostname,
187 pUrlData->proxyOrigPort);
188 blob_appendf(&snd, "Host: %s:%d\r\n", pUrlData->hostname, pUrlData->proxyOrigPort);
189 if( pUrlData->proxyAuth ){
190 blob_appendf(&snd, "Proxy-Authorization: %s\r\n", pUrlData->proxyAuth);
191 }
192 blob_append(&snd, "Proxy-Connection: keep-alive\r\n", -1);
193 blob_append(&snd, "User-Agent: Fossil/" RELEASE_VERSION "\r\n", -1);
194 blob_append(&snd, "\r\n", 2);
195 BIO_write(bio, blob_buffer(&snd), blob_size(&snd));
196 blob_reset(&snd);
197
198 /* Wait for end of reply */
199 blob_zero(&reply);
200 do{
201 int len;
202 char buf[256];
203 len = BIO_read(bio, buf, sizeof(buf));
204 blob_append(&reply, buf, len);
205
206 bbuf = blob_buffer(&reply);
207 len = blob_size(&reply);
208 while(end < len) {
209 if(bbuf[end] == '\r') {
210 if(len - end < 4) {
211 /* need more data */
212 break;
213 }
214 if(memcmp(&bbuf[end], "\r\n\r\n", 4) == 0) {
215 done = 1;
216 break;
217 }
218 }
219 end++;
220 }
221 }while(!done);
222 sscanf(bbuf, "HTTP/1.%d %d", &httpVerMin, &rc);
223 blob_reset(&reply);
224 return rc;
225 }
226
227 /*
228 ** Open an SSL connection. The identify of the server is determined
229 ** by variables that are set using url_parse():
230 **
@@ -201,41 +249,67 @@
249 X509_STORE_add_cert(SSL_CTX_get_cert_store(sslCtx), cert);
250 X509_free(cert);
251 hasSavedCertificate = 1;
252 }
253
254 if( pUrlData->useProxy ){
255 int rc;
256 BIO *sBio;
257 char *connStr;
258 connStr = mprintf("%s:%d", g.urlName, pUrlData->port);
259 sBio = BIO_new_connect(connStr);
260 free(connStr);
261 if( BIO_do_connect(sBio)<=0 ){
262 ssl_set_errmsg("SSL: cannot connect to proxy %s:%d (%s)",
263 pUrlData->name, pUrlData->port, ERR_reason_error_string(ERR_get_error()));
264 ssl_close();
265 return 1;
266 }
267 rc = establish_proxy_tunnel(pUrlData, sBio);
268 if( rc<200||rc>299 ){
269 ssl_set_errmsg("SSL: proxy connect failed with HTTP status code %d", rc);
270 return 1;
271 }
272
273 pUrlData->path = pUrlData->proxyUrlPath;
274
275 iBio = BIO_new_ssl(sslCtx, 1);
276 BIO_push(iBio, sBio);
277 }else{
278 iBio = BIO_new_ssl_connect(sslCtx);
279 }
280 if( iBio==NULL ) {
281 ssl_set_errmsg("SSL: cannot open SSL (%s)",
282 ERR_reason_error_string(ERR_get_error()));
283 return 1;
284 }
285 BIO_get_ssl(iBio, &ssl);
286
287 #if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT)
288 if( !SSL_set_tlsext_host_name(ssl, pUrlData->useProxy?pUrlData->hostname:pUrlData->name) ){
289 fossil_warning("WARNING: failed to set server name indication (SNI), "
290 "continuing without it.\n");
291 }
292 #endif
293
294 SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
295
296 if( !pUrlData->useProxy ){
297 BIO_set_conn_hostname(iBio, pUrlData->name);
298 BIO_set_conn_int_port(iBio, &pUrlData->port);
299 if( BIO_do_connect(iBio)<=0 ){
300 ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)",
301 pUrlData->name, pUrlData->port, ERR_reason_error_string(ERR_get_error()));
302 ssl_close();
303 return 1;
304 }
 
 
 
 
 
305 }
306
307 if( BIO_do_handshake(iBio)<=0 ) {
308 ssl_set_errmsg("Error establishing SSL connection %s:%d (%s)",
309 pUrlData->useProxy?pUrlData->hostname:pUrlData->name,
310 pUrlData->useProxy?pUrlData->proxyOrigPort:pUrlData->port,
311 ERR_reason_error_string(ERR_get_error()));
312 ssl_close();
313 return 1;
314 }
315 /* Check if certificate is valid */
@@ -283,11 +357,11 @@
357 " certificates list\n\n"
358 "If you are not expecting this message, answer no and "
359 "contact your server\nadministrator.\n\n"
360 "Accept certificate for host %s (a=always/y/N)? ",
361 X509_verify_cert_error_string(e), desc, warning,
362 pUrlData->useProxy?pUrlData->hostname:pUrlData->name);
363 BIO_free(mem);
364
365 prompt_user(prompt, &ans);
366 free(prompt);
367 cReply = blob_str(&ans)[0];
@@ -333,14 +407,14 @@
407
408 mem = BIO_new(BIO_s_mem());
409 PEM_write_bio_X509(mem, cert);
410 BIO_write(mem, "", 1); /* nul-terminate mem buffer */
411 BIO_get_mem_data(mem, &zCert);
412 zHost = mprintf("cert:%s", pUrlData->useProxy?pUrlData->hostname:pUrlData->name);
413 db_set(zHost, zCert, 1);
414 free(zHost);
415 zHost = mprintf("trusted:%s", pUrlData->useProxy?pUrlData->hostname:pUrlData->name);
416 db_set_int(zHost, trusted, 1);
417 free(zHost);
418 BIO_free(mem);
419 }
420
@@ -351,18 +425,18 @@
425 X509 *ssl_get_certificate(UrlData *pUrlData, int *pTrusted){
426 char *zHost, *zCert;
427 BIO *mem;
428 X509 *cert;
429
430 zHost = mprintf("cert:%s", pUrlData->useProxy?pUrlData->hostname:pUrlData->name);
431 zCert = db_get(zHost, NULL);
432 free(zHost);
433 if ( zCert==NULL )
434 return NULL;
435
436 if ( pTrusted!=0 ){
437 zHost = mprintf("trusted:%s", pUrlData->useProxy?pUrlData->hostname:pUrlData->name);
438 *pTrusted = db_get_int(zHost, 0);
439 free(zHost);
440 }
441
442 mem = BIO_new(BIO_s_mem());
443
+97 -23
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -173,10 +173,58 @@
173173
if( iBio!=NULL ){
174174
(void)BIO_reset(iBio);
175175
BIO_free_all(iBio);
176176
}
177177
}
178
+
179
+/* See RFC2817 for details */
180
+static int establish_proxy_tunnel(UrlData *pUrlData, BIO *bio){
181
+ int rc, httpVerMin;
182
+ char *bbuf;
183
+ Blob snd, reply;
184
+ int done=0,end=0;
185
+ blob_zero(&snd);
186
+ blob_appendf(&snd, "CONNECT %s:%d HTTP/1.1\r\n", pUrlData->hostname,
187
+ pUrlData->proxyOrigPort);
188
+ blob_appendf(&snd, "Host: %s:%d\r\n", pUrlData->hostname, pUrlData->proxyOrigPort);
189
+ if( pUrlData->proxyAuth ){
190
+ blob_appendf(&snd, "Proxy-Authorization: %s\r\n", pUrlData->proxyAuth);
191
+ }
192
+ blob_append(&snd, "Proxy-Connection: keep-alive\r\n", -1);
193
+ blob_append(&snd, "User-Agent: Fossil/" RELEASE_VERSION "\r\n", -1);
194
+ blob_append(&snd, "\r\n", 2);
195
+ BIO_write(bio, blob_buffer(&snd), blob_size(&snd));
196
+ blob_reset(&snd);
197
+
198
+ /* Wait for end of reply */
199
+ blob_zero(&reply);
200
+ do{
201
+ int len;
202
+ char buf[256];
203
+ len = BIO_read(bio, buf, sizeof(buf));
204
+ blob_append(&reply, buf, len);
205
+
206
+ bbuf = blob_buffer(&reply);
207
+ len = blob_size(&reply);
208
+ while(end < len) {
209
+ if(bbuf[end] == '\r') {
210
+ if(len - end < 4) {
211
+ /* need more data */
212
+ break;
213
+ }
214
+ if(memcmp(&bbuf[end], "\r\n\r\n", 4) == 0) {
215
+ done = 1;
216
+ break;
217
+ }
218
+ }
219
+ end++;
220
+ }
221
+ }while(!done);
222
+ sscanf(bbuf, "HTTP/1.%d %d", &httpVerMin, &rc);
223
+ blob_reset(&reply);
224
+ return rc;
225
+}
178226
179227
/*
180228
** Open an SSL connection. The identify of the server is determined
181229
** by variables that are set using url_parse():
182230
**
@@ -201,41 +249,67 @@
201249
X509_STORE_add_cert(SSL_CTX_get_cert_store(sslCtx), cert);
202250
X509_free(cert);
203251
hasSavedCertificate = 1;
204252
}
205253
206
- iBio = BIO_new_ssl_connect(sslCtx);
254
+ if( pUrlData->useProxy ){
255
+ int rc;
256
+ BIO *sBio;
257
+ char *connStr;
258
+ connStr = mprintf("%s:%d", g.urlName, pUrlData->port);
259
+ sBio = BIO_new_connect(connStr);
260
+ free(connStr);
261
+ if( BIO_do_connect(sBio)<=0 ){
262
+ ssl_set_errmsg("SSL: cannot connect to proxy %s:%d (%s)",
263
+ pUrlData->name, pUrlData->port, ERR_reason_error_string(ERR_get_error()));
264
+ ssl_close();
265
+ return 1;
266
+ }
267
+ rc = establish_proxy_tunnel(pUrlData, sBio);
268
+ if( rc<200||rc>299 ){
269
+ ssl_set_errmsg("SSL: proxy connect failed with HTTP status code %d", rc);
270
+ return 1;
271
+ }
272
+
273
+ pUrlData->path = pUrlData->proxyUrlPath;
274
+
275
+ iBio = BIO_new_ssl(sslCtx, 1);
276
+ BIO_push(iBio, sBio);
277
+ }else{
278
+ iBio = BIO_new_ssl_connect(sslCtx);
279
+ }
280
+ if( iBio==NULL ) {
281
+ ssl_set_errmsg("SSL: cannot open SSL (%s)",
282
+ ERR_reason_error_string(ERR_get_error()));
283
+ return 1;
284
+ }
207285
BIO_get_ssl(iBio, &ssl);
208286
209287
#if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT)
210
- if( !SSL_set_tlsext_host_name(ssl, pUrlData->name) ){
288
+ if( !SSL_set_tlsext_host_name(ssl, pUrlData->useProxy?pUrlData->hostname:pUrlData->name) ){
211289
fossil_warning("WARNING: failed to set server name indication (SNI), "
212290
"continuing without it.\n");
213291
}
214292
#endif
215293
216294
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
217
- if( iBio==NULL ) {
218
- ssl_set_errmsg("SSL: cannot open SSL (%s)",
219
- ERR_reason_error_string(ERR_get_error()));
220
- return 1;
221
- }
222
-
223
- BIO_set_conn_hostname(iBio, pUrlData->name);
224
- BIO_set_conn_int_port(iBio, &pUrlData->port);
225
-
226
- if( BIO_do_connect(iBio)<=0 ){
227
- ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)",
228
- pUrlData->name, pUrlData->port,
229
- ERR_reason_error_string(ERR_get_error()));
230
- ssl_close();
231
- return 1;
295
+
296
+ if( !pUrlData->useProxy ){
297
+ BIO_set_conn_hostname(iBio, pUrlData->name);
298
+ BIO_set_conn_int_port(iBio, &pUrlData->port);
299
+ if( BIO_do_connect(iBio)<=0 ){
300
+ ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)",
301
+ pUrlData->name, pUrlData->port, ERR_reason_error_string(ERR_get_error()));
302
+ ssl_close();
303
+ return 1;
304
+ }
232305
}
233306
234307
if( BIO_do_handshake(iBio)<=0 ) {
235308
ssl_set_errmsg("Error establishing SSL connection %s:%d (%s)",
236
- pUrlData->name, pUrlData->port,
309
+ pUrlData->useProxy?pUrlData->hostname:pUrlData->name,
310
+ pUrlData->useProxy?pUrlData->proxyOrigPort:pUrlData->port,
237311
ERR_reason_error_string(ERR_get_error()));
238312
ssl_close();
239313
return 1;
240314
}
241315
/* Check if certificate is valid */
@@ -283,11 +357,11 @@
283357
" certificates list\n\n"
284358
"If you are not expecting this message, answer no and "
285359
"contact your server\nadministrator.\n\n"
286360
"Accept certificate for host %s (a=always/y/N)? ",
287361
X509_verify_cert_error_string(e), desc, warning,
288
- pUrlData->name);
362
+ pUrlData->useProxy?pUrlData->hostname:pUrlData->name);
289363
BIO_free(mem);
290364
291365
prompt_user(prompt, &ans);
292366
free(prompt);
293367
cReply = blob_str(&ans)[0];
@@ -333,14 +407,14 @@
333407
334408
mem = BIO_new(BIO_s_mem());
335409
PEM_write_bio_X509(mem, cert);
336410
BIO_write(mem, "", 1); /* nul-terminate mem buffer */
337411
BIO_get_mem_data(mem, &zCert);
338
- zHost = mprintf("cert:%s", pUrlData->name);
412
+ zHost = mprintf("cert:%s", pUrlData->useProxy?pUrlData->hostname:pUrlData->name);
339413
db_set(zHost, zCert, 1);
340414
free(zHost);
341
- zHost = mprintf("trusted:%s", pUrlData->name);
415
+ zHost = mprintf("trusted:%s", pUrlData->useProxy?pUrlData->hostname:pUrlData->name);
342416
db_set_int(zHost, trusted, 1);
343417
free(zHost);
344418
BIO_free(mem);
345419
}
346420
@@ -351,18 +425,18 @@
351425
X509 *ssl_get_certificate(UrlData *pUrlData, int *pTrusted){
352426
char *zHost, *zCert;
353427
BIO *mem;
354428
X509 *cert;
355429
356
- zHost = mprintf("cert:%s", pUrlData->name);
430
+ zHost = mprintf("cert:%s", pUrlData->useProxy?pUrlData->hostname:pUrlData->name);
357431
zCert = db_get(zHost, NULL);
358432
free(zHost);
359433
if ( zCert==NULL )
360434
return NULL;
361435
362436
if ( pTrusted!=0 ){
363
- zHost = mprintf("trusted:%s", pUrlData->name);
437
+ zHost = mprintf("trusted:%s", pUrlData->useProxy?pUrlData->hostname:pUrlData->name);
364438
*pTrusted = db_get_int(zHost, 0);
365439
free(zHost);
366440
}
367441
368442
mem = BIO_new(BIO_s_mem());
369443
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -173,10 +173,58 @@
173 if( iBio!=NULL ){
174 (void)BIO_reset(iBio);
175 BIO_free_all(iBio);
176 }
177 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
179 /*
180 ** Open an SSL connection. The identify of the server is determined
181 ** by variables that are set using url_parse():
182 **
@@ -201,41 +249,67 @@
201 X509_STORE_add_cert(SSL_CTX_get_cert_store(sslCtx), cert);
202 X509_free(cert);
203 hasSavedCertificate = 1;
204 }
205
206 iBio = BIO_new_ssl_connect(sslCtx);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207 BIO_get_ssl(iBio, &ssl);
208
209 #if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT)
210 if( !SSL_set_tlsext_host_name(ssl, pUrlData->name) ){
211 fossil_warning("WARNING: failed to set server name indication (SNI), "
212 "continuing without it.\n");
213 }
214 #endif
215
216 SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
217 if( iBio==NULL ) {
218 ssl_set_errmsg("SSL: cannot open SSL (%s)",
219 ERR_reason_error_string(ERR_get_error()));
220 return 1;
221 }
222
223 BIO_set_conn_hostname(iBio, pUrlData->name);
224 BIO_set_conn_int_port(iBio, &pUrlData->port);
225
226 if( BIO_do_connect(iBio)<=0 ){
227 ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)",
228 pUrlData->name, pUrlData->port,
229 ERR_reason_error_string(ERR_get_error()));
230 ssl_close();
231 return 1;
232 }
233
234 if( BIO_do_handshake(iBio)<=0 ) {
235 ssl_set_errmsg("Error establishing SSL connection %s:%d (%s)",
236 pUrlData->name, pUrlData->port,
 
237 ERR_reason_error_string(ERR_get_error()));
238 ssl_close();
239 return 1;
240 }
241 /* Check if certificate is valid */
@@ -283,11 +357,11 @@
283 " certificates list\n\n"
284 "If you are not expecting this message, answer no and "
285 "contact your server\nadministrator.\n\n"
286 "Accept certificate for host %s (a=always/y/N)? ",
287 X509_verify_cert_error_string(e), desc, warning,
288 pUrlData->name);
289 BIO_free(mem);
290
291 prompt_user(prompt, &ans);
292 free(prompt);
293 cReply = blob_str(&ans)[0];
@@ -333,14 +407,14 @@
333
334 mem = BIO_new(BIO_s_mem());
335 PEM_write_bio_X509(mem, cert);
336 BIO_write(mem, "", 1); /* nul-terminate mem buffer */
337 BIO_get_mem_data(mem, &zCert);
338 zHost = mprintf("cert:%s", pUrlData->name);
339 db_set(zHost, zCert, 1);
340 free(zHost);
341 zHost = mprintf("trusted:%s", pUrlData->name);
342 db_set_int(zHost, trusted, 1);
343 free(zHost);
344 BIO_free(mem);
345 }
346
@@ -351,18 +425,18 @@
351 X509 *ssl_get_certificate(UrlData *pUrlData, int *pTrusted){
352 char *zHost, *zCert;
353 BIO *mem;
354 X509 *cert;
355
356 zHost = mprintf("cert:%s", pUrlData->name);
357 zCert = db_get(zHost, NULL);
358 free(zHost);
359 if ( zCert==NULL )
360 return NULL;
361
362 if ( pTrusted!=0 ){
363 zHost = mprintf("trusted:%s", pUrlData->name);
364 *pTrusted = db_get_int(zHost, 0);
365 free(zHost);
366 }
367
368 mem = BIO_new(BIO_s_mem());
369
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -173,10 +173,58 @@
173 if( iBio!=NULL ){
174 (void)BIO_reset(iBio);
175 BIO_free_all(iBio);
176 }
177 }
178
179 /* See RFC2817 for details */
180 static int establish_proxy_tunnel(UrlData *pUrlData, BIO *bio){
181 int rc, httpVerMin;
182 char *bbuf;
183 Blob snd, reply;
184 int done=0,end=0;
185 blob_zero(&snd);
186 blob_appendf(&snd, "CONNECT %s:%d HTTP/1.1\r\n", pUrlData->hostname,
187 pUrlData->proxyOrigPort);
188 blob_appendf(&snd, "Host: %s:%d\r\n", pUrlData->hostname, pUrlData->proxyOrigPort);
189 if( pUrlData->proxyAuth ){
190 blob_appendf(&snd, "Proxy-Authorization: %s\r\n", pUrlData->proxyAuth);
191 }
192 blob_append(&snd, "Proxy-Connection: keep-alive\r\n", -1);
193 blob_append(&snd, "User-Agent: Fossil/" RELEASE_VERSION "\r\n", -1);
194 blob_append(&snd, "\r\n", 2);
195 BIO_write(bio, blob_buffer(&snd), blob_size(&snd));
196 blob_reset(&snd);
197
198 /* Wait for end of reply */
199 blob_zero(&reply);
200 do{
201 int len;
202 char buf[256];
203 len = BIO_read(bio, buf, sizeof(buf));
204 blob_append(&reply, buf, len);
205
206 bbuf = blob_buffer(&reply);
207 len = blob_size(&reply);
208 while(end < len) {
209 if(bbuf[end] == '\r') {
210 if(len - end < 4) {
211 /* need more data */
212 break;
213 }
214 if(memcmp(&bbuf[end], "\r\n\r\n", 4) == 0) {
215 done = 1;
216 break;
217 }
218 }
219 end++;
220 }
221 }while(!done);
222 sscanf(bbuf, "HTTP/1.%d %d", &httpVerMin, &rc);
223 blob_reset(&reply);
224 return rc;
225 }
226
227 /*
228 ** Open an SSL connection. The identify of the server is determined
229 ** by variables that are set using url_parse():
230 **
@@ -201,41 +249,67 @@
249 X509_STORE_add_cert(SSL_CTX_get_cert_store(sslCtx), cert);
250 X509_free(cert);
251 hasSavedCertificate = 1;
252 }
253
254 if( pUrlData->useProxy ){
255 int rc;
256 BIO *sBio;
257 char *connStr;
258 connStr = mprintf("%s:%d", g.urlName, pUrlData->port);
259 sBio = BIO_new_connect(connStr);
260 free(connStr);
261 if( BIO_do_connect(sBio)<=0 ){
262 ssl_set_errmsg("SSL: cannot connect to proxy %s:%d (%s)",
263 pUrlData->name, pUrlData->port, ERR_reason_error_string(ERR_get_error()));
264 ssl_close();
265 return 1;
266 }
267 rc = establish_proxy_tunnel(pUrlData, sBio);
268 if( rc<200||rc>299 ){
269 ssl_set_errmsg("SSL: proxy connect failed with HTTP status code %d", rc);
270 return 1;
271 }
272
273 pUrlData->path = pUrlData->proxyUrlPath;
274
275 iBio = BIO_new_ssl(sslCtx, 1);
276 BIO_push(iBio, sBio);
277 }else{
278 iBio = BIO_new_ssl_connect(sslCtx);
279 }
280 if( iBio==NULL ) {
281 ssl_set_errmsg("SSL: cannot open SSL (%s)",
282 ERR_reason_error_string(ERR_get_error()));
283 return 1;
284 }
285 BIO_get_ssl(iBio, &ssl);
286
287 #if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT)
288 if( !SSL_set_tlsext_host_name(ssl, pUrlData->useProxy?pUrlData->hostname:pUrlData->name) ){
289 fossil_warning("WARNING: failed to set server name indication (SNI), "
290 "continuing without it.\n");
291 }
292 #endif
293
294 SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
295
296 if( !pUrlData->useProxy ){
297 BIO_set_conn_hostname(iBio, pUrlData->name);
298 BIO_set_conn_int_port(iBio, &pUrlData->port);
299 if( BIO_do_connect(iBio)<=0 ){
300 ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)",
301 pUrlData->name, pUrlData->port, ERR_reason_error_string(ERR_get_error()));
302 ssl_close();
303 return 1;
304 }
 
 
 
 
 
305 }
306
307 if( BIO_do_handshake(iBio)<=0 ) {
308 ssl_set_errmsg("Error establishing SSL connection %s:%d (%s)",
309 pUrlData->useProxy?pUrlData->hostname:pUrlData->name,
310 pUrlData->useProxy?pUrlData->proxyOrigPort:pUrlData->port,
311 ERR_reason_error_string(ERR_get_error()));
312 ssl_close();
313 return 1;
314 }
315 /* Check if certificate is valid */
@@ -283,11 +357,11 @@
357 " certificates list\n\n"
358 "If you are not expecting this message, answer no and "
359 "contact your server\nadministrator.\n\n"
360 "Accept certificate for host %s (a=always/y/N)? ",
361 X509_verify_cert_error_string(e), desc, warning,
362 pUrlData->useProxy?pUrlData->hostname:pUrlData->name);
363 BIO_free(mem);
364
365 prompt_user(prompt, &ans);
366 free(prompt);
367 cReply = blob_str(&ans)[0];
@@ -333,14 +407,14 @@
407
408 mem = BIO_new(BIO_s_mem());
409 PEM_write_bio_X509(mem, cert);
410 BIO_write(mem, "", 1); /* nul-terminate mem buffer */
411 BIO_get_mem_data(mem, &zCert);
412 zHost = mprintf("cert:%s", pUrlData->useProxy?pUrlData->hostname:pUrlData->name);
413 db_set(zHost, zCert, 1);
414 free(zHost);
415 zHost = mprintf("trusted:%s", pUrlData->useProxy?pUrlData->hostname:pUrlData->name);
416 db_set_int(zHost, trusted, 1);
417 free(zHost);
418 BIO_free(mem);
419 }
420
@@ -351,18 +425,18 @@
425 X509 *ssl_get_certificate(UrlData *pUrlData, int *pTrusted){
426 char *zHost, *zCert;
427 BIO *mem;
428 X509 *cert;
429
430 zHost = mprintf("cert:%s", pUrlData->useProxy?pUrlData->hostname:pUrlData->name);
431 zCert = db_get(zHost, NULL);
432 free(zHost);
433 if ( zCert==NULL )
434 return NULL;
435
436 if ( pTrusted!=0 ){
437 zHost = mprintf("trusted:%s", pUrlData->useProxy?pUrlData->hostname:pUrlData->name);
438 *pTrusted = db_get_int(zHost, 0);
439 free(zHost);
440 }
441
442 mem = BIO_new(BIO_s_mem());
443
+3 -1
--- src/main.c
+++ src/main.c
@@ -189,11 +189,13 @@
189189
char *urlPasswd; /* Password for http: */
190190
char *urlCanonical; /* Canonical representation of the URL */
191191
char *urlProxyAuth; /* Proxy-Authorizer: string */
192192
char *urlFossil; /* The fossil query parameter on ssh: */
193193
unsigned urlFlags; /* Boolean flags controlling URL processing */
194
-
194
+ int useProxy; /* Used to remember that a proxy is in use */
195
+ char *proxyUrlPath;
196
+ int proxyOrigPort; /* Tunneled port number for https through proxy */
195197
const char *zLogin; /* Login name. "" if not logged in. */
196198
const char *zSSLIdentity; /* Value of --ssl-identity option, filename of
197199
** SSL client identity */
198200
int useLocalauth; /* No login required if from 127.0.0.1 */
199201
int noPswd; /* Logged in without password (on 127.0.0.1) */
200202
--- src/main.c
+++ src/main.c
@@ -189,11 +189,13 @@
189 char *urlPasswd; /* Password for http: */
190 char *urlCanonical; /* Canonical representation of the URL */
191 char *urlProxyAuth; /* Proxy-Authorizer: string */
192 char *urlFossil; /* The fossil query parameter on ssh: */
193 unsigned urlFlags; /* Boolean flags controlling URL processing */
194
 
 
195 const char *zLogin; /* Login name. "" if not logged in. */
196 const char *zSSLIdentity; /* Value of --ssl-identity option, filename of
197 ** SSL client identity */
198 int useLocalauth; /* No login required if from 127.0.0.1 */
199 int noPswd; /* Logged in without password (on 127.0.0.1) */
200
--- src/main.c
+++ src/main.c
@@ -189,11 +189,13 @@
189 char *urlPasswd; /* Password for http: */
190 char *urlCanonical; /* Canonical representation of the URL */
191 char *urlProxyAuth; /* Proxy-Authorizer: string */
192 char *urlFossil; /* The fossil query parameter on ssh: */
193 unsigned urlFlags; /* Boolean flags controlling URL processing */
194 int useProxy; /* Used to remember that a proxy is in use */
195 char *proxyUrlPath;
196 int proxyOrigPort; /* Tunneled port number for https through proxy */
197 const char *zLogin; /* Login name. "" if not logged in. */
198 const char *zSSLIdentity; /* Value of --ssl-identity option, filename of
199 ** SSL client identity */
200 int useLocalauth; /* No login required if from 127.0.0.1 */
201 int noPswd; /* Logged in without password (on 127.0.0.1) */
202
+11
--- src/url.c
+++ src/url.c
@@ -62,10 +62,13 @@
6262
char *passwd; /* Password for http: */
6363
char *canonical; /* Canonical representation of the URL */
6464
char *proxyAuth; /* Proxy-Authorizer: string */
6565
char *fossil; /* The fossil query parameter on ssh: */
6666
unsigned flags; /* Boolean flags controlling URL processing */
67
+ int useProxy; /* Used to remember that a proxy is in use */
68
+ char *proxyUrlPath;
69
+ int proxyOrigPort; /* Tunneled port number for https through proxy */
6770
};
6871
#endif /* INTERFACE */
6972
7073
7174
/*
@@ -120,10 +123,11 @@
120123
char *zLogin;
121124
char *zExe;
122125
char cQuerySep = '?';
123126
124127
pUrlData->isFile = 0;
128
+ pUrlData->useProxy = 0;
125129
if( zUrl[4]=='s' ){
126130
pUrlData->isHttps = 1;
127131
pUrlData->protocol = "https";
128132
pUrlData->dfltPort = 443;
129133
iStart = 8;
@@ -384,12 +388,15 @@
384388
}
385389
if( zProxy && zProxy[0] && !is_false(zProxy)
386390
&& !g.urlIsSsh && !g.urlIsFile ){
387391
char *zOriginalUrl = g.urlCanonical;
388392
char *zOriginalHost = g.urlHostname;
393
+ int fOriginalIsHttps = g.urlIsHttps;
389394
char *zOriginalUser = g.urlUser;
390395
char *zOriginalPasswd = g.urlPasswd;
396
+ char *zOriginalUrlPath = g.urlPath;
397
+ int iOriginalPort = g.urlPort;
391398
unsigned uOriginalFlags = g.urlFlags;
392399
g.urlUser = 0;
393400
g.urlPasswd = "";
394401
url_parse(zProxy, 0);
395402
if( zMsg ) fossil_print("%s%s\n", zMsg, g.urlCanonical);
@@ -401,10 +408,14 @@
401408
g.urlProxyAuth = mprintf("Basic %z", zCredentials2);
402409
free(zCredentials1);
403410
}
404411
g.urlUser = zOriginalUser;
405412
g.urlPasswd = zOriginalPasswd;
413
+ g.urlIsHttps = fOriginalIsHttps;
414
+ g.useProxy = 1;
415
+ g.proxyUrlPath = zOriginalUrlPath;
416
+ g.proxyOrigPort = iOriginalPort;
406417
g.urlFlags = uOriginalFlags;
407418
}
408419
}
409420
410421
#if INTERFACE
411422
--- src/url.c
+++ src/url.c
@@ -62,10 +62,13 @@
62 char *passwd; /* Password for http: */
63 char *canonical; /* Canonical representation of the URL */
64 char *proxyAuth; /* Proxy-Authorizer: string */
65 char *fossil; /* The fossil query parameter on ssh: */
66 unsigned flags; /* Boolean flags controlling URL processing */
 
 
 
67 };
68 #endif /* INTERFACE */
69
70
71 /*
@@ -120,10 +123,11 @@
120 char *zLogin;
121 char *zExe;
122 char cQuerySep = '?';
123
124 pUrlData->isFile = 0;
 
125 if( zUrl[4]=='s' ){
126 pUrlData->isHttps = 1;
127 pUrlData->protocol = "https";
128 pUrlData->dfltPort = 443;
129 iStart = 8;
@@ -384,12 +388,15 @@
384 }
385 if( zProxy && zProxy[0] && !is_false(zProxy)
386 && !g.urlIsSsh && !g.urlIsFile ){
387 char *zOriginalUrl = g.urlCanonical;
388 char *zOriginalHost = g.urlHostname;
 
389 char *zOriginalUser = g.urlUser;
390 char *zOriginalPasswd = g.urlPasswd;
 
 
391 unsigned uOriginalFlags = g.urlFlags;
392 g.urlUser = 0;
393 g.urlPasswd = "";
394 url_parse(zProxy, 0);
395 if( zMsg ) fossil_print("%s%s\n", zMsg, g.urlCanonical);
@@ -401,10 +408,14 @@
401 g.urlProxyAuth = mprintf("Basic %z", zCredentials2);
402 free(zCredentials1);
403 }
404 g.urlUser = zOriginalUser;
405 g.urlPasswd = zOriginalPasswd;
 
 
 
 
406 g.urlFlags = uOriginalFlags;
407 }
408 }
409
410 #if INTERFACE
411
--- src/url.c
+++ src/url.c
@@ -62,10 +62,13 @@
62 char *passwd; /* Password for http: */
63 char *canonical; /* Canonical representation of the URL */
64 char *proxyAuth; /* Proxy-Authorizer: string */
65 char *fossil; /* The fossil query parameter on ssh: */
66 unsigned flags; /* Boolean flags controlling URL processing */
67 int useProxy; /* Used to remember that a proxy is in use */
68 char *proxyUrlPath;
69 int proxyOrigPort; /* Tunneled port number for https through proxy */
70 };
71 #endif /* INTERFACE */
72
73
74 /*
@@ -120,10 +123,11 @@
123 char *zLogin;
124 char *zExe;
125 char cQuerySep = '?';
126
127 pUrlData->isFile = 0;
128 pUrlData->useProxy = 0;
129 if( zUrl[4]=='s' ){
130 pUrlData->isHttps = 1;
131 pUrlData->protocol = "https";
132 pUrlData->dfltPort = 443;
133 iStart = 8;
@@ -384,12 +388,15 @@
388 }
389 if( zProxy && zProxy[0] && !is_false(zProxy)
390 && !g.urlIsSsh && !g.urlIsFile ){
391 char *zOriginalUrl = g.urlCanonical;
392 char *zOriginalHost = g.urlHostname;
393 int fOriginalIsHttps = g.urlIsHttps;
394 char *zOriginalUser = g.urlUser;
395 char *zOriginalPasswd = g.urlPasswd;
396 char *zOriginalUrlPath = g.urlPath;
397 int iOriginalPort = g.urlPort;
398 unsigned uOriginalFlags = g.urlFlags;
399 g.urlUser = 0;
400 g.urlPasswd = "";
401 url_parse(zProxy, 0);
402 if( zMsg ) fossil_print("%s%s\n", zMsg, g.urlCanonical);
@@ -401,10 +408,14 @@
408 g.urlProxyAuth = mprintf("Basic %z", zCredentials2);
409 free(zCredentials1);
410 }
411 g.urlUser = zOriginalUser;
412 g.urlPasswd = zOriginalPasswd;
413 g.urlIsHttps = fOriginalIsHttps;
414 g.useProxy = 1;
415 g.proxyUrlPath = zOriginalUrlPath;
416 g.proxyOrigPort = iOriginalPort;
417 g.urlFlags = uOriginalFlags;
418 }
419 }
420
421 #if INTERFACE
422
--- www/changes.wiki
+++ www/changes.wiki
@@ -6,10 +6,11 @@
66
* Honor timezones in imports from git.
77
* The [/reports] page now requires Read ("o") permissions. The "byweek"
88
report now properly propagates the selected year through the event type
99
filter links.
1010
* The [/help/info | info command] now shows leaf status of the checkout.
11
+ * Add support for tunneling https through a http proxy (Ticket [e854101c4f]).
1112
1213
<h2>Changes For Version 1.28 (2014-01-27)</h2>
1314
* Enhance [/help?cmd=/reports | /reports] to support event type filtering.
1415
* When cloning a repository, the user name passed via the URL (if any)
1516
is now used as the default local admin user's name.
1617
--- www/changes.wiki
+++ www/changes.wiki
@@ -6,10 +6,11 @@
6 * Honor timezones in imports from git.
7 * The [/reports] page now requires Read ("o") permissions. The "byweek"
8 report now properly propagates the selected year through the event type
9 filter links.
10 * The [/help/info | info command] now shows leaf status of the checkout.
 
11
12 <h2>Changes For Version 1.28 (2014-01-27)</h2>
13 * Enhance [/help?cmd=/reports | /reports] to support event type filtering.
14 * When cloning a repository, the user name passed via the URL (if any)
15 is now used as the default local admin user's name.
16
--- www/changes.wiki
+++ www/changes.wiki
@@ -6,10 +6,11 @@
6 * Honor timezones in imports from git.
7 * The [/reports] page now requires Read ("o") permissions. The "byweek"
8 report now properly propagates the selected year through the event type
9 filter links.
10 * The [/help/info | info command] now shows leaf status of the checkout.
11 * Add support for tunneling https through a http proxy (Ticket [e854101c4f]).
12
13 <h2>Changes For Version 1.28 (2014-01-27)</h2>
14 * Enhance [/help?cmd=/reports | /reports] to support event type filtering.
15 * When cloning a repository, the user name passed via the URL (if any)
16 is now used as the default local admin user's name.
17
--- www/changes.wiki
+++ www/changes.wiki
@@ -6,10 +6,11 @@
66
* Honor timezones in imports from git.
77
* The [/reports] page now requires Read ("o") permissions. The "byweek"
88
report now properly propagates the selected year through the event type
99
filter links.
1010
* The [/help/info | info command] now shows leaf status of the checkout.
11
+ * Add support for tunneling https through a http proxy (Ticket [e854101c4f]).
1112
1213
<h2>Changes For Version 1.28 (2014-01-27)</h2>
1314
* Enhance [/help?cmd=/reports | /reports] to support event type filtering.
1415
* When cloning a repository, the user name passed via the URL (if any)
1516
is now used as the default local admin user's name.
1617
--- www/changes.wiki
+++ www/changes.wiki
@@ -6,10 +6,11 @@
6 * Honor timezones in imports from git.
7 * The [/reports] page now requires Read ("o") permissions. The "byweek"
8 report now properly propagates the selected year through the event type
9 filter links.
10 * The [/help/info | info command] now shows leaf status of the checkout.
 
11
12 <h2>Changes For Version 1.28 (2014-01-27)</h2>
13 * Enhance [/help?cmd=/reports | /reports] to support event type filtering.
14 * When cloning a repository, the user name passed via the URL (if any)
15 is now used as the default local admin user's name.
16
--- www/changes.wiki
+++ www/changes.wiki
@@ -6,10 +6,11 @@
6 * Honor timezones in imports from git.
7 * The [/reports] page now requires Read ("o") permissions. The "byweek"
8 report now properly propagates the selected year through the event type
9 filter links.
10 * The [/help/info | info command] now shows leaf status of the checkout.
11 * Add support for tunneling https through a http proxy (Ticket [e854101c4f]).
12
13 <h2>Changes For Version 1.28 (2014-01-27)</h2>
14 * Enhance [/help?cmd=/reports | /reports] to support event type filtering.
15 * When cloning a repository, the user name passed via the URL (if any)
16 is now used as the default local admin user's name.
17

Keyboard Shortcuts

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