]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
lib: reorder protocol functions to avoid forward declarations (misc)
authorViktor Szakats <commit@vsz.me>
Sun, 21 Dec 2025 15:19:52 +0000 (16:19 +0100)
committerViktor Szakats <commit@vsz.me>
Tue, 13 Jan 2026 00:31:27 +0000 (01:31 +0100)
For protocols: dict, file, gopher, tftp, http, mqtt, smb.

Move protocol hander table to the end of sources, rearrange static
functions is reverse dependency order as necessary.

Closes #20274

lib/dict.c
lib/file.c
lib/gopher.c
lib/http.c
lib/mqtt.c
lib/smb.c
lib/tftp.c

index 5839e804c27637ec8b5154df4b4d077a6b2b7ee4..54563b4e45145be86fa724ef8a3316fcc3caa20c 100644 (file)
 #define DICT_DEFINE3 "/LOOKUP:"
 
 
-/*
- * Forward declarations.
- */
-
-static CURLcode dict_do(struct Curl_easy *data, bool *done);
-
-/*
- * DICT protocol handler.
- */
-
-const struct Curl_handler Curl_handler_dict = {
-  "dict",                               /* scheme */
-  ZERO_NULL,                            /* setup_connection */
-  dict_do,                              /* do_it */
-  ZERO_NULL,                            /* done */
-  ZERO_NULL,                            /* do_more */
-  ZERO_NULL,                            /* connect_it */
-  ZERO_NULL,                            /* connecting */
-  ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_pollset */
-  ZERO_NULL,                            /* doing_pollset */
-  ZERO_NULL,                            /* domore_pollset */
-  ZERO_NULL,                            /* perform_pollset */
-  ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* write_resp */
-  ZERO_NULL,                            /* write_resp_hd */
-  ZERO_NULL,                            /* connection_check */
-  ZERO_NULL,                            /* attach connection */
-  ZERO_NULL,                            /* follow */
-  PORT_DICT,                            /* defport */
-  CURLPROTO_DICT,                       /* protocol */
-  CURLPROTO_DICT,                       /* family */
-  PROTOPT_NONE | PROTOPT_NOURLQUERY     /* flags */
-};
-
 #define DYN_DICT_WORD 10000
 static char *unescape_word(const char *input)
 {
@@ -306,4 +271,33 @@ error:
   curlx_free(path);
   return result;
 }
+
+/*
+ * DICT protocol handler.
+ */
+const struct Curl_handler Curl_handler_dict = {
+  "dict",                               /* scheme */
+  ZERO_NULL,                            /* setup_connection */
+  dict_do,                              /* do_it */
+  ZERO_NULL,                            /* done */
+  ZERO_NULL,                            /* do_more */
+  ZERO_NULL,                            /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_pollset */
+  ZERO_NULL,                            /* doing_pollset */
+  ZERO_NULL,                            /* domore_pollset */
+  ZERO_NULL,                            /* perform_pollset */
+  ZERO_NULL,                            /* disconnect */
+  ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
+  ZERO_NULL,                            /* connection_check */
+  ZERO_NULL,                            /* attach connection */
+  ZERO_NULL,                            /* follow */
+  PORT_DICT,                            /* defport */
+  CURLPROTO_DICT,                       /* protocol */
+  CURLPROTO_DICT,                       /* family */
+  PROTOPT_NONE | PROTOPT_NOURLQUERY     /* flags */
+};
+
 #endif /* CURL_DISABLE_DICT */
index 982f254b2d94594410a39c24aea67d5ffd05d429..9ba536bf2ea706e96ee9dc8bc60868e3064e47ad 100644 (file)
@@ -82,49 +82,6 @@ struct FILEPROTO {
   int fd;     /* open file descriptor to read from! */
 };
 
-/*
- * Forward declarations.
- */
-
-static CURLcode file_do(struct Curl_easy *data, bool *done);
-static CURLcode file_done(struct Curl_easy *data,
-                          CURLcode status, bool premature);
-static CURLcode file_connect(struct Curl_easy *data, bool *done);
-static CURLcode file_disconnect(struct Curl_easy *data,
-                                struct connectdata *conn,
-                                bool dead_connection);
-static CURLcode file_setup_connection(struct Curl_easy *data,
-                                      struct connectdata *conn);
-
-/*
- * FILE scheme handler.
- */
-
-const struct Curl_handler Curl_handler_file = {
-  "file",                               /* scheme */
-  file_setup_connection,                /* setup_connection */
-  file_do,                              /* do_it */
-  file_done,                            /* done */
-  ZERO_NULL,                            /* do_more */
-  file_connect,                         /* connect_it */
-  ZERO_NULL,                            /* connecting */
-  ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_pollset */
-  ZERO_NULL,                            /* doing_pollset */
-  ZERO_NULL,                            /* domore_pollset */
-  ZERO_NULL,                            /* perform_pollset */
-  file_disconnect,                      /* disconnect */
-  ZERO_NULL,                            /* write_resp */
-  ZERO_NULL,                            /* write_resp_hd */
-  ZERO_NULL,                            /* connection_check */
-  ZERO_NULL,                            /* attach connection */
-  ZERO_NULL,                            /* follow */
-  0,                                    /* defport */
-  CURLPROTO_FILE,                       /* protocol */
-  CURLPROTO_FILE,                       /* family */
-  PROTOPT_NONETWORK | PROTOPT_NOURLQUERY /* flags */
-};
-
 static void file_cleanup(struct FILEPROTO *file)
 {
   Curl_safefree(file->freepath);
@@ -158,6 +115,19 @@ static CURLcode file_setup_connection(struct Curl_easy *data,
   return CURLE_OK;
 }
 
+static CURLcode file_done(struct Curl_easy *data,
+                          CURLcode status, bool premature)
+{
+  struct FILEPROTO *file = Curl_meta_get(data, CURL_META_FILE_EASY);
+  (void)status;
+  (void)premature;
+
+  if(file)
+    file_cleanup(file);
+
+  return CURLE_OK;
+}
+
 /*
  * file_connect() gets called from Curl_protocol_connect() to allow us to
  * do protocol-specific actions at connect-time. We emulate a
@@ -276,19 +246,6 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done)
   return CURLE_OK;
 }
 
-static CURLcode file_done(struct Curl_easy *data,
-                          CURLcode status, bool premature)
-{
-  struct FILEPROTO *file = Curl_meta_get(data, CURL_META_FILE_EASY);
-  (void)status;
-  (void)premature;
-
-  if(file)
-    file_cleanup(file);
-
-  return CURLE_OK;
-}
-
 static CURLcode file_disconnect(struct Curl_easy *data,
                                 struct connectdata *conn,
                                 bool dead_connection)
@@ -648,4 +605,32 @@ out:
   return result;
 }
 
+/*
+ * FILE scheme handler.
+ */
+const struct Curl_handler Curl_handler_file = {
+  "file",                               /* scheme */
+  file_setup_connection,                /* setup_connection */
+  file_do,                              /* do_it */
+  file_done,                            /* done */
+  ZERO_NULL,                            /* do_more */
+  file_connect,                         /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_pollset */
+  ZERO_NULL,                            /* doing_pollset */
+  ZERO_NULL,                            /* domore_pollset */
+  ZERO_NULL,                            /* perform_pollset */
+  file_disconnect,                      /* disconnect */
+  ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
+  ZERO_NULL,                            /* connection_check */
+  ZERO_NULL,                            /* attach connection */
+  ZERO_NULL,                            /* follow */
+  0,                                    /* defport */
+  CURLPROTO_FILE,                       /* protocol */
+  CURLPROTO_FILE,                       /* family */
+  PROTOPT_NONETWORK | PROTOPT_NOURLQUERY /* flags */
+};
+
 #endif
index 186530de9f18c5ac2ccb36b09a050a222e76507a..9d35c68f316a4c3b499a4f7509b45f7c5edf32ad 100644 (file)
 #include "url.h"
 #include "escape.h"
 
-/*
- * Forward declarations.
- */
-
-static CURLcode gopher_do(struct Curl_easy *data, bool *done);
 #ifdef USE_SSL
-static CURLcode gopher_connect(struct Curl_easy *data, bool *done);
-static CURLcode gopher_connecting(struct Curl_easy *data, bool *done);
-#endif
-
-/*
- * Gopher protocol handler.
- * This is also a nice simple template to build off for simple
- * connect-command-download protocols.
- */
-
-const struct Curl_handler Curl_handler_gopher = {
-  "gopher",                             /* scheme */
-  ZERO_NULL,                            /* setup_connection */
-  gopher_do,                            /* do_it */
-  ZERO_NULL,                            /* done */
-  ZERO_NULL,                            /* do_more */
-  ZERO_NULL,                            /* connect_it */
-  ZERO_NULL,                            /* connecting */
-  ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_pollset */
-  ZERO_NULL,                            /* doing_pollset */
-  ZERO_NULL,                            /* domore_pollset */
-  ZERO_NULL,                            /* perform_pollset */
-  ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* write_resp */
-  ZERO_NULL,                            /* write_resp_hd */
-  ZERO_NULL,                            /* connection_check */
-  ZERO_NULL,                            /* attach connection */
-  ZERO_NULL,                            /* follow */
-  PORT_GOPHER,                          /* defport */
-  CURLPROTO_GOPHER,                     /* protocol */
-  CURLPROTO_GOPHER,                     /* family */
-  PROTOPT_NONE                          /* flags */
-};
-
-#ifdef USE_SSL
-const struct Curl_handler Curl_handler_gophers = {
-  "gophers",                            /* scheme */
-  ZERO_NULL,                            /* setup_connection */
-  gopher_do,                            /* do_it */
-  ZERO_NULL,                            /* done */
-  ZERO_NULL,                            /* do_more */
-  gopher_connect,                       /* connect_it */
-  gopher_connecting,                    /* connecting */
-  ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_pollset */
-  ZERO_NULL,                            /* doing_pollset */
-  ZERO_NULL,                            /* domore_pollset */
-  ZERO_NULL,                            /* perform_pollset */
-  ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* write_resp */
-  ZERO_NULL,                            /* write_resp_hd */
-  ZERO_NULL,                            /* connection_check */
-  ZERO_NULL,                            /* attach connection */
-  ZERO_NULL,                            /* follow */
-  PORT_GOPHER,                          /* defport */
-  CURLPROTO_GOPHERS,                    /* protocol */
-  CURLPROTO_GOPHER,                     /* family */
-  PROTOPT_SSL                           /* flags */
-};
-
 static CURLcode gopher_connect(struct Curl_easy *data, bool *done)
 {
   (void)data;
@@ -231,4 +165,63 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done)
   Curl_xfer_setup_recv(data, FIRSTSOCKET, -1);
   return CURLE_OK;
 }
+
+/*
+ * Gopher protocol handler.
+ * This is also a nice simple template to build off for simple
+ * connect-command-download protocols.
+ */
+
+const struct Curl_handler Curl_handler_gopher = {
+  "gopher",                             /* scheme */
+  ZERO_NULL,                            /* setup_connection */
+  gopher_do,                            /* do_it */
+  ZERO_NULL,                            /* done */
+  ZERO_NULL,                            /* do_more */
+  ZERO_NULL,                            /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_pollset */
+  ZERO_NULL,                            /* doing_pollset */
+  ZERO_NULL,                            /* domore_pollset */
+  ZERO_NULL,                            /* perform_pollset */
+  ZERO_NULL,                            /* disconnect */
+  ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
+  ZERO_NULL,                            /* connection_check */
+  ZERO_NULL,                            /* attach connection */
+  ZERO_NULL,                            /* follow */
+  PORT_GOPHER,                          /* defport */
+  CURLPROTO_GOPHER,                     /* protocol */
+  CURLPROTO_GOPHER,                     /* family */
+  PROTOPT_NONE                          /* flags */
+};
+
+#ifdef USE_SSL
+const struct Curl_handler Curl_handler_gophers = {
+  "gophers",                            /* scheme */
+  ZERO_NULL,                            /* setup_connection */
+  gopher_do,                            /* do_it */
+  ZERO_NULL,                            /* done */
+  ZERO_NULL,                            /* do_more */
+  gopher_connect,                       /* connect_it */
+  gopher_connecting,                    /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_pollset */
+  ZERO_NULL,                            /* doing_pollset */
+  ZERO_NULL,                            /* domore_pollset */
+  ZERO_NULL,                            /* perform_pollset */
+  ZERO_NULL,                            /* disconnect */
+  ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
+  ZERO_NULL,                            /* connection_check */
+  ZERO_NULL,                            /* attach connection */
+  ZERO_NULL,                            /* follow */
+  PORT_GOPHER,                          /* defport */
+  CURLPROTO_GOPHERS,                    /* protocol */
+  CURLPROTO_GOPHER,                     /* family */
+  PROTOPT_SSL                           /* flags */
+};
+#endif
+
 #endif /* CURL_DISABLE_GOPHER */
index f452fe2550f1a8c9309b495f36d1646e3ac352f0..76d904c05ad3f1e3aee3bb772a34624c5b72c141 100644 (file)
 #include "bufref.h"
 #include "curlx/strparse.h"
 
-/*
- * Forward declarations.
- */
-
-static bool http_should_fail(struct Curl_easy *data, int httpcode);
-static bool http_exp100_is_waiting(struct Curl_easy *data);
-static CURLcode http_exp100_add_reader(struct Curl_easy *data);
-static void http_exp100_send_anyway(struct Curl_easy *data);
-static bool http_exp100_is_selected(struct Curl_easy *data);
-static void http_exp100_got100(struct Curl_easy *data);
-static CURLcode http_firstwrite(struct Curl_easy *data);
-static CURLcode http_header(struct Curl_easy *data,
-                            const char *hd, size_t hdlen);
-static CURLcode http_range(struct Curl_easy *data,
-                           Curl_HttpReq httpreq);
-static CURLcode http_req_set_TE(struct Curl_easy *data,
-                                struct dynbuf *req,
-                                int httpversion);
-static CURLcode http_size(struct Curl_easy *data);
-static CURLcode http_statusline(struct Curl_easy *data,
-                                struct connectdata *conn);
-static CURLcode http_target(struct Curl_easy *data, struct dynbuf *req);
-static CURLcode http_useragent(struct Curl_easy *data);
-static CURLcode http_write_header(struct Curl_easy *data,
-                                  const char *hd, size_t hdlen);
-
-/*
- * HTTP handler interface.
- */
-const struct Curl_handler Curl_handler_http = {
-  "http",                               /* scheme */
-  Curl_http_setup_conn,                 /* setup_connection */
-  Curl_http,                            /* do_it */
-  Curl_http_done,                       /* done */
-  ZERO_NULL,                            /* do_more */
-  ZERO_NULL,                            /* connect_it */
-  ZERO_NULL,                            /* connecting */
-  ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_pollset */
-  Curl_http_doing_pollset,              /* doing_pollset */
-  ZERO_NULL,                            /* domore_pollset */
-  Curl_http_perform_pollset,            /* perform_pollset */
-  ZERO_NULL,                            /* disconnect */
-  Curl_http_write_resp,                 /* write_resp */
-  Curl_http_write_resp_hd,              /* write_resp_hd */
-  ZERO_NULL,                            /* connection_check */
-  ZERO_NULL,                            /* attach connection */
-  Curl_http_follow,                     /* follow */
-  PORT_HTTP,                            /* defport */
-  CURLPROTO_HTTP,                       /* protocol */
-  CURLPROTO_HTTP,                       /* family */
-  PROTOPT_CREDSPERREQUEST |             /* flags */
-    PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE
-
-};
-
-#ifdef USE_SSL
-/*
- * HTTPS handler interface.
- */
-const struct Curl_handler Curl_handler_https = {
-  "https",                              /* scheme */
-  Curl_http_setup_conn,                 /* setup_connection */
-  Curl_http,                            /* do_it */
-  Curl_http_done,                       /* done */
-  ZERO_NULL,                            /* do_more */
-  ZERO_NULL,                            /* connect_it */
-  NULL,                                 /* connecting */
-  ZERO_NULL,                            /* doing */
-  NULL,                                 /* proto_pollset */
-  Curl_http_doing_pollset,              /* doing_pollset */
-  ZERO_NULL,                            /* domore_pollset */
-  Curl_http_perform_pollset,            /* perform_pollset */
-  ZERO_NULL,                            /* disconnect */
-  Curl_http_write_resp,                 /* write_resp */
-  Curl_http_write_resp_hd,              /* write_resp_hd */
-  ZERO_NULL,                            /* connection_check */
-  ZERO_NULL,                            /* attach connection */
-  Curl_http_follow,                     /* follow */
-  PORT_HTTPS,                           /* defport */
-  CURLPROTO_HTTPS,                      /* protocol */
-  CURLPROTO_HTTP,                       /* family */
-  PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN | /* flags */
-    PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE
-};
-
-#endif
-
 void Curl_http_neg_init(struct Curl_easy *data, struct http_negotiation *neg)
 {
   memset(neg, 0, sizeof(*neg));
@@ -547,13 +459,83 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data,
   return CURLE_OK;
 }
 
+/**
+ * http_should_fail() determines whether an HTTP response code has gotten us
+ * into an error state or not.
+ *
+ * @retval FALSE communications should continue
+ *
+ * @retval TRUE communications should not continue
+ */
+static bool http_should_fail(struct Curl_easy *data, int httpcode)
+{
+  DEBUGASSERT(data);
+  DEBUGASSERT(data->conn);
+
+  /*
+  ** If we have not been asked to fail on error,
+  ** do not fail.
+  */
+  if(!data->set.http_fail_on_error)
+    return FALSE;
+
+  /*
+  ** Any code < 400 is never terminal.
+  */
+  if(httpcode < 400)
+    return FALSE;
+
+  /*
+  ** A 416 response to a resume request is presumably because the file is
+  ** already completely downloaded and thus not actually a fail.
+  */
+  if(data->state.resume_from && data->state.httpreq == HTTPREQ_GET &&
+     httpcode == 416)
+    return FALSE;
+
+  /*
+  ** Any code >= 400 that is not 401 or 407 is always
+  ** a terminal error
+  */
+  if((httpcode != 401) && (httpcode != 407))
+    return TRUE;
+
+  /*
+  ** All we have left to deal with is 401 and 407
+  */
+  DEBUGASSERT((httpcode == 401) || (httpcode == 407));
+
+  /*
+  ** Examine the current authentication state to see if this is an error. The
+  ** idea is for this function to get called after processing all the headers
+  ** in a response message. So, if we have been to asked to authenticate a
+  ** particular stage, and we have done it, we are OK. If we are already
+  ** completely authenticated, it is not OK to get another 401 or 407.
+  **
+  ** It is possible for authentication to go stale such that the client needs
+  ** to reauthenticate. Once that info is available, use it here.
+  */
+
+  /*
+  ** Either we are not authenticating, or we are supposed to be authenticating
+  ** something else. This is an error.
+  */
+  if((httpcode == 401) && !data->state.aptr.user)
+    return TRUE;
+#ifndef CURL_DISABLE_PROXY
+  if((httpcode == 407) && !data->conn->bits.proxy_user_passwd)
+    return TRUE;
+#endif
+
+  return data->state.authproblem;
+}
+
 /*
  * Curl_http_auth_act() gets called when all HTTP headers have been received
  * and it checks what authentication methods that are available and decides
  * which one (if any) to use. It will set 'newurl' if an auth method was
  * picked.
  */
-
 CURLcode Curl_http_auth_act(struct Curl_easy *data)
 {
   struct connectdata *conn = data->conn;
@@ -1116,77 +1098,6 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
 #endif
 }
 
-/**
- * http_should_fail() determines whether an HTTP response code has gotten us
- * into an error state or not.
- *
- * @retval FALSE communications should continue
- *
- * @retval TRUE communications should not continue
- */
-static bool http_should_fail(struct Curl_easy *data, int httpcode)
-{
-  DEBUGASSERT(data);
-  DEBUGASSERT(data->conn);
-
-  /*
-  ** If we have not been asked to fail on error,
-  ** do not fail.
-  */
-  if(!data->set.http_fail_on_error)
-    return FALSE;
-
-  /*
-  ** Any code < 400 is never terminal.
-  */
-  if(httpcode < 400)
-    return FALSE;
-
-  /*
-  ** A 416 response to a resume request is presumably because the file is
-  ** already completely downloaded and thus not actually a fail.
-  */
-  if(data->state.resume_from && data->state.httpreq == HTTPREQ_GET &&
-     httpcode == 416)
-    return FALSE;
-
-  /*
-  ** Any code >= 400 that is not 401 or 407 is always
-  ** a terminal error
-  */
-  if((httpcode != 401) && (httpcode != 407))
-    return TRUE;
-
-  /*
-  ** All we have left to deal with is 401 and 407
-  */
-  DEBUGASSERT((httpcode == 401) || (httpcode == 407));
-
-  /*
-  ** Examine the current authentication state to see if this is an error. The
-  ** idea is for this function to get called after processing all the headers
-  ** in a response message. So, if we have been to asked to authenticate a
-  ** particular stage, and we have done it, we are OK. If we are already
-  ** completely authenticated, it is not OK to get another 401 or 407.
-  **
-  ** It is possible for authentication to go stale such that the client needs
-  ** to reauthenticate. Once that info is available, use it here.
-  */
-
-  /*
-  ** Either we are not authenticating, or we are supposed to be authenticating
-  ** something else. This is an error.
-  */
-  if((httpcode == 401) && !data->state.aptr.user)
-    return TRUE;
-#ifndef CURL_DISABLE_PROXY
-  if((httpcode == 407) && !data->conn->bits.proxy_user_passwd)
-    return TRUE;
-#endif
-
-  return data->state.authproblem;
-}
-
 static void http_switch_to_get(struct Curl_easy *data, int code)
 {
   const char *req = data->set.str[STRING_CUSTOMREQUEST];
@@ -1532,6 +1443,145 @@ bool Curl_compareheader(const char *headerline, /* line to check */
   return FALSE; /* no match */
 }
 
+struct cr_exp100_ctx {
+  struct Curl_creader super;
+  struct curltime start; /* time started waiting */
+  enum expect100 state;
+};
+
+/* Expect: 100-continue client reader, blocking uploads */
+
+static void http_exp100_continue(struct Curl_easy *data,
+                                 struct Curl_creader *reader)
+{
+  struct cr_exp100_ctx *ctx = reader->ctx;
+  if(ctx->state > EXP100_SEND_DATA) {
+    ctx->state = EXP100_SEND_DATA;
+    Curl_expire_done(data, EXPIRE_100_TIMEOUT);
+  }
+}
+
+static CURLcode cr_exp100_read(struct Curl_easy *data,
+                               struct Curl_creader *reader,
+                               char *buf, size_t blen,
+                               size_t *nread, bool *eos)
+{
+  struct cr_exp100_ctx *ctx = reader->ctx;
+  timediff_t ms;
+
+  switch(ctx->state) {
+  case EXP100_SENDING_REQUEST:
+    if(!Curl_req_sendbuf_empty(data)) {
+      /* The initial request data has not been fully sent yet. Do
+       * not start the timer yet. */
+      DEBUGF(infof(data, "cr_exp100_read, request not full sent yet"));
+      *nread = 0;
+      *eos = FALSE;
+      return CURLE_OK;
+    }
+    /* We are now waiting for a reply from the server or
+     * a timeout on our side IFF the request has been fully sent. */
+    DEBUGF(infof(data, "cr_exp100_read, start AWAITING_CONTINUE, "
+           "timeout %dms", data->set.expect_100_timeout));
+    ctx->state = EXP100_AWAITING_CONTINUE;
+    ctx->start = *Curl_pgrs_now(data);
+    Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT);
+    *nread = 0;
+    *eos = FALSE;
+    return CURLE_OK;
+  case EXP100_FAILED:
+    DEBUGF(infof(data, "cr_exp100_read, expectation failed, error"));
+    *nread = 0;
+    *eos = FALSE;
+    return CURLE_READ_ERROR;
+  case EXP100_AWAITING_CONTINUE:
+    ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->start);
+    if(ms < data->set.expect_100_timeout) {
+      DEBUGF(infof(data, "cr_exp100_read, AWAITING_CONTINUE, not expired"));
+      *nread = 0;
+      *eos = FALSE;
+      return CURLE_OK;
+    }
+    /* we have waited long enough, continue anyway */
+    http_exp100_continue(data, reader);
+    infof(data, "Done waiting for 100-continue");
+    FALLTHROUGH();
+  default:
+    DEBUGF(infof(data, "cr_exp100_read, pass through"));
+    return Curl_creader_read(data, reader->next, buf, blen, nread, eos);
+  }
+}
+
+static void cr_exp100_done(struct Curl_easy *data,
+                           struct Curl_creader *reader, int premature)
+{
+  struct cr_exp100_ctx *ctx = reader->ctx;
+  ctx->state = premature ? EXP100_FAILED : EXP100_SEND_DATA;
+  Curl_expire_done(data, EXPIRE_100_TIMEOUT);
+}
+
+static const struct Curl_crtype cr_exp100 = {
+  "cr-exp100",
+  Curl_creader_def_init,
+  cr_exp100_read,
+  Curl_creader_def_close,
+  Curl_creader_def_needs_rewind,
+  Curl_creader_def_total_length,
+  Curl_creader_def_resume_from,
+  Curl_creader_def_cntrl,
+  Curl_creader_def_is_paused,
+  cr_exp100_done,
+  sizeof(struct cr_exp100_ctx)
+};
+
+static CURLcode http_exp100_add_reader(struct Curl_easy *data)
+{
+  struct Curl_creader *reader = NULL;
+  CURLcode result;
+
+  result = Curl_creader_create(&reader, data, &cr_exp100, CURL_CR_PROTOCOL);
+  if(!result)
+    result = Curl_creader_add(data, reader);
+  if(!result) {
+    struct cr_exp100_ctx *ctx = reader->ctx;
+    ctx->state = EXP100_SENDING_REQUEST;
+  }
+
+  if(result && reader)
+    Curl_creader_free(data, reader);
+  return result;
+}
+
+static void http_exp100_got100(struct Curl_easy *data)
+{
+  struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100);
+  if(r)
+    http_exp100_continue(data, r);
+}
+
+static bool http_exp100_is_waiting(struct Curl_easy *data)
+{
+  struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100);
+  if(r) {
+    struct cr_exp100_ctx *ctx = r->ctx;
+    return ctx->state == EXP100_AWAITING_CONTINUE;
+  }
+  return FALSE;
+}
+
+static void http_exp100_send_anyway(struct Curl_easy *data)
+{
+  struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100);
+  if(r)
+    http_exp100_continue(data, r);
+}
+
+static bool http_exp100_is_selected(struct Curl_easy *data)
+{
+  struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100);
+  return !!r;
+}
+
 /* this returns the socket to wait for in the DO and DOING state for the multi
    interface and then we are always _sending_ a request and thus we wait for
    the single socket to become writable only */
@@ -1559,6 +1609,33 @@ CURLcode Curl_http_perform_pollset(struct Curl_easy *data,
   return result;
 }
 
+static CURLcode http_write_header(struct Curl_easy *data,
+                                  const char *hd, size_t hdlen)
+{
+  CURLcode result;
+  int writetype;
+
+  /* now, only output this if the header AND body are requested:
+   */
+  Curl_debug(data, CURLINFO_HEADER_IN, hd, hdlen);
+
+  writetype = CLIENTWRITE_HEADER |
+    ((data->req.httpcode / 100 == 1) ? CLIENTWRITE_1XX : 0);
+
+  result = Curl_client_write(data, writetype, hd, hdlen);
+  if(result)
+    return result;
+
+  result = Curl_bump_headersize(data, hdlen, FALSE);
+  if(result)
+    return result;
+
+  data->req.deductheadercount = (100 <= data->req.httpcode &&
+                                 199 >= data->req.httpcode) ?
+    data->req.headerbytecount : 0;
+  return result;
+}
+
 /*
  * Curl_http_done() gets called after a single HTTP request has been
  * performed.
@@ -3800,33 +3877,6 @@ CURLcode Curl_bump_headersize(struct Curl_easy *data,
   return CURLE_OK;
 }
 
-static CURLcode http_write_header(struct Curl_easy *data,
-                                  const char *hd, size_t hdlen)
-{
-  CURLcode result;
-  int writetype;
-
-  /* now, only output this if the header AND body are requested:
-   */
-  Curl_debug(data, CURLINFO_HEADER_IN, hd, hdlen);
-
-  writetype = CLIENTWRITE_HEADER |
-    ((data->req.httpcode / 100 == 1) ? CLIENTWRITE_1XX : 0);
-
-  result = Curl_client_write(data, writetype, hd, hdlen);
-  if(result)
-    return result;
-
-  result = Curl_bump_headersize(data, hdlen, FALSE);
-  if(result)
-    return result;
-
-  data->req.deductheadercount = (100 <= data->req.httpcode &&
-                                 199 >= data->req.httpcode) ?
-    data->req.headerbytecount : 0;
-  return result;
-}
-
 static CURLcode http_on_response(struct Curl_easy *data,
                                  const char *last_hd, size_t last_hd_len,
                                  const char *buf, size_t blen,
@@ -4931,143 +4981,65 @@ void Curl_http_resp_free(struct http_resp *resp)
   }
 }
 
-struct cr_exp100_ctx {
-  struct Curl_creader super;
-  struct curltime start; /* time started waiting */
-  enum expect100 state;
-};
-
-/* Expect: 100-continue client reader, blocking uploads */
-
-static void http_exp100_continue(struct Curl_easy *data,
-                                 struct Curl_creader *reader)
-{
-  struct cr_exp100_ctx *ctx = reader->ctx;
-  if(ctx->state > EXP100_SEND_DATA) {
-    ctx->state = EXP100_SEND_DATA;
-    Curl_expire_done(data, EXPIRE_100_TIMEOUT);
-  }
-}
-
-static CURLcode cr_exp100_read(struct Curl_easy *data,
-                               struct Curl_creader *reader,
-                               char *buf, size_t blen,
-                               size_t *nread, bool *eos)
-{
-  struct cr_exp100_ctx *ctx = reader->ctx;
-  timediff_t ms;
-
-  switch(ctx->state) {
-  case EXP100_SENDING_REQUEST:
-    if(!Curl_req_sendbuf_empty(data)) {
-      /* The initial request data has not been fully sent yet. Do
-       * not start the timer yet. */
-      DEBUGF(infof(data, "cr_exp100_read, request not full sent yet"));
-      *nread = 0;
-      *eos = FALSE;
-      return CURLE_OK;
-    }
-    /* We are now waiting for a reply from the server or
-     * a timeout on our side IFF the request has been fully sent. */
-    DEBUGF(infof(data, "cr_exp100_read, start AWAITING_CONTINUE, "
-           "timeout %dms", data->set.expect_100_timeout));
-    ctx->state = EXP100_AWAITING_CONTINUE;
-    ctx->start = *Curl_pgrs_now(data);
-    Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT);
-    *nread = 0;
-    *eos = FALSE;
-    return CURLE_OK;
-  case EXP100_FAILED:
-    DEBUGF(infof(data, "cr_exp100_read, expectation failed, error"));
-    *nread = 0;
-    *eos = FALSE;
-    return CURLE_READ_ERROR;
-  case EXP100_AWAITING_CONTINUE:
-    ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->start);
-    if(ms < data->set.expect_100_timeout) {
-      DEBUGF(infof(data, "cr_exp100_read, AWAITING_CONTINUE, not expired"));
-      *nread = 0;
-      *eos = FALSE;
-      return CURLE_OK;
-    }
-    /* we have waited long enough, continue anyway */
-    http_exp100_continue(data, reader);
-    infof(data, "Done waiting for 100-continue");
-    FALLTHROUGH();
-  default:
-    DEBUGF(infof(data, "cr_exp100_read, pass through"));
-    return Curl_creader_read(data, reader->next, buf, blen, nread, eos);
-  }
-}
-
-static void cr_exp100_done(struct Curl_easy *data,
-                           struct Curl_creader *reader, int premature)
-{
-  struct cr_exp100_ctx *ctx = reader->ctx;
-  ctx->state = premature ? EXP100_FAILED : EXP100_SEND_DATA;
-  Curl_expire_done(data, EXPIRE_100_TIMEOUT);
-}
+/*
+ * HTTP handler interface.
+ */
+const struct Curl_handler Curl_handler_http = {
+  "http",                               /* scheme */
+  Curl_http_setup_conn,                 /* setup_connection */
+  Curl_http,                            /* do_it */
+  Curl_http_done,                       /* done */
+  ZERO_NULL,                            /* do_more */
+  ZERO_NULL,                            /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_pollset */
+  Curl_http_doing_pollset,              /* doing_pollset */
+  ZERO_NULL,                            /* domore_pollset */
+  Curl_http_perform_pollset,            /* perform_pollset */
+  ZERO_NULL,                            /* disconnect */
+  Curl_http_write_resp,                 /* write_resp */
+  Curl_http_write_resp_hd,              /* write_resp_hd */
+  ZERO_NULL,                            /* connection_check */
+  ZERO_NULL,                            /* attach connection */
+  Curl_http_follow,                     /* follow */
+  PORT_HTTP,                            /* defport */
+  CURLPROTO_HTTP,                       /* protocol */
+  CURLPROTO_HTTP,                       /* family */
+  PROTOPT_CREDSPERREQUEST |             /* flags */
+    PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE
 
-static const struct Curl_crtype cr_exp100 = {
-  "cr-exp100",
-  Curl_creader_def_init,
-  cr_exp100_read,
-  Curl_creader_def_close,
-  Curl_creader_def_needs_rewind,
-  Curl_creader_def_total_length,
-  Curl_creader_def_resume_from,
-  Curl_creader_def_cntrl,
-  Curl_creader_def_is_paused,
-  cr_exp100_done,
-  sizeof(struct cr_exp100_ctx)
 };
 
-static CURLcode http_exp100_add_reader(struct Curl_easy *data)
-{
-  struct Curl_creader *reader = NULL;
-  CURLcode result;
-
-  result = Curl_creader_create(&reader, data, &cr_exp100, CURL_CR_PROTOCOL);
-  if(!result)
-    result = Curl_creader_add(data, reader);
-  if(!result) {
-    struct cr_exp100_ctx *ctx = reader->ctx;
-    ctx->state = EXP100_SENDING_REQUEST;
-  }
-
-  if(result && reader)
-    Curl_creader_free(data, reader);
-  return result;
-}
-
-static void http_exp100_got100(struct Curl_easy *data)
-{
-  struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100);
-  if(r)
-    http_exp100_continue(data, r);
-}
-
-static bool http_exp100_is_waiting(struct Curl_easy *data)
-{
-  struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100);
-  if(r) {
-    struct cr_exp100_ctx *ctx = r->ctx;
-    return ctx->state == EXP100_AWAITING_CONTINUE;
-  }
-  return FALSE;
-}
-
-static void http_exp100_send_anyway(struct Curl_easy *data)
-{
-  struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100);
-  if(r)
-    http_exp100_continue(data, r);
-}
-
-static bool http_exp100_is_selected(struct Curl_easy *data)
-{
-  struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100);
-  return !!r;
-}
+#ifdef USE_SSL
+/*
+ * HTTPS handler interface.
+ */
+const struct Curl_handler Curl_handler_https = {
+  "https",                              /* scheme */
+  Curl_http_setup_conn,                 /* setup_connection */
+  Curl_http,                            /* do_it */
+  Curl_http_done,                       /* done */
+  ZERO_NULL,                            /* do_more */
+  ZERO_NULL,                            /* connect_it */
+  NULL,                                 /* connecting */
+  ZERO_NULL,                            /* doing */
+  NULL,                                 /* proto_pollset */
+  Curl_http_doing_pollset,              /* doing_pollset */
+  ZERO_NULL,                            /* domore_pollset */
+  Curl_http_perform_pollset,            /* perform_pollset */
+  ZERO_NULL,                            /* disconnect */
+  Curl_http_write_resp,                 /* write_resp */
+  Curl_http_write_resp_hd,              /* write_resp_hd */
+  ZERO_NULL,                            /* connection_check */
+  ZERO_NULL,                            /* attach connection */
+  Curl_http_follow,                     /* follow */
+  PORT_HTTPS,                           /* defport */
+  CURLPROTO_HTTPS,                      /* protocol */
+  CURLPROTO_HTTP,                       /* family */
+  PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN | /* flags */
+    PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE
+};
+#endif
 
 #endif /* CURL_DISABLE_HTTP */
index 2ba1ca73e9245ed565a2186e998a1e6015596dca..5f5c91a49a388abcfaa93c62669f9673720395ce 100644 (file)
@@ -89,48 +89,6 @@ struct MQTT {
   BIT(pingsent); /* 1 while we wait for ping response */
 };
 
-/*
- * Forward declarations.
- */
-
-static CURLcode mqtt_do(struct Curl_easy *data, bool *done);
-static CURLcode mqtt_done(struct Curl_easy *data,
-                          CURLcode status, bool premature);
-static CURLcode mqtt_doing(struct Curl_easy *data, bool *done);
-static CURLcode mqtt_pollset(struct Curl_easy *data,
-                             struct easy_pollset *ps);
-static CURLcode mqtt_setup_conn(struct Curl_easy *data,
-                                struct connectdata *conn);
-
-/*
- * MQTT protocol handler.
- */
-
-const struct Curl_handler Curl_handler_mqtt = {
-  "mqtt",                             /* scheme */
-  mqtt_setup_conn,                    /* setup_connection */
-  mqtt_do,                            /* do_it */
-  mqtt_done,                          /* done */
-  ZERO_NULL,                          /* do_more */
-  ZERO_NULL,                          /* connect_it */
-  ZERO_NULL,                          /* connecting */
-  mqtt_doing,                         /* doing */
-  ZERO_NULL,                          /* proto_pollset */
-  mqtt_pollset,                       /* doing_pollset */
-  ZERO_NULL,                          /* domore_pollset */
-  ZERO_NULL,                          /* perform_pollset */
-  ZERO_NULL,                          /* disconnect */
-  ZERO_NULL,                          /* write_resp */
-  ZERO_NULL,                          /* write_resp_hd */
-  ZERO_NULL,                          /* connection_check */
-  ZERO_NULL,                          /* attach connection */
-  ZERO_NULL,                          /* follow */
-  PORT_MQTT,                          /* defport */
-  CURLPROTO_MQTT,                     /* protocol */
-  CURLPROTO_MQTT,                     /* family */
-  PROTOPT_NONE                        /* flags */
-};
-
 static void mqtt_easy_dtor(void *key, size_t klen, void *entry)
 {
   struct MQTT *mq = entry;
@@ -976,4 +934,33 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done)
   return result;
 }
 
+/*
+ * MQTT protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_mqtt = {
+  "mqtt",                             /* scheme */
+  mqtt_setup_conn,                    /* setup_connection */
+  mqtt_do,                            /* do_it */
+  mqtt_done,                          /* done */
+  ZERO_NULL,                          /* do_more */
+  ZERO_NULL,                          /* connect_it */
+  ZERO_NULL,                          /* connecting */
+  mqtt_doing,                         /* doing */
+  ZERO_NULL,                          /* proto_pollset */
+  mqtt_pollset,                       /* doing_pollset */
+  ZERO_NULL,                          /* domore_pollset */
+  ZERO_NULL,                          /* perform_pollset */
+  ZERO_NULL,                          /* disconnect */
+  ZERO_NULL,                          /* write_resp */
+  ZERO_NULL,                          /* write_resp_hd */
+  ZERO_NULL,                          /* connection_check */
+  ZERO_NULL,                          /* attach connection */
+  ZERO_NULL,                          /* follow */
+  PORT_MQTT,                          /* defport */
+  CURLPROTO_MQTT,                     /* protocol */
+  CURLPROTO_MQTT,                     /* family */
+  PROTOPT_NONE                        /* flags */
+};
+
 #endif /* CURL_DISABLE_MQTT */
index 003a38f19f0f1fb05c35c9b38687409ae65e2b81..e4a1df14121110bab881c84e0eb303487af58fd6 100644 (file)
--- a/lib/smb.c
+++ b/lib/smb.c
@@ -285,77 +285,6 @@ struct smb_tree_disconnect {
 #  pragma pack(pop)
 #endif
 
-/* Local API functions */
-static CURLcode smb_setup_connection(struct Curl_easy *data,
-                                     struct connectdata *conn);
-static CURLcode smb_connect(struct Curl_easy *data, bool *done);
-static CURLcode smb_connection_state(struct Curl_easy *data, bool *done);
-static CURLcode smb_do(struct Curl_easy *data, bool *done);
-static CURLcode smb_request_state(struct Curl_easy *data, bool *done);
-static CURLcode smb_pollset(struct Curl_easy *data,
-                            struct easy_pollset *ps);
-static CURLcode smb_parse_url_path(struct Curl_easy *data,
-                                   struct smb_conn *smbc,
-                                   struct smb_request *req);
-
-/*
- * SMB handler interface
- */
-const struct Curl_handler Curl_handler_smb = {
-  "smb",                                /* scheme */
-  smb_setup_connection,                 /* setup_connection */
-  smb_do,                               /* do_it */
-  ZERO_NULL,                            /* done */
-  ZERO_NULL,                            /* do_more */
-  smb_connect,                          /* connect_it */
-  smb_connection_state,                 /* connecting */
-  smb_request_state,                    /* doing */
-  smb_pollset,                          /* proto_pollset */
-  smb_pollset,                          /* doing_pollset */
-  ZERO_NULL,                            /* domore_pollset */
-  ZERO_NULL,                            /* perform_pollset */
-  ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* write_resp */
-  ZERO_NULL,                            /* write_resp_hd */
-  ZERO_NULL,                            /* connection_check */
-  ZERO_NULL,                            /* attach connection */
-  ZERO_NULL,                            /* follow */
-  PORT_SMB,                             /* defport */
-  CURLPROTO_SMB,                        /* protocol */
-  CURLPROTO_SMB,                        /* family */
-  PROTOPT_CONN_REUSE                    /* flags */
-};
-
-#ifdef USE_SSL
-/*
- * SMBS handler interface
- */
-const struct Curl_handler Curl_handler_smbs = {
-  "smbs",                               /* scheme */
-  smb_setup_connection,                 /* setup_connection */
-  smb_do,                               /* do_it */
-  ZERO_NULL,                            /* done */
-  ZERO_NULL,                            /* do_more */
-  smb_connect,                          /* connect_it */
-  smb_connection_state,                 /* connecting */
-  smb_request_state,                    /* doing */
-  smb_pollset,                          /* proto_pollset */
-  smb_pollset,                          /* doing_pollset */
-  ZERO_NULL,                            /* domore_pollset */
-  ZERO_NULL,                            /* perform_pollset */
-  ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* write_resp */
-  ZERO_NULL,                            /* write_resp_hd */
-  ZERO_NULL,                            /* connection_check */
-  ZERO_NULL,                            /* attach connection */
-  ZERO_NULL,                            /* follow */
-  PORT_SMBS,                            /* defport */
-  CURLPROTO_SMBS,                       /* protocol */
-  CURLPROTO_SMB,                        /* family */
-  PROTOPT_SSL | PROTOPT_CONN_REUSE      /* flags */
-};
-#endif
-
 #define MAX_PAYLOAD_SIZE  0x8000
 #define MAX_MESSAGE_SIZE  (MAX_PAYLOAD_SIZE + 0x1000)
 #define CLIENTNAME        "curl"
@@ -460,6 +389,49 @@ static void smb_conn_dtor(void *key, size_t klen, void *entry)
   curlx_free(smbc);
 }
 
+static CURLcode smb_parse_url_path(struct Curl_easy *data,
+                                   struct smb_conn *smbc,
+                                   struct smb_request *req)
+{
+  char *path;
+  char *slash;
+  CURLcode result;
+
+  /* URL decode the path */
+  result = Curl_urldecode(data->state.up.path, 0, &path, NULL, REJECT_CTRL);
+  if(result)
+    return result;
+
+  /* Parse the path for the share */
+  smbc->share = curlx_strdup((*path == '/' || *path == '\\')
+                             ? path + 1 : path);
+  curlx_free(path);
+  if(!smbc->share)
+    return CURLE_OUT_OF_MEMORY;
+
+  slash = strchr(smbc->share, '/');
+  if(!slash)
+    slash = strchr(smbc->share, '\\');
+
+  /* The share must be present */
+  if(!slash) {
+    Curl_safefree(smbc->share);
+    failf(data, "missing share in URL path for SMB");
+    return CURLE_URL_MALFORMAT;
+  }
+
+  /* Parse the path for the file path converting any forward slashes into
+     backslashes */
+  *slash++ = 0;
+  req->path = slash;
+
+  for(; *slash; slash++) {
+    if(*slash == '/')
+      *slash = '\\';
+  }
+  return CURLE_OK;
+}
+
 /* this should setup things in the connection, not in the easy
    handle */
 static CURLcode smb_setup_connection(struct Curl_easy *data,
@@ -1226,47 +1198,62 @@ static CURLcode smb_do(struct Curl_easy *data, bool *done)
   return CURLE_URL_MALFORMAT;
 }
 
-static CURLcode smb_parse_url_path(struct Curl_easy *data,
-                                   struct smb_conn *smbc,
-                                   struct smb_request *req)
-{
-  char *path;
-  char *slash;
-  CURLcode result;
-
-  /* URL decode the path */
-  result = Curl_urldecode(data->state.up.path, 0, &path, NULL, REJECT_CTRL);
-  if(result)
-    return result;
-
-  /* Parse the path for the share */
-  smbc->share = curlx_strdup((*path == '/' || *path == '\\')
-                             ? path + 1 : path);
-  curlx_free(path);
-  if(!smbc->share)
-    return CURLE_OUT_OF_MEMORY;
-
-  slash = strchr(smbc->share, '/');
-  if(!slash)
-    slash = strchr(smbc->share, '\\');
-
-  /* The share must be present */
-  if(!slash) {
-    Curl_safefree(smbc->share);
-    failf(data, "missing share in URL path for SMB");
-    return CURLE_URL_MALFORMAT;
-  }
-
-  /* Parse the path for the file path converting any forward slashes into
-     backslashes */
-  *slash++ = 0;
-  req->path = slash;
+/*
+ * SMB handler interface
+ */
+const struct Curl_handler Curl_handler_smb = {
+  "smb",                                /* scheme */
+  smb_setup_connection,                 /* setup_connection */
+  smb_do,                               /* do_it */
+  ZERO_NULL,                            /* done */
+  ZERO_NULL,                            /* do_more */
+  smb_connect,                          /* connect_it */
+  smb_connection_state,                 /* connecting */
+  smb_request_state,                    /* doing */
+  smb_pollset,                          /* proto_pollset */
+  smb_pollset,                          /* doing_pollset */
+  ZERO_NULL,                            /* domore_pollset */
+  ZERO_NULL,                            /* perform_pollset */
+  ZERO_NULL,                            /* disconnect */
+  ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
+  ZERO_NULL,                            /* connection_check */
+  ZERO_NULL,                            /* attach connection */
+  ZERO_NULL,                            /* follow */
+  PORT_SMB,                             /* defport */
+  CURLPROTO_SMB,                        /* protocol */
+  CURLPROTO_SMB,                        /* family */
+  PROTOPT_CONN_REUSE                    /* flags */
+};
 
-  for(; *slash; slash++) {
-    if(*slash == '/')
-      *slash = '\\';
-  }
-  return CURLE_OK;
-}
+#ifdef USE_SSL
+/*
+ * SMBS handler interface
+ */
+const struct Curl_handler Curl_handler_smbs = {
+  "smbs",                               /* scheme */
+  smb_setup_connection,                 /* setup_connection */
+  smb_do,                               /* do_it */
+  ZERO_NULL,                            /* done */
+  ZERO_NULL,                            /* do_more */
+  smb_connect,                          /* connect_it */
+  smb_connection_state,                 /* connecting */
+  smb_request_state,                    /* doing */
+  smb_pollset,                          /* proto_pollset */
+  smb_pollset,                          /* doing_pollset */
+  ZERO_NULL,                            /* domore_pollset */
+  ZERO_NULL,                            /* perform_pollset */
+  ZERO_NULL,                            /* disconnect */
+  ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
+  ZERO_NULL,                            /* connection_check */
+  ZERO_NULL,                            /* attach connection */
+  ZERO_NULL,                            /* follow */
+  PORT_SMBS,                            /* defport */
+  CURLPROTO_SMBS,                       /* protocol */
+  CURLPROTO_SMB,                        /* family */
+  PROTOPT_SSL | PROTOPT_CONN_REUSE      /* flags */
+};
+#endif
 
 #endif /* CURL_DISABLE_SMB && USE_CURL_NTLM_CORE && SIZEOF_CURL_OFF_T > 4 */
index 69571bbdde7f3a244b436607bef37680d598a77f..0c65c57c5904b77be9fc4b294d6aacda247116f1 100644 (file)
@@ -142,49 +142,6 @@ struct tftp_conn {
   BIT(remote_pinned);
 };
 
-/* Forward declarations */
-static CURLcode tftp_rx(struct tftp_conn *state, tftp_event_t event);
-static CURLcode tftp_tx(struct tftp_conn *state, tftp_event_t event);
-static CURLcode tftp_connect(struct Curl_easy *data, bool *done);
-static CURLcode tftp_do(struct Curl_easy *data, bool *done);
-static CURLcode tftp_done(struct Curl_easy *data,
-                          CURLcode, bool premature);
-static CURLcode tftp_setup_connection(struct Curl_easy *data,
-                                      struct connectdata *conn);
-static CURLcode tftp_multi_statemach(struct Curl_easy *data, bool *done);
-static CURLcode tftp_doing(struct Curl_easy *data, bool *dophase_done);
-static CURLcode tftp_pollset(struct Curl_easy *data,
-                             struct easy_pollset *ps);
-static CURLcode tftp_translate_code(tftp_error_t error);
-
-/*
- * TFTP protocol handler.
- */
-const struct Curl_handler Curl_handler_tftp = {
-  "tftp",                               /* scheme */
-  tftp_setup_connection,                /* setup_connection */
-  tftp_do,                              /* do_it */
-  tftp_done,                            /* done */
-  ZERO_NULL,                            /* do_more */
-  tftp_connect,                         /* connect_it */
-  tftp_multi_statemach,                 /* connecting */
-  tftp_doing,                           /* doing */
-  tftp_pollset,                         /* proto_pollset */
-  tftp_pollset,                         /* doing_pollset */
-  ZERO_NULL,                            /* domore_pollset */
-  ZERO_NULL,                            /* perform_pollset */
-  ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* write_resp */
-  ZERO_NULL,                            /* write_resp_hd */
-  ZERO_NULL,                            /* connection_check */
-  ZERO_NULL,                            /* attach connection */
-  ZERO_NULL,                            /* follow */
-  PORT_TFTP,                            /* defport */
-  CURLPROTO_TFTP,                       /* protocol */
-  CURLPROTO_TFTP,                       /* family */
-  PROTOPT_NOTCPPROXY | PROTOPT_NOURLQUERY /* flags */
-};
-
 /**********************************************************
  *
  * tftp_set_timeouts -
@@ -383,185 +340,175 @@ static CURLcode tftp_option_add(struct tftp_conn *state, size_t *csize,
   return CURLE_OK;
 }
 
-static CURLcode tftp_connect_for_tx(struct tftp_conn *state,
-                                    tftp_event_t event)
-{
-  CURLcode result;
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
-  struct Curl_easy *data = state->data;
-
-  infof(data, "%s", "Connected for transmit");
-#endif
-  state->state = TFTP_STATE_TX;
-  result = tftp_set_timeouts(state);
-  if(result)
-    return result;
-  return tftp_tx(state, event);
-}
-
-static CURLcode tftp_connect_for_rx(struct tftp_conn *state,
-                                    tftp_event_t event)
-{
-  CURLcode result;
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
-  struct Curl_easy *data = state->data;
-
-  infof(data, "%s", "Connected for receive");
-#endif
-  state->state = TFTP_STATE_RX;
-  result = tftp_set_timeouts(state);
-  if(result)
-    return result;
-  return tftp_rx(state, event);
-}
+/* the next blocknum is x + 1 but it needs to wrap at an unsigned 16bit
+   boundary */
+#define NEXT_BLOCKNUM(x) (((x) + 1) & 0xffff)
 
-static CURLcode tftp_send_first(struct tftp_conn *state,
-                                tftp_event_t event)
+/**********************************************************
+ *
+ * tftp_tx
+ *
+ * Event handler for the TX state
+ *
+ **********************************************************/
+static CURLcode tftp_tx(struct tftp_conn *state, tftp_event_t event)
 {
-  size_t sbytes;
-  ssize_t senddata;
-  const char *mode = "octet";
-  char *filename;
   struct Curl_easy *data = state->data;
-  const struct Curl_sockaddr_ex *remote_addr = NULL;
+  ssize_t sbytes;
   CURLcode result = CURLE_OK;
-
-  /* Set ASCII mode if -B flag was used */
-  if(data->state.prefer_ascii)
-    mode = "netascii";
+  struct SingleRequest *k = &data->req;
+  size_t cb; /* Bytes currently read */
+  char buffer[STRERROR_LEN];
+  char *bufptr;
+  bool eos;
 
   switch(event) {
 
-  case TFTP_EVENT_INIT:    /* Send the first packet out */
-  case TFTP_EVENT_TIMEOUT: /* Resend the first packet out */
-    /* Increment the retry counter, quit if over the limit */
-    state->retries++;
-    if(state->retries > state->retry_max) {
-      state->error = TFTP_ERR_NORESPONSE;
-      state->state = TFTP_STATE_FIN;
-      return result;
-    }
+  case TFTP_EVENT_ACK:
+  case TFTP_EVENT_OACK:
+    if(event == TFTP_EVENT_ACK) {
+      /* Ack the packet */
+      int rblock = getrpacketblock(&state->rpacket);
 
-    if(data->state.upload) {
-      /* If we are uploading, send an WRQ */
-      setpacketevent(&state->spacket, TFTP_EVENT_WRQ);
-      if(data->state.infilesize != -1)
-        Curl_pgrsSetUploadSize(data, data->state.infilesize);
-    }
-    else {
-      /* If we are downloading, send an RRQ */
-      setpacketevent(&state->spacket, TFTP_EVENT_RRQ);
-    }
-    /* As RFC3617 describes the separator slash is not actually part of the
-       filename so we skip the always-present first letter of the path
-       string. */
-    if(!state->data->state.up.path[1]) {
-      failf(data, "Missing filename");
-      return CURLE_TFTP_ILLEGAL;
-    }
-    result = Curl_urldecode(&state->data->state.up.path[1], 0,
-                            &filename, NULL, REJECT_ZERO);
-    if(result)
-      return result;
+      if(rblock != state->block &&
+         /* There is a bug in tftpd-hpa that causes it to send us an ack for
+          * 65535 when the block number wraps to 0. So when we are expecting
+          * 0, also accept 65535. See
+          * https://www.syslinux.org/archives/2010-September/015612.html
+          * */
+         !(state->block == 0 && rblock == 65535)) {
+        /* This is not the expected block. Log it and up the retry counter */
+        infof(data, "Received ACK for block %d, expecting %d",
+              rblock, state->block);
+        state->retries++;
+        /* Bail out if over the maximum */
+        if(state->retries > state->retry_max) {
+          failf(data, "tftp_tx: giving up waiting for block %d ack",
+                state->block);
+          result = CURLE_SEND_ERROR;
+        }
+        else {
+          /* Re-send the data packet */
+          sbytes = sendto(state->sockfd, (void *)state->spacket.data,
+                          4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG,
+                          (struct sockaddr *)&state->remote_addr,
+                          state->remote_addrlen);
+          /* Check all sbytes were sent */
+          if(sbytes < 0) {
+            failf(data, "%s",
+                  curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+            result = CURLE_SEND_ERROR;
+          }
+        }
 
-    if(strlen(filename) > (state->blksize - strlen(mode) - 4)) {
-      failf(data, "TFTP filename too long");
-      curlx_free(filename);
-      return CURLE_TFTP_ILLEGAL; /* too long filename field */
+        return result;
+      }
+      /* This is the expected packet. Reset the counters and send the next
+         block */
+      state->rx_time = time(NULL);
+      state->block++;
     }
+    else
+      state->block = 1; /* first data block is 1 when using OACK */
 
-    curl_msnprintf((char *)state->spacket.data + 2,
-                   state->blksize,
-                   "%s%c%s%c", filename, '\0', mode, '\0');
-    sbytes = 4 + strlen(filename) + strlen(mode);
-    curlx_free(filename);
-
-    /* optional addition of TFTP options */
-    if(!data->set.tftp_no_options) {
-      char buf[64];
-      /* add tsize option */
-      curl_msnprintf(buf, sizeof(buf), "%" FMT_OFF_T,
-                     data->state.upload && (data->state.infilesize != -1) ?
-                     data->state.infilesize : 0);
-
-      result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_TSIZE);
-      if(result == CURLE_OK)
-        result = tftp_option_add(state, &sbytes, sbytes, buf);
-
-      /* add blksize option */
-      curl_msnprintf(buf, sizeof(buf), "%d", state->requested_blksize);
-      if(result == CURLE_OK)
-        result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_BLKSIZE);
-      if(result == CURLE_OK)
-        result = tftp_option_add(state, &sbytes, sbytes, buf);
-
-      /* add timeout option */
-      curl_msnprintf(buf, sizeof(buf), "%d", state->retry_time);
-      if(result == CURLE_OK)
-        result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_INTERVAL);
-      if(result == CURLE_OK)
-        result = tftp_option_add(state, &sbytes, sbytes, buf);
-
-      if(result != CURLE_OK) {
-        failf(data, "TFTP buffer too small for options");
-        return CURLE_TFTP_ILLEGAL;
-      }
+    state->retries = 0;
+    setpacketevent(&state->spacket, TFTP_EVENT_DATA);
+    setpacketblock(&state->spacket, state->block);
+    if(state->block > 1 && state->sbytes < state->blksize) {
+      state->state = TFTP_STATE_FIN;
+      return CURLE_OK;
     }
 
-    /* the typecase for the 3rd argument is mostly for systems that do
-       not have a size_t argument, like older unixes that want an 'int' */
-#ifdef __AMIGA__
-#define CURL_SENDTO_ARG5(x) CURL_UNCONST(x)
-#else
-#define CURL_SENDTO_ARG5(x) (x)
-#endif
-    remote_addr = Curl_conn_get_remote_addr(data, FIRSTSOCKET);
-    if(!remote_addr)
-      return CURLE_FAILED_INIT;
+    /* TFTP considers data block size < 512 bytes as an end of session. So
+     * in some cases we must wait for additional data to build full (512 bytes)
+     * data block.
+     * */
+    state->sbytes = 0;
+    bufptr = (char *)state->spacket.data + 4;
+    do {
+      result = Curl_client_read(data, bufptr, state->blksize - state->sbytes,
+                                &cb, &eos);
+      if(result)
+        return result;
+      state->sbytes += cb;
+      bufptr += cb;
+    } while(state->sbytes < state->blksize && cb);
 
-    senddata = sendto(state->sockfd, (void *)state->spacket.data,
-                      (SEND_TYPE_ARG3)sbytes, 0,
-                      CURL_SENDTO_ARG5(&remote_addr->curl_sa_addr),
-                      (curl_socklen_t)remote_addr->addrlen);
-    if(senddata != (ssize_t)sbytes) {
-      char buffer[STRERROR_LEN];
+    sbytes = sendto(state->sockfd, (void *)state->spacket.data,
+                    4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG,
+                    (struct sockaddr *)&state->remote_addr,
+                    state->remote_addrlen);
+    /* Check all sbytes were sent */
+    if(sbytes < 0) {
       failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
       return CURLE_SEND_ERROR;
     }
+    /* Update the progress meter */
+    k->writebytecount += state->sbytes;
+    Curl_pgrs_upload_inc(data, state->sbytes);
     break;
 
-  case TFTP_EVENT_OACK:
-    if(data->state.upload) {
-      result = tftp_connect_for_tx(state, event);
+  case TFTP_EVENT_TIMEOUT:
+    /* Increment the retry counter and log the timeout */
+    state->retries++;
+    infof(data, "Timeout waiting for block %d ACK. "
+          " Retries = %d", NEXT_BLOCKNUM(state->block), state->retries);
+    /* Decide if we have had enough */
+    if(state->retries > state->retry_max) {
+      state->error = TFTP_ERR_TIMEOUT;
+      state->state = TFTP_STATE_FIN;
     }
     else {
-      result = tftp_connect_for_rx(state, event);
+      /* Re-send the data packet */
+      sbytes = sendto(state->sockfd, (void *)state->spacket.data,
+                      4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG,
+                      (struct sockaddr *)&state->remote_addr,
+                      state->remote_addrlen);
+      /* Check all sbytes were sent */
+      if(sbytes < 0) {
+        failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+        return CURLE_SEND_ERROR;
+      }
+      /* since this was a re-send, we remain at the still byte position */
+      Curl_pgrsSetUploadCounter(data, k->writebytecount);
     }
     break;
 
-  case TFTP_EVENT_ACK: /* Connected for transmit */
-    result = tftp_connect_for_tx(state, event);
-    break;
-
-  case TFTP_EVENT_DATA: /* Connected for receive */
-    result = tftp_connect_for_rx(state, event);
-    break;
-
   case TFTP_EVENT_ERROR:
     state->state = TFTP_STATE_FIN;
+    setpacketevent(&state->spacket, TFTP_EVENT_ERROR);
+    setpacketblock(&state->spacket, state->block);
+    (void)sendto(state->sockfd, (void *)state->spacket.data, 4, SEND_4TH_ARG,
+                 (struct sockaddr *)&state->remote_addr,
+                 state->remote_addrlen);
+    /* do not bother with the return code, but if the socket is still up we
+     * should be a good TFTP client and let the server know we are done */
+    state->state = TFTP_STATE_FIN;
     break;
 
   default:
-    failf(state->data, "tftp_send_first: internal error");
-    return CURLE_TFTP_ILLEGAL;
+    failf(data, "tftp_tx: internal error, event: %i", (int)(event));
+    break;
   }
 
   return result;
 }
 
-/* the next blocknum is x + 1 but it needs to wrap at an unsigned 16bit
-   boundary */
-#define NEXT_BLOCKNUM(x) (((x) + 1) & 0xffff)
+static CURLcode tftp_connect_for_tx(struct tftp_conn *state,
+                                    tftp_event_t event)
+{
+  CURLcode result;
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+  struct Curl_easy *data = state->data;
+
+  infof(data, "%s", "Connected for transmit");
+#endif
+  state->state = TFTP_STATE_TX;
+  result = tftp_set_timeouts(state);
+  if(result)
+    return result;
+  return tftp_tx(state, event);
+}
 
 /**********************************************************
  *
@@ -609,217 +556,67 @@ static CURLcode tftp_rx(struct tftp_conn *state, tftp_event_t event)
                     state->remote_addrlen);
     if(sbytes < 0) {
       failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
-      return CURLE_SEND_ERROR;
-    }
-
-    /* Check if completed (That is, a less than full packet is received) */
-    if(state->rbytes < (ssize_t)state->blksize + 4) {
-      state->state = TFTP_STATE_FIN;
-    }
-    else {
-      state->state = TFTP_STATE_RX;
-    }
-    state->rx_time = time(NULL);
-    break;
-
-  case TFTP_EVENT_OACK:
-    /* ACK option acknowledgement so we can move on to data */
-    state->block = 0;
-    state->retries = 0;
-    setpacketevent(&state->spacket, TFTP_EVENT_ACK);
-    setpacketblock(&state->spacket, state->block);
-    sbytes = sendto(state->sockfd, (void *)state->spacket.data,
-                    4, SEND_4TH_ARG,
-                    (struct sockaddr *)&state->remote_addr,
-                    state->remote_addrlen);
-    if(sbytes < 0) {
-      failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
-      return CURLE_SEND_ERROR;
-    }
-
-    /* we are ready to RX data */
-    state->state = TFTP_STATE_RX;
-    state->rx_time = time(NULL);
-    break;
-
-  case TFTP_EVENT_TIMEOUT:
-    /* Increment the retry count and fail if over the limit */
-    state->retries++;
-    infof(data,
-          "Timeout waiting for block %d ACK. Retries = %d",
-          NEXT_BLOCKNUM(state->block), state->retries);
-    if(state->retries > state->retry_max) {
-      state->error = TFTP_ERR_TIMEOUT;
-      state->state = TFTP_STATE_FIN;
-    }
-    else {
-      /* Resend the previous ACK */
-      sbytes = sendto(state->sockfd, (void *)state->spacket.data,
-                      4, SEND_4TH_ARG,
-                      (struct sockaddr *)&state->remote_addr,
-                      state->remote_addrlen);
-      if(sbytes < 0) {
-        failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
-        return CURLE_SEND_ERROR;
-      }
-    }
-    break;
-
-  case TFTP_EVENT_ERROR:
-    setpacketevent(&state->spacket, TFTP_EVENT_ERROR);
-    setpacketblock(&state->spacket, state->block);
-    (void)sendto(state->sockfd, (void *)state->spacket.data,
-                 4, SEND_4TH_ARG,
-                 (struct sockaddr *)&state->remote_addr,
-                 state->remote_addrlen);
-    /* do not bother with the return code, but if the socket is still up we
-     * should be a good TFTP client and let the server know we are done */
-    state->state = TFTP_STATE_FIN;
-    break;
-
-  default:
-    failf(data, "%s", "tftp_rx: internal error");
-    return CURLE_TFTP_ILLEGAL; /* not really the perfect return code for
-                                  this */
-  }
-  return CURLE_OK;
-}
-
-/**********************************************************
- *
- * tftp_tx
- *
- * Event handler for the TX state
- *
- **********************************************************/
-static CURLcode tftp_tx(struct tftp_conn *state, tftp_event_t event)
-{
-  struct Curl_easy *data = state->data;
-  ssize_t sbytes;
-  CURLcode result = CURLE_OK;
-  struct SingleRequest *k = &data->req;
-  size_t cb; /* Bytes currently read */
-  char buffer[STRERROR_LEN];
-  char *bufptr;
-  bool eos;
-
-  switch(event) {
-
-  case TFTP_EVENT_ACK:
-  case TFTP_EVENT_OACK:
-    if(event == TFTP_EVENT_ACK) {
-      /* Ack the packet */
-      int rblock = getrpacketblock(&state->rpacket);
-
-      if(rblock != state->block &&
-         /* There is a bug in tftpd-hpa that causes it to send us an ack for
-          * 65535 when the block number wraps to 0. So when we are expecting
-          * 0, also accept 65535. See
-          * https://www.syslinux.org/archives/2010-September/015612.html
-          * */
-         !(state->block == 0 && rblock == 65535)) {
-        /* This is not the expected block. Log it and up the retry counter */
-        infof(data, "Received ACK for block %d, expecting %d",
-              rblock, state->block);
-        state->retries++;
-        /* Bail out if over the maximum */
-        if(state->retries > state->retry_max) {
-          failf(data, "tftp_tx: giving up waiting for block %d ack",
-                state->block);
-          result = CURLE_SEND_ERROR;
-        }
-        else {
-          /* Re-send the data packet */
-          sbytes = sendto(state->sockfd, (void *)state->spacket.data,
-                          4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG,
-                          (struct sockaddr *)&state->remote_addr,
-                          state->remote_addrlen);
-          /* Check all sbytes were sent */
-          if(sbytes < 0) {
-            failf(data, "%s",
-                  curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
-            result = CURLE_SEND_ERROR;
-          }
-        }
-
-        return result;
-      }
-      /* This is the expected packet. Reset the counters and send the next
-         block */
-      state->rx_time = time(NULL);
-      state->block++;
+      return CURLE_SEND_ERROR;
     }
-    else
-      state->block = 1; /* first data block is 1 when using OACK */
 
-    state->retries = 0;
-    setpacketevent(&state->spacket, TFTP_EVENT_DATA);
-    setpacketblock(&state->spacket, state->block);
-    if(state->block > 1 && state->sbytes < state->blksize) {
+    /* Check if completed (That is, a less than full packet is received) */
+    if(state->rbytes < (ssize_t)state->blksize + 4) {
       state->state = TFTP_STATE_FIN;
-      return CURLE_OK;
     }
+    else {
+      state->state = TFTP_STATE_RX;
+    }
+    state->rx_time = time(NULL);
+    break;
 
-    /* TFTP considers data block size < 512 bytes as an end of session. So
-     * in some cases we must wait for additional data to build full (512 bytes)
-     * data block.
-     * */
-    state->sbytes = 0;
-    bufptr = (char *)state->spacket.data + 4;
-    do {
-      result = Curl_client_read(data, bufptr, state->blksize - state->sbytes,
-                                &cb, &eos);
-      if(result)
-        return result;
-      state->sbytes += cb;
-      bufptr += cb;
-    } while(state->sbytes < state->blksize && cb);
-
+  case TFTP_EVENT_OACK:
+    /* ACK option acknowledgement so we can move on to data */
+    state->block = 0;
+    state->retries = 0;
+    setpacketevent(&state->spacket, TFTP_EVENT_ACK);
+    setpacketblock(&state->spacket, state->block);
     sbytes = sendto(state->sockfd, (void *)state->spacket.data,
-                    4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG,
+                    4, SEND_4TH_ARG,
                     (struct sockaddr *)&state->remote_addr,
                     state->remote_addrlen);
-    /* Check all sbytes were sent */
     if(sbytes < 0) {
       failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
       return CURLE_SEND_ERROR;
     }
-    /* Update the progress meter */
-    k->writebytecount += state->sbytes;
-    Curl_pgrs_upload_inc(data, state->sbytes);
+
+    /* we are ready to RX data */
+    state->state = TFTP_STATE_RX;
+    state->rx_time = time(NULL);
     break;
 
   case TFTP_EVENT_TIMEOUT:
-    /* Increment the retry counter and log the timeout */
+    /* Increment the retry count and fail if over the limit */
     state->retries++;
-    infof(data, "Timeout waiting for block %d ACK. "
-          " Retries = %d", NEXT_BLOCKNUM(state->block), state->retries);
-    /* Decide if we have had enough */
+    infof(data,
+          "Timeout waiting for block %d ACK. Retries = %d",
+          NEXT_BLOCKNUM(state->block), state->retries);
     if(state->retries > state->retry_max) {
       state->error = TFTP_ERR_TIMEOUT;
       state->state = TFTP_STATE_FIN;
     }
     else {
-      /* Re-send the data packet */
+      /* Resend the previous ACK */
       sbytes = sendto(state->sockfd, (void *)state->spacket.data,
-                      4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG,
+                      4, SEND_4TH_ARG,
                       (struct sockaddr *)&state->remote_addr,
                       state->remote_addrlen);
-      /* Check all sbytes were sent */
       if(sbytes < 0) {
         failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
         return CURLE_SEND_ERROR;
       }
-      /* since this was a re-send, we remain at the still byte position */
-      Curl_pgrsSetUploadCounter(data, k->writebytecount);
     }
     break;
 
   case TFTP_EVENT_ERROR:
-    state->state = TFTP_STATE_FIN;
     setpacketevent(&state->spacket, TFTP_EVENT_ERROR);
     setpacketblock(&state->spacket, state->block);
-    (void)sendto(state->sockfd, (void *)state->spacket.data, 4, SEND_4TH_ARG,
+    (void)sendto(state->sockfd, (void *)state->spacket.data,
+                 4, SEND_4TH_ARG,
                  (struct sockaddr *)&state->remote_addr,
                  state->remote_addrlen);
     /* do not bother with the return code, but if the socket is still up we
@@ -828,8 +625,168 @@ static CURLcode tftp_tx(struct tftp_conn *state, tftp_event_t event)
     break;
 
   default:
-    failf(data, "tftp_tx: internal error, event: %i", (int)(event));
+    failf(data, "%s", "tftp_rx: internal error");
+    return CURLE_TFTP_ILLEGAL; /* not really the perfect return code for
+                                  this */
+  }
+  return CURLE_OK;
+}
+
+static CURLcode tftp_connect_for_rx(struct tftp_conn *state,
+                                    tftp_event_t event)
+{
+  CURLcode result;
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+  struct Curl_easy *data = state->data;
+
+  infof(data, "%s", "Connected for receive");
+#endif
+  state->state = TFTP_STATE_RX;
+  result = tftp_set_timeouts(state);
+  if(result)
+    return result;
+  return tftp_rx(state, event);
+}
+
+static CURLcode tftp_send_first(struct tftp_conn *state,
+                                tftp_event_t event)
+{
+  size_t sbytes;
+  ssize_t senddata;
+  const char *mode = "octet";
+  char *filename;
+  struct Curl_easy *data = state->data;
+  const struct Curl_sockaddr_ex *remote_addr = NULL;
+  CURLcode result = CURLE_OK;
+
+  /* Set ASCII mode if -B flag was used */
+  if(data->state.prefer_ascii)
+    mode = "netascii";
+
+  switch(event) {
+
+  case TFTP_EVENT_INIT:    /* Send the first packet out */
+  case TFTP_EVENT_TIMEOUT: /* Resend the first packet out */
+    /* Increment the retry counter, quit if over the limit */
+    state->retries++;
+    if(state->retries > state->retry_max) {
+      state->error = TFTP_ERR_NORESPONSE;
+      state->state = TFTP_STATE_FIN;
+      return result;
+    }
+
+    if(data->state.upload) {
+      /* If we are uploading, send an WRQ */
+      setpacketevent(&state->spacket, TFTP_EVENT_WRQ);
+      if(data->state.infilesize != -1)
+        Curl_pgrsSetUploadSize(data, data->state.infilesize);
+    }
+    else {
+      /* If we are downloading, send an RRQ */
+      setpacketevent(&state->spacket, TFTP_EVENT_RRQ);
+    }
+    /* As RFC3617 describes the separator slash is not actually part of the
+       filename so we skip the always-present first letter of the path
+       string. */
+    if(!state->data->state.up.path[1]) {
+      failf(data, "Missing filename");
+      return CURLE_TFTP_ILLEGAL;
+    }
+    result = Curl_urldecode(&state->data->state.up.path[1], 0,
+                            &filename, NULL, REJECT_ZERO);
+    if(result)
+      return result;
+
+    if(strlen(filename) > (state->blksize - strlen(mode) - 4)) {
+      failf(data, "TFTP filename too long");
+      curlx_free(filename);
+      return CURLE_TFTP_ILLEGAL; /* too long filename field */
+    }
+
+    curl_msnprintf((char *)state->spacket.data + 2,
+                   state->blksize,
+                   "%s%c%s%c", filename, '\0', mode, '\0');
+    sbytes = 4 + strlen(filename) + strlen(mode);
+    curlx_free(filename);
+
+    /* optional addition of TFTP options */
+    if(!data->set.tftp_no_options) {
+      char buf[64];
+      /* add tsize option */
+      curl_msnprintf(buf, sizeof(buf), "%" FMT_OFF_T,
+                     data->state.upload && (data->state.infilesize != -1) ?
+                     data->state.infilesize : 0);
+
+      result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_TSIZE);
+      if(result == CURLE_OK)
+        result = tftp_option_add(state, &sbytes, sbytes, buf);
+
+      /* add blksize option */
+      curl_msnprintf(buf, sizeof(buf), "%d", state->requested_blksize);
+      if(result == CURLE_OK)
+        result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_BLKSIZE);
+      if(result == CURLE_OK)
+        result = tftp_option_add(state, &sbytes, sbytes, buf);
+
+      /* add timeout option */
+      curl_msnprintf(buf, sizeof(buf), "%d", state->retry_time);
+      if(result == CURLE_OK)
+        result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_INTERVAL);
+      if(result == CURLE_OK)
+        result = tftp_option_add(state, &sbytes, sbytes, buf);
+
+      if(result != CURLE_OK) {
+        failf(data, "TFTP buffer too small for options");
+        return CURLE_TFTP_ILLEGAL;
+      }
+    }
+
+    /* the typecase for the 3rd argument is mostly for systems that do
+       not have a size_t argument, like older unixes that want an 'int' */
+#ifdef __AMIGA__
+#define CURL_SENDTO_ARG5(x) CURL_UNCONST(x)
+#else
+#define CURL_SENDTO_ARG5(x) (x)
+#endif
+    remote_addr = Curl_conn_get_remote_addr(data, FIRSTSOCKET);
+    if(!remote_addr)
+      return CURLE_FAILED_INIT;
+
+    senddata = sendto(state->sockfd, (void *)state->spacket.data,
+                      (SEND_TYPE_ARG3)sbytes, 0,
+                      CURL_SENDTO_ARG5(&remote_addr->curl_sa_addr),
+                      (curl_socklen_t)remote_addr->addrlen);
+    if(senddata != (ssize_t)sbytes) {
+      char buffer[STRERROR_LEN];
+      failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+      return CURLE_SEND_ERROR;
+    }
+    break;
+
+  case TFTP_EVENT_OACK:
+    if(data->state.upload) {
+      result = tftp_connect_for_tx(state, event);
+    }
+    else {
+      result = tftp_connect_for_rx(state, event);
+    }
+    break;
+
+  case TFTP_EVENT_ACK: /* Connected for transmit */
+    result = tftp_connect_for_tx(state, event);
+    break;
+
+  case TFTP_EVENT_DATA: /* Connected for receive */
+    result = tftp_connect_for_rx(state, event);
+    break;
+
+  case TFTP_EVENT_ERROR:
+    state->state = TFTP_STATE_FIN;
     break;
+
+  default:
+    failf(state->data, "tftp_send_first: internal error");
+    return CURLE_TFTP_ILLEGAL;
   }
 
   return result;
@@ -1377,4 +1334,33 @@ static CURLcode tftp_setup_connection(struct Curl_easy *data,
 
   return CURLE_OK;
 }
+
+/*
+ * TFTP protocol handler.
+ */
+const struct Curl_handler Curl_handler_tftp = {
+  "tftp",                               /* scheme */
+  tftp_setup_connection,                /* setup_connection */
+  tftp_do,                              /* do_it */
+  tftp_done,                            /* done */
+  ZERO_NULL,                            /* do_more */
+  tftp_connect,                         /* connect_it */
+  tftp_multi_statemach,                 /* connecting */
+  tftp_doing,                           /* doing */
+  tftp_pollset,                         /* proto_pollset */
+  tftp_pollset,                         /* doing_pollset */
+  ZERO_NULL,                            /* domore_pollset */
+  ZERO_NULL,                            /* perform_pollset */
+  ZERO_NULL,                            /* disconnect */
+  ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
+  ZERO_NULL,                            /* connection_check */
+  ZERO_NULL,                            /* attach connection */
+  ZERO_NULL,                            /* follow */
+  PORT_TFTP,                            /* defport */
+  CURLPROTO_TFTP,                       /* protocol */
+  CURLPROTO_TFTP,                       /* family */
+  PROTOPT_NOTCPPROXY | PROTOPT_NOURLQUERY /* flags */
+};
+
 #endif