From 38d2afcefb3519f7e6a3b7c3afda4f5bfb67bde9 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Thu, 29 Apr 2010 00:49:04 +0200 Subject: [PATCH] telnet: Allow programatic use of telnet. The main change is to allow input from user-specified methods, when they are specified with CURLOPT_READFUNCTION. All calls to fflush(stdout) in telnet.c were removed, which makes using 'curl telnet://foo.com' painful since prompts and other data are not always returned to the user promptly. Use 'curl --no-buffer telnet://foo.com' instead. In general, the user should have their CURLOPT_WRITEFUNCTION do a fflush for interactive use. Also fix assumption that reading from stdin never returns < 0. Old code could crash in that case. Call progress functions in telnet main loop. Signed-off-by: Ben Greear --- lib/telnet.c | 96 ++++++++++++++++++++++++++++++++++++--------------- lib/url.c | 14 ++++++-- lib/urldata.h | 2 ++ src/main.c | 4 ++- 4 files changed, 86 insertions(+), 30 deletions(-) diff --git a/lib/telnet.c b/lib/telnet.c index 9409f4955..e7f05eb91 100644 --- a/lib/telnet.c +++ b/lib/telnet.c @@ -67,6 +67,7 @@ #include "sendf.h" #include "telnet.h" #include "connect.h" +#include "progress.h" #define _MPRINTF_REPLACE /* use our functions only */ #include @@ -962,16 +963,16 @@ CURLcode telrcv(struct connectdata *conn, struct SessionHandle *data = conn->data; struct TELNET *tn = (struct TELNET *)data->state.proto.telnet; -#define startskipping() \ - if(startwrite >= 0) { \ - result = Curl_client_write(conn, \ - CLIENTWRITE_BODY, \ - (char *)&inbuf[startwrite], \ - in-startwrite); \ - if(result != CURLE_OK) \ - return result; \ - } \ - startwrite = -1 +#define startskipping() \ + if(startwrite >= 0) { \ + result = Curl_client_write(conn, \ + CLIENTWRITE_BODY, \ + (char *)&inbuf[startwrite], \ + in-startwrite); \ + if(result != CURLE_OK) \ + return result; \ + } \ + startwrite = -1 #define writebyte() \ if(startwrite < 0) \ @@ -1206,6 +1207,7 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) #else int interval_ms; struct pollfd pfd[2]; + int poll_cnt; #endif int ret; ssize_t nread; @@ -1213,6 +1215,8 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) bool keepon = TRUE; char *buf = data->state.buffer; struct TELNET *tn; + curl_off_t total_dl = 0; + curl_off_t total_ul = 0; *done = TRUE; /* unconditionally */ @@ -1402,8 +1406,6 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) break; } - fflush(stdout); - /* Negotiate if the peer has started negotiating, otherwise don't. We don't want to speak telnet with non-telnet servers, like POP or SMTP. */ @@ -1446,27 +1448,28 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) #else pfd[0].fd = sockfd; pfd[0].events = POLLIN; - pfd[1].fd = 0; - pfd[1].events = POLLIN; - interval_ms = 1 * 1000; + + if (data->set.is_fread_set) { + poll_cnt = 1; + interval_ms = 100; /* poll user-supplied read function */ + } + else { + pfd[1].fd = 0; + pfd[1].events = POLLIN; + poll_cnt = 2; + interval_ms = 1 * 1000; + } while(keepon) { - switch (Curl_poll(pfd, 2, interval_ms)) { + switch (Curl_poll(pfd, poll_cnt, interval_ms)) { case -1: /* error, stop reading */ keepon = FALSE; continue; case 0: /* timeout */ - break; + pfd[0].revents = 0; + pfd[1].revents = 0; + /* fall through */ default: /* read! */ - if(pfd[1].revents & POLLIN) { /* read from stdin */ - nread = read(0, buf, 255); - code = send_telnet_data(conn, buf, nread); - if(code) { - keepon = FALSE; - break; - } - } - if(pfd[0].revents & POLLIN) { /* read data from network */ ret = Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread); @@ -1486,6 +1489,8 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) break; } + total_dl += nread; + Curl_pgrsSetDownloadCounter(data, total_dl); code = telrcv(conn, (unsigned char *)buf, nread); if(code) { keepon = FALSE; @@ -1500,7 +1505,39 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) tn->already_negotiated = 1; } } - } + + nread = 0; + if (poll_cnt == 2) { + if(pfd[1].revents & POLLIN) { /* read from stdin */ + nread = read(0, buf, BUFSIZE - 1); + } + } + else { + /* read from user-supplied method */ + nread = (int)conn->fread_func(buf, 1, BUFSIZE - 1, conn->fread_in); + if (nread == CURL_READFUNC_ABORT) { + keepon = FALSE; + break; + } + if (nread == CURL_READFUNC_PAUSE) + break; + } + + if (nread > 0) { + code = send_telnet_data(conn, buf, nread); + if(code) { + keepon = FALSE; + break; + } + total_ul += nread; + Curl_pgrsSetUploadCounter(data, total_ul); + } + else if (nread < 0) + keepon = FALSE; + + break; + } /* poll switch statement */ + if(data->set.timeout) { now = Curl_tvnow(); if(Curl_tvdiff(now, conn->created) >= data->set.timeout) { @@ -1509,6 +1546,11 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) keepon = FALSE; } } + + if(Curl_pgrsUpdate(conn)) { + code = CURLE_ABORTED_BY_CALLBACK; + break; + } } #endif /* mark this as "no further transfer wanted" */ diff --git a/lib/url.c b/lib/url.c index a84e69d35..56dd5dc9e 100644 --- a/lib/url.c +++ b/lib/url.c @@ -690,6 +690,8 @@ CURLcode Curl_init_userdefined(struct UserDefined *set) /* use fread as default function to read input */ set->fread_func = (curl_read_callback)fread; + set->is_fread_set = 0; + set->is_fwrite_set = 0; set->seek_func = ZERO_NULL; set->seek_client = ZERO_NULL; @@ -1825,18 +1827,26 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, * Set data write callback */ data->set.fwrite_func = va_arg(param, curl_write_callback); - if(!data->set.fwrite_func) + if(!data->set.fwrite_func) { + data->set.is_fwrite_set = 0; /* When set to NULL, reset to our internal default function */ data->set.fwrite_func = (curl_write_callback)fwrite; + } + else + data->set.is_fwrite_set = 0; break; case CURLOPT_READFUNCTION: /* * Read data callback */ data->set.fread_func = va_arg(param, curl_read_callback); - if(!data->set.fread_func) + if(!data->set.fread_func) { + data->set.is_fread_set = 0; /* When set to NULL, reset to our internal default function */ data->set.fread_func = (curl_read_callback)fread; + } + else + data->set.is_fread_set = 1; break; case CURLOPT_SEEKFUNCTION: /* diff --git a/lib/urldata.h b/lib/urldata.h index 02233b685..42065d184 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1235,6 +1235,8 @@ struct UserDefined { curl_write_callback fwrite_header; /* function that stores headers */ curl_write_callback fwrite_rtp; /* function that stores interleaved RTP */ curl_read_callback fread_func; /* function that reads the input */ + int is_fread_set; /* boolean, has read callback been set to non-NULL? */ + int is_fwrite_set; /* boolean, has write callback been set to non-NULL? */ curl_progress_callback fprogress; /* function for progress information */ curl_debug_callback fdebug; /* function that write informational data */ curl_ioctl_callback ioctl_func; /* function for I/O control */ diff --git a/src/main.c b/src/main.c index 684869379..eff0f9a3b 100644 --- a/src/main.c +++ b/src/main.c @@ -4987,7 +4987,9 @@ operate(struct Configurable *config, int argc, argv_item_t argv[]) input.config = config; my_setopt(curl, CURLOPT_READDATA, &input); /* what call to read */ - my_setopt(curl, CURLOPT_READFUNCTION, my_fread); + if ((outfile && !curlx_strequal("-", outfile)) || + !curlx_strnequal(url, "telnet:", 7)) + my_setopt(curl, CURLOPT_READFUNCTION, my_fread); /* in 7.18.0, the CURLOPT_SEEKFUNCTION/DATA pair is taking over what CURLOPT_IOCTLFUNCTION/DATA pair previously provided for seeking */