gopher.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. /***************************************************************************
  2. * _ _ ____ _
  3. * Project ___| | | | _ \| |
  4. * / __| | | | |_) | |
  5. * | (__| |_| | _ <| |___
  6. * \___|\___/|_| \_\_____|
  7. *
  8. * Copyright (C) Daniel Stenberg, <[email protected]>, et al.
  9. *
  10. * This software is licensed as described in the file COPYING, which
  11. * you should have received as part of this distribution. The terms
  12. * are also available at https://curl.se/docs/copyright.html.
  13. *
  14. * You may opt to use, copy, modify, merge, publish, distribute and/or sell
  15. * copies of the Software, and permit persons to whom the Software is
  16. * furnished to do so, under the terms of the COPYING file.
  17. *
  18. * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  19. * KIND, either express or implied.
  20. *
  21. * SPDX-License-Identifier: curl
  22. *
  23. ***************************************************************************/
  24. #include "curl_setup.h"
  25. #ifndef CURL_DISABLE_GOPHER
  26. #include "urldata.h"
  27. #include "transfer.h"
  28. #include "sendf.h"
  29. #include "curl_trc.h"
  30. #include "cfilters.h"
  31. #include "connect.h"
  32. #include "gopher.h"
  33. #include "select.h"
  34. #include "url.h"
  35. #include "escape.h"
  36. /*
  37. * Forward declarations.
  38. */
  39. static CURLcode gopher_do(struct Curl_easy *data, bool *done);
  40. #ifdef USE_SSL
  41. static CURLcode gopher_connect(struct Curl_easy *data, bool *done);
  42. static CURLcode gopher_connecting(struct Curl_easy *data, bool *done);
  43. #endif
  44. /*
  45. * Gopher protocol handler.
  46. * This is also a nice simple template to build off for simple
  47. * connect-command-download protocols.
  48. */
  49. const struct Curl_handler Curl_handler_gopher = {
  50. "gopher", /* scheme */
  51. ZERO_NULL, /* setup_connection */
  52. gopher_do, /* do_it */
  53. ZERO_NULL, /* done */
  54. ZERO_NULL, /* do_more */
  55. ZERO_NULL, /* connect_it */
  56. ZERO_NULL, /* connecting */
  57. ZERO_NULL, /* doing */
  58. ZERO_NULL, /* proto_pollset */
  59. ZERO_NULL, /* doing_pollset */
  60. ZERO_NULL, /* domore_pollset */
  61. ZERO_NULL, /* perform_pollset */
  62. ZERO_NULL, /* disconnect */
  63. ZERO_NULL, /* write_resp */
  64. ZERO_NULL, /* write_resp_hd */
  65. ZERO_NULL, /* connection_check */
  66. ZERO_NULL, /* attach connection */
  67. ZERO_NULL, /* follow */
  68. PORT_GOPHER, /* defport */
  69. CURLPROTO_GOPHER, /* protocol */
  70. CURLPROTO_GOPHER, /* family */
  71. PROTOPT_NONE /* flags */
  72. };
  73. #ifdef USE_SSL
  74. const struct Curl_handler Curl_handler_gophers = {
  75. "gophers", /* scheme */
  76. ZERO_NULL, /* setup_connection */
  77. gopher_do, /* do_it */
  78. ZERO_NULL, /* done */
  79. ZERO_NULL, /* do_more */
  80. gopher_connect, /* connect_it */
  81. gopher_connecting, /* connecting */
  82. ZERO_NULL, /* doing */
  83. ZERO_NULL, /* proto_pollset */
  84. ZERO_NULL, /* doing_pollset */
  85. ZERO_NULL, /* domore_pollset */
  86. ZERO_NULL, /* perform_pollset */
  87. ZERO_NULL, /* disconnect */
  88. ZERO_NULL, /* write_resp */
  89. ZERO_NULL, /* write_resp_hd */
  90. ZERO_NULL, /* connection_check */
  91. ZERO_NULL, /* attach connection */
  92. ZERO_NULL, /* follow */
  93. PORT_GOPHER, /* defport */
  94. CURLPROTO_GOPHERS, /* protocol */
  95. CURLPROTO_GOPHER, /* family */
  96. PROTOPT_SSL /* flags */
  97. };
  98. static CURLcode gopher_connect(struct Curl_easy *data, bool *done)
  99. {
  100. (void)data;
  101. (void)done;
  102. return CURLE_OK;
  103. }
  104. static CURLcode gopher_connecting(struct Curl_easy *data, bool *done)
  105. {
  106. struct connectdata *conn = data->conn;
  107. CURLcode result;
  108. result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done);
  109. if(result)
  110. connclose(conn, "Failed TLS connection");
  111. *done = TRUE;
  112. return result;
  113. }
  114. #endif
  115. static CURLcode gopher_do(struct Curl_easy *data, bool *done)
  116. {
  117. CURLcode result = CURLE_OK;
  118. struct connectdata *conn = data->conn;
  119. curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
  120. char *gopherpath;
  121. char *path = data->state.up.path;
  122. char *query = data->state.up.query;
  123. const char *buf = NULL;
  124. char *buf_alloc = NULL;
  125. size_t nwritten, buf_len;
  126. timediff_t timeout_ms;
  127. int what;
  128. *done = TRUE; /* unconditionally */
  129. /* path is guaranteed non-NULL */
  130. DEBUGASSERT(path);
  131. if(query)
  132. gopherpath = curl_maprintf("%s?%s", path, query);
  133. else
  134. gopherpath = curlx_strdup(path);
  135. if(!gopherpath)
  136. return CURLE_OUT_OF_MEMORY;
  137. /* Create selector. Degenerate cases: / and /1 => convert to "" */
  138. if(strlen(gopherpath) <= 2) {
  139. buf = "";
  140. buf_len = 0;
  141. curlx_free(gopherpath);
  142. }
  143. else {
  144. char *newp;
  145. /* Otherwise, drop / and the first character (i.e., item type) ... */
  146. newp = gopherpath;
  147. newp += 2;
  148. /* ... and finally unescape */
  149. result = Curl_urldecode(newp, 0, &buf_alloc, &buf_len, REJECT_ZERO);
  150. curlx_free(gopherpath);
  151. if(result)
  152. return result;
  153. buf = buf_alloc;
  154. }
  155. for(; buf_len;) {
  156. result = Curl_xfer_send(data, buf, buf_len, FALSE, &nwritten);
  157. if(!result) { /* Which may not have written it all! */
  158. result = Curl_client_write(data, CLIENTWRITE_HEADER, buf, nwritten);
  159. if(result)
  160. break;
  161. if(nwritten > buf_len) {
  162. DEBUGASSERT(0);
  163. break;
  164. }
  165. buf_len -= nwritten;
  166. buf += nwritten;
  167. if(!buf_len)
  168. break; /* but it did write it all */
  169. }
  170. else
  171. break;
  172. timeout_ms = Curl_timeleft_ms(data, FALSE);
  173. if(timeout_ms < 0) {
  174. result = CURLE_OPERATION_TIMEDOUT;
  175. break;
  176. }
  177. if(!timeout_ms)
  178. timeout_ms = TIMEDIFF_T_MAX;
  179. /* Do not busyloop. The entire loop thing is a work-around as it causes a
  180. BLOCKING behavior which is a NO-NO. This function should rather be
  181. split up in a do and a doing piece where the pieces that are not
  182. possible to send now will be sent in the doing function repeatedly
  183. until the entire request is sent.
  184. */
  185. what = SOCKET_WRITABLE(sockfd, timeout_ms);
  186. if(what < 0) {
  187. result = CURLE_SEND_ERROR;
  188. break;
  189. }
  190. else if(!what) {
  191. result = CURLE_OPERATION_TIMEDOUT;
  192. break;
  193. }
  194. }
  195. curlx_free(buf_alloc);
  196. if(!result)
  197. result = Curl_xfer_send(data, "\r\n", 2, FALSE, &nwritten);
  198. if(result) {
  199. failf(data, "Failed sending Gopher request");
  200. return result;
  201. }
  202. result = Curl_client_write(data, CLIENTWRITE_HEADER, "\r\n", 2);
  203. if(result)
  204. return result;
  205. Curl_xfer_setup_recv(data, FIRSTSOCKET, -1);
  206. return CURLE_OK;
  207. }
  208. #endif /* CURL_DISABLE_GOPHER */