diff -r -U 8 curl-7.15.1-orig/include/curl/curl.h curl-7.15.1/include/curl/curl.h --- curl-7.15.1-orig/include/curl/curl.h 2005-12-04 11:04:44.000000000 -0800 +++ curl-7.15.1/include/curl/curl.h 2006-02-05 18:26:10.000000000 -0800 @@ -907,16 +907,19 @@ response. Typically used for FTP-SSL purposes but is not restricted to that. libcurl will then instead use the same IP address it used for the control connection. */ CINIT(FTP_SKIP_PASV_IP, LONG, 137), /* Select "file method" to use when doing FTP */ CINIT(FTP_FILEMETHOD, LONG, 138), + /* no transfer, set up connection and let application use the socket */ + CINIT(CONNECT_ONLY, LONG, 139), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; /* Below here follows defines for the CURLOPT_IPRESOLVE option. If a host name resolves addresses using more than one IP protocol version, this option might be handy to force libcurl to use a specific IP version. */ #define CURL_IPRESOLVE_WHATEVER 0 /* default, resolves addresses to all IP versions that your system allows */ @@ -1261,19 +1264,20 @@ CURLINFO_PRIVATE = CURLINFO_STRING + 21, CURLINFO_HTTP_CONNECTCODE = CURLINFO_LONG + 22, CURLINFO_HTTPAUTH_AVAIL = CURLINFO_LONG + 23, CURLINFO_PROXYAUTH_AVAIL = CURLINFO_LONG + 24, CURLINFO_OS_ERRNO = CURLINFO_LONG + 25, CURLINFO_NUM_CONNECTS = CURLINFO_LONG + 26, CURLINFO_SSL_ENGINES = CURLINFO_SLIST + 27, CURLINFO_COOKIELIST = CURLINFO_SLIST + 28, + CURLINFO_SOCKET = CURLINFO_LONG + 29, /* Fill in new entries below here! */ - CURLINFO_LASTONE = 28 + CURLINFO_LASTONE = 29 } CURLINFO; /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as CURLINFO_HTTP_CODE */ #define CURLINFO_HTTP_CODE CURLINFO_RESPONSE_CODE typedef enum { CURLCLOSEPOLICY_NONE, /* first, never use this */ diff -r -U 8 curl-7.15.1-orig/lib/easy.c curl-7.15.1/lib/easy.c --- curl-7.15.1-orig/lib/easy.c 2005-07-17 15:46:02.000000000 -0700 +++ curl-7.15.1/lib/easy.c 2006-02-05 18:04:36.000000000 -0800 @@ -521,16 +521,18 @@ if(!outcurl->state.connects) { break; } memset(outcurl->state.connects, 0, sizeof(struct connectdata *)*outcurl->state.numconnects); + outcurl->state.lastconnect = -1; + outcurl->progress.flags = data->progress.flags; outcurl->progress.callback = data->progress.callback; #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) if(data->cookies) { /* If cookies are enabled in the parent handle, we enable them in the clone as well! */ outcurl->cookies = Curl_cookie_init(data, diff -r -U 8 curl-7.15.1-orig/lib/ftp.c curl-7.15.1/lib/ftp.c --- curl-7.15.1-orig/lib/ftp.c 2005-12-05 15:10:48.000000000 -0800 +++ curl-7.15.1/lib/ftp.c 2006-02-05 19:22:36.000000000 -0800 @@ -1664,17 +1664,17 @@ * connect to connect and we should not be "hanging" here waiting. */ if(data->set.verbose) /* this just dumps information about this second connection */ ftp_pasv_verbose(conn, conninfo, newhost, connectport); #ifndef CURL_DISABLE_HTTP - if(conn->bits.tunnel_proxy) { + if(conn->bits.tunnel_proxy && conn->bits.httpproxy) { /* FIX: this MUST wait for a proper connect first if 'connected' is * FALSE */ /* BLOCKING */ /* We want "seamless" FTP operations through HTTP proxy tunnel */ result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, newhost, newport); if(CURLE_OK != result) return result; @@ -2743,17 +2743,17 @@ ftp->user = conn->user; ftp->passwd = conn->passwd; if (isBadFtpString(ftp->user) || isBadFtpString(ftp->passwd)) return CURLE_URL_MALFORMAT; ftp->response_time = 3600; /* set default response time-out */ #ifndef CURL_DISABLE_HTTP - if (conn->bits.tunnel_proxy) { + if (conn->bits.tunnel_proxy && conn->bits.httpproxy) { /* BLOCKING */ /* We want "seamless" FTP operations through HTTP proxy tunnel */ result = Curl_proxyCONNECT(conn, FIRSTSOCKET, conn->host.name, conn->remote_port); if(CURLE_OK != result) return result; } #endif /* CURL_DISABLE_HTTP */ diff -r -U 8 curl-7.15.1-orig/lib/getinfo.c curl-7.15.1/lib/getinfo.c --- curl-7.15.1-orig/lib/getinfo.c 2005-07-28 01:17:16.000000000 -0700 +++ curl-7.15.1/lib/getinfo.c 2006-02-05 18:59:10.000000000 -0800 @@ -182,13 +182,21 @@ *param_longp = data->info.numconnects; break; case CURLINFO_SSL_ENGINES: *param_slistp = Curl_ssl_engines_list(data); break; case CURLINFO_COOKIELIST: *param_slistp = Curl_cookie_list(data); break; + case CURLINFO_SOCKET: + if((data->state.lastconnect != -1) && + (data->state.connects[data->state.lastconnect] != NULL)) + *param_longp = data->state.connects[data->state.lastconnect]-> + sock[FIRSTSOCKET]; + else + *param_longp = INVALID_SOCKET; + break; default: return CURLE_BAD_FUNCTION_ARGUMENT; } return CURLE_OK; } diff -r -U 8 curl-7.15.1-orig/lib/http.c curl-7.15.1/lib/http.c --- curl-7.15.1-orig/lib/http.c 2005-10-20 23:07:34.000000000 -0700 +++ curl-7.15.1/lib/http.c 2006-02-05 19:23:08.000000000 -0800 @@ -1351,17 +1351,17 @@ data=conn->data; /* If we are not using a proxy and we want a secure connection, perform SSL * initialization & connection now. If using a proxy with https, then we * must tell the proxy to CONNECT to the host we want to talk to. Only * after the connect has occured, can we start talking SSL */ - if(conn->bits.tunnel_proxy) { + if(conn->bits.tunnel_proxy && conn->bits.httpproxy) { /* either SSL over proxy, or explicitly asked for */ result = Curl_proxyCONNECT(conn, FIRSTSOCKET, conn->host.name, conn->remote_port); if(CURLE_OK != result) return result; } diff -r -U 8 curl-7.15.1-orig/lib/multi.c curl-7.15.1/lib/multi.c --- curl-7.15.1-orig/lib/multi.c 2005-03-08 23:22:00.000000000 -0800 +++ curl-7.15.1/lib/multi.c 2006-02-05 17:43:48.000000000 -0800 @@ -517,51 +517,60 @@ Curl_posttransfer(easy->easy_handle); Curl_done(&easy->easy_conn, easy->result); Curl_disconnect(easy->easy_conn); /* close the connection */ easy->easy_conn = NULL; /* no more connection */ } break; case CURLM_STATE_DO: - /* Perform the protocol's DO action */ - easy->result = Curl_do(&easy->easy_conn, &dophase_done); + if(easy->easy_handle->set.connect_only) { + /* keep connection open for application to use the socket */ + easy->easy_conn->bits.close = FALSE; + multistate(easy, CURLM_STATE_DONE); + easy->result = CURLE_OK; + result = CURLM_OK; + } + else { + /* Perform the protocol's DO action */ + easy->result = Curl_do(&easy->easy_conn, &dophase_done); - if(CURLE_OK == easy->result) { + if(CURLE_OK == easy->result) { - if(!dophase_done) { - /* DO was not completed in one function call, we must continue - DOING... */ - multistate(easy, CURLM_STATE_DOING); - result = CURLM_OK; - } + if(!dophase_done) { + /* DO was not completed in one function call, we must continue + DOING... */ + multistate(easy, CURLM_STATE_DOING); + result = CURLM_OK; + } - /* after DO, go PERFORM... or DO_MORE */ - else if(easy->easy_conn->bits.do_more) { - /* we're supposed to do more, but we need to sit down, relax - and wait a little while first */ - multistate(easy, CURLM_STATE_DO_MORE); - result = CURLM_OK; + /* after DO, go PERFORM... or DO_MORE */ + else if(easy->easy_conn->bits.do_more) { + /* we're supposed to do more, but we need to sit down, relax + and wait a little while first */ + multistate(easy, CURLM_STATE_DO_MORE); + result = CURLM_OK; + } + else { + /* we're done with the DO, now PERFORM */ + easy->result = Curl_readwrite_init(easy->easy_conn); + if(CURLE_OK == easy->result) { + multistate(easy, CURLM_STATE_PERFORM); + result = CURLM_CALL_MULTI_PERFORM; + } + } } else { - /* we're done with the DO, now PERFORM */ - easy->result = Curl_readwrite_init(easy->easy_conn); - if(CURLE_OK == easy->result) { - multistate(easy, CURLM_STATE_PERFORM); - result = CURLM_CALL_MULTI_PERFORM; - } + /* failure detected */ + Curl_posttransfer(easy->easy_handle); + Curl_done(&easy->easy_conn, easy->result); + Curl_disconnect(easy->easy_conn); /* close the connection */ + easy->easy_conn = NULL; /* no more connection */ } } - else { - /* failure detected */ - Curl_posttransfer(easy->easy_handle); - Curl_done(&easy->easy_conn, easy->result); - Curl_disconnect(easy->easy_conn); /* close the connection */ - easy->easy_conn = NULL; /* no more connection */ - } break; case CURLM_STATE_DOING: /* we continue DOING until the DO phase is complete */ easy->result = Curl_protocol_doing(easy->easy_conn, &dophase_done); if(CURLE_OK == easy->result) { if(dophase_done) { /* after DO, go PERFORM... or DO_MORE */ diff -r -U 8 curl-7.15.1-orig/lib/transfer.c curl-7.15.1/lib/transfer.c --- curl-7.15.1-orig/lib/transfer.c 2005-11-24 11:22:48.000000000 -0800 +++ curl-7.15.1/lib/transfer.c 2006-02-05 17:18:00.000000000 -0800 @@ -2150,17 +2150,22 @@ if(res == CURLE_OK) { if (data->set.source_url) /* 3rd party transfer */ res = Curl_second_connect(conn); else conn->sec_conn = NULL; } - if(res == CURLE_OK) { + if(data->set.connect_only) { + /* keep connection open for application to use the socket */ + conn->bits.close = FALSE; + res = Curl_done(&conn, CURLE_OK); + } + else if(res == CURLE_OK) { bool do_done; res = Curl_do(&conn, &do_done); /* for non 3rd party transfer only */ if(res == CURLE_OK && !data->set.source_url) { res = Transfer(conn); /* now fetch that URL please */ if(res == CURLE_OK) { retry = Curl_retry_request(conn, &newurl); diff -r -U 8 curl-7.15.1-orig/lib/url.c curl-7.15.1/lib/url.c --- curl-7.15.1-orig/lib/url.c 2005-12-07 00:05:52.000000000 -0800 +++ curl-7.15.1/lib/url.c 2006-02-05 18:41:58.000000000 -0800 @@ -348,16 +348,19 @@ malloc(sizeof(struct connectdata *) * data->state.numconnects); if(!data->state.connects) res = CURLE_OUT_OF_MEMORY; else memset(data->state.connects, 0, sizeof(struct connectdata *)*data->state.numconnects); + /* most recent connection is not yet defined */ + data->state.lastconnect = -1; + /* * libcurl 7.10 introduced SSL verification *by default*! This needs to be * switched off unless wanted. */ data->set.ssl.verifypeer = TRUE; data->set.ssl.verifyhost = 2; #ifdef CURL_CA_BUNDLE /* This is our prefered CA cert bundle since install time */ @@ -428,16 +431,20 @@ long i; if(newconnects < data->state.numconnects) { /* Since this number is *decreased* from the existing number, we must close the possibly open connections that live on the indexes that are being removed! */ for(i=newconnects; i< data->state.numconnects; i++) Curl_disconnect(data->state.connects[i]); + + /* If the most recent connection is no longer valid, mark it invalid. */ + if(data->state.lastconnect <= newconnects) + data->state.lastconnect = -1; } if(newconnects) { newptr= (struct connectdata **) realloc(data->state.connects, sizeof(struct connectdata *) * newconnects); if(!newptr) /* we closed a few connections in vain, but so what? */ return CURLE_OUT_OF_MEMORY; @@ -449,18 +456,19 @@ data->state.connects = newptr; data->state.numconnects = newconnects; } else { /* zero makes NO cache at all */ if(data->state.connects) free(data->state.connects); - data->state.connects=NULL; - data->state.numconnects=0; + data->state.connects = NULL; + data->state.numconnects = 0; + data->state.lastconnect = -1; } } break; case CURLOPT_FORBID_REUSE: /* * When this transfer is done, it must not be left to be reused by a * subsequent transfer but shall be closed immediately. */ @@ -1455,16 +1463,23 @@ case CURLOPT_FTP_ACCOUNT: data->set.ftp_account = va_arg(param, char *); break; case CURLOPT_IGNORE_CONTENT_LENGTH: data->set.ignorecl = va_arg(param, long)?TRUE:FALSE; break; + case CURLOPT_CONNECT_ONLY: + /* + * No data transfer, set up connection and let application use the socket + */ + data->set.connect_only = va_arg(param, long)?TRUE:FALSE; + break; + default: /* unknown tag and its companion, just ignore: */ result = CURLE_FAILED_INIT; /* correct this */ break; } return result; } @@ -3815,20 +3830,24 @@ *connp = NULL; /* to make the caller of this function better detect that this was actually killed here */ /* If we had an error already, make sure we return that one. But if we got a new error, return that. */ if(!result && res2) result = res2; } - else + else { + /* remember the most recently used connection */ + data->state.lastconnect = conn->connectindex; + infof(data, "Connection #%ld to host %s left intact\n", conn->connectindex, conn->bits.httpproxy?conn->proxy.dispname:conn->host.dispname); + } return result; } CURLcode Curl_do(struct connectdata **connp, bool *done) { CURLcode result=CURLE_OK; struct connectdata *conn = *connp; diff -r -U 8 curl-7.15.1-orig/lib/urldata.h curl-7.15.1/lib/urldata.h --- curl-7.15.1-orig/lib/urldata.h 2005-11-29 00:06:00.000000000 -0800 +++ curl-7.15.1/lib/urldata.h 2006-02-05 18:33:14.000000000 -0800 @@ -855,16 +855,17 @@ /* buffers to store authentication data in, as parsed from input options */ struct timeval keeps_speed; /* for the progress meter really */ /* 'connects' will be an allocated array with pointers. If the pointer is set, it holds an allocated connection. */ struct connectdata **connects; long numconnects; /* size of the 'connects' array */ + int lastconnect; /* index of most recent connect or -1 if undefined */ char *headerbuff; /* allocated buffer to store headers in */ size_t headersize; /* size of the allocation */ char buffer[BUFSIZE+1]; /* download buffer */ char uploadbuffer[BUFSIZE+1]; /* upload buffer */ curl_off_t current_speed; /* the ProgressShow() funcion sets this, bytes / second */ @@ -1080,16 +1081,17 @@ curl_ftpssl ftp_ssl; /* if AUTH TLS is to be attempted etc */ curl_ftpauth ftpsslauth; /* what AUTH XXX to be attempted */ bool no_signal; /* do not use any signal/alarm handler */ bool global_dns_cache; /* subject for future removal */ bool tcp_nodelay; /* whether to enable TCP_NODELAY or not */ bool ignorecl; /* ignore content length */ bool ftp_skip_ip; /* skip the IP address the FTP server passes on to us */ + bool connect_only; /* make connection, let application use the socket */ }; /* * In August 2001, this struct was redesigned and is since stricter than * before. The 'connectdata' struct MUST have all the connection oriented * stuff as we may now have several simultaneous connections and connection * structs in memory. *