From 38d2afcefb3519f7e6a3b7c3afda4f5bfb67bde9 Mon Sep 17 00:00:00 2001
From: Ben Greear <greearb@candelatech.com>
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 <greearb@candelatech.com>
---
 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 <curl/mprintf.h>
@@ -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 */