xref: /third_party/curl/lib/gopher.c (revision 13498266)
113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci ***************************************************************************/
2413498266Sopenharmony_ci
2513498266Sopenharmony_ci#include "curl_setup.h"
2613498266Sopenharmony_ci
2713498266Sopenharmony_ci#ifndef CURL_DISABLE_GOPHER
2813498266Sopenharmony_ci
2913498266Sopenharmony_ci#include "urldata.h"
3013498266Sopenharmony_ci#include <curl/curl.h>
3113498266Sopenharmony_ci#include "transfer.h"
3213498266Sopenharmony_ci#include "sendf.h"
3313498266Sopenharmony_ci#include "cfilters.h"
3413498266Sopenharmony_ci#include "connect.h"
3513498266Sopenharmony_ci#include "progress.h"
3613498266Sopenharmony_ci#include "gopher.h"
3713498266Sopenharmony_ci#include "select.h"
3813498266Sopenharmony_ci#include "strdup.h"
3913498266Sopenharmony_ci#include "vtls/vtls.h"
4013498266Sopenharmony_ci#include "url.h"
4113498266Sopenharmony_ci#include "escape.h"
4213498266Sopenharmony_ci#include "warnless.h"
4313498266Sopenharmony_ci#include "curl_printf.h"
4413498266Sopenharmony_ci#include "curl_memory.h"
4513498266Sopenharmony_ci/* The last #include file should be: */
4613498266Sopenharmony_ci#include "memdebug.h"
4713498266Sopenharmony_ci
4813498266Sopenharmony_ci/*
4913498266Sopenharmony_ci * Forward declarations.
5013498266Sopenharmony_ci */
5113498266Sopenharmony_ci
5213498266Sopenharmony_cistatic CURLcode gopher_do(struct Curl_easy *data, bool *done);
5313498266Sopenharmony_ci#ifdef USE_SSL
5413498266Sopenharmony_cistatic CURLcode gopher_connect(struct Curl_easy *data, bool *done);
5513498266Sopenharmony_cistatic CURLcode gopher_connecting(struct Curl_easy *data, bool *done);
5613498266Sopenharmony_ci#endif
5713498266Sopenharmony_ci
5813498266Sopenharmony_ci/*
5913498266Sopenharmony_ci * Gopher protocol handler.
6013498266Sopenharmony_ci * This is also a nice simple template to build off for simple
6113498266Sopenharmony_ci * connect-command-download protocols.
6213498266Sopenharmony_ci */
6313498266Sopenharmony_ci
6413498266Sopenharmony_ciconst struct Curl_handler Curl_handler_gopher = {
6513498266Sopenharmony_ci  "GOPHER",                             /* scheme */
6613498266Sopenharmony_ci  ZERO_NULL,                            /* setup_connection */
6713498266Sopenharmony_ci  gopher_do,                            /* do_it */
6813498266Sopenharmony_ci  ZERO_NULL,                            /* done */
6913498266Sopenharmony_ci  ZERO_NULL,                            /* do_more */
7013498266Sopenharmony_ci  ZERO_NULL,                            /* connect_it */
7113498266Sopenharmony_ci  ZERO_NULL,                            /* connecting */
7213498266Sopenharmony_ci  ZERO_NULL,                            /* doing */
7313498266Sopenharmony_ci  ZERO_NULL,                            /* proto_getsock */
7413498266Sopenharmony_ci  ZERO_NULL,                            /* doing_getsock */
7513498266Sopenharmony_ci  ZERO_NULL,                            /* domore_getsock */
7613498266Sopenharmony_ci  ZERO_NULL,                            /* perform_getsock */
7713498266Sopenharmony_ci  ZERO_NULL,                            /* disconnect */
7813498266Sopenharmony_ci  ZERO_NULL,                            /* write_resp */
7913498266Sopenharmony_ci  ZERO_NULL,                            /* connection_check */
8013498266Sopenharmony_ci  ZERO_NULL,                            /* attach connection */
8113498266Sopenharmony_ci  PORT_GOPHER,                          /* defport */
8213498266Sopenharmony_ci  CURLPROTO_GOPHER,                     /* protocol */
8313498266Sopenharmony_ci  CURLPROTO_GOPHER,                     /* family */
8413498266Sopenharmony_ci  PROTOPT_NONE                          /* flags */
8513498266Sopenharmony_ci};
8613498266Sopenharmony_ci
8713498266Sopenharmony_ci#ifdef USE_SSL
8813498266Sopenharmony_ciconst struct Curl_handler Curl_handler_gophers = {
8913498266Sopenharmony_ci  "GOPHERS",                            /* scheme */
9013498266Sopenharmony_ci  ZERO_NULL,                            /* setup_connection */
9113498266Sopenharmony_ci  gopher_do,                            /* do_it */
9213498266Sopenharmony_ci  ZERO_NULL,                            /* done */
9313498266Sopenharmony_ci  ZERO_NULL,                            /* do_more */
9413498266Sopenharmony_ci  gopher_connect,                       /* connect_it */
9513498266Sopenharmony_ci  gopher_connecting,                    /* connecting */
9613498266Sopenharmony_ci  ZERO_NULL,                            /* doing */
9713498266Sopenharmony_ci  ZERO_NULL,                            /* proto_getsock */
9813498266Sopenharmony_ci  ZERO_NULL,                            /* doing_getsock */
9913498266Sopenharmony_ci  ZERO_NULL,                            /* domore_getsock */
10013498266Sopenharmony_ci  ZERO_NULL,                            /* perform_getsock */
10113498266Sopenharmony_ci  ZERO_NULL,                            /* disconnect */
10213498266Sopenharmony_ci  ZERO_NULL,                            /* write_resp */
10313498266Sopenharmony_ci  ZERO_NULL,                            /* connection_check */
10413498266Sopenharmony_ci  ZERO_NULL,                            /* attach connection */
10513498266Sopenharmony_ci  PORT_GOPHER,                          /* defport */
10613498266Sopenharmony_ci  CURLPROTO_GOPHERS,                    /* protocol */
10713498266Sopenharmony_ci  CURLPROTO_GOPHER,                     /* family */
10813498266Sopenharmony_ci  PROTOPT_SSL                           /* flags */
10913498266Sopenharmony_ci};
11013498266Sopenharmony_ci
11113498266Sopenharmony_cistatic CURLcode gopher_connect(struct Curl_easy *data, bool *done)
11213498266Sopenharmony_ci{
11313498266Sopenharmony_ci  (void)data;
11413498266Sopenharmony_ci  (void)done;
11513498266Sopenharmony_ci  return CURLE_OK;
11613498266Sopenharmony_ci}
11713498266Sopenharmony_ci
11813498266Sopenharmony_cistatic CURLcode gopher_connecting(struct Curl_easy *data, bool *done)
11913498266Sopenharmony_ci{
12013498266Sopenharmony_ci  struct connectdata *conn = data->conn;
12113498266Sopenharmony_ci  CURLcode result;
12213498266Sopenharmony_ci
12313498266Sopenharmony_ci  result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done);
12413498266Sopenharmony_ci  if(result)
12513498266Sopenharmony_ci    connclose(conn, "Failed TLS connection");
12613498266Sopenharmony_ci  *done = TRUE;
12713498266Sopenharmony_ci  return result;
12813498266Sopenharmony_ci}
12913498266Sopenharmony_ci#endif
13013498266Sopenharmony_ci
13113498266Sopenharmony_cistatic CURLcode gopher_do(struct Curl_easy *data, bool *done)
13213498266Sopenharmony_ci{
13313498266Sopenharmony_ci  CURLcode result = CURLE_OK;
13413498266Sopenharmony_ci  struct connectdata *conn = data->conn;
13513498266Sopenharmony_ci  curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
13613498266Sopenharmony_ci  char *gopherpath;
13713498266Sopenharmony_ci  char *path = data->state.up.path;
13813498266Sopenharmony_ci  char *query = data->state.up.query;
13913498266Sopenharmony_ci  char *sel = NULL;
14013498266Sopenharmony_ci  char *sel_org = NULL;
14113498266Sopenharmony_ci  timediff_t timeout_ms;
14213498266Sopenharmony_ci  ssize_t amount, k;
14313498266Sopenharmony_ci  size_t len;
14413498266Sopenharmony_ci  int what;
14513498266Sopenharmony_ci
14613498266Sopenharmony_ci  *done = TRUE; /* unconditionally */
14713498266Sopenharmony_ci
14813498266Sopenharmony_ci  /* path is guaranteed non-NULL */
14913498266Sopenharmony_ci  DEBUGASSERT(path);
15013498266Sopenharmony_ci
15113498266Sopenharmony_ci  if(query)
15213498266Sopenharmony_ci    gopherpath = aprintf("%s?%s", path, query);
15313498266Sopenharmony_ci  else
15413498266Sopenharmony_ci    gopherpath = strdup(path);
15513498266Sopenharmony_ci
15613498266Sopenharmony_ci  if(!gopherpath)
15713498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
15813498266Sopenharmony_ci
15913498266Sopenharmony_ci  /* Create selector. Degenerate cases: / and /1 => convert to "" */
16013498266Sopenharmony_ci  if(strlen(gopherpath) <= 2) {
16113498266Sopenharmony_ci    sel = (char *)"";
16213498266Sopenharmony_ci    len = strlen(sel);
16313498266Sopenharmony_ci    free(gopherpath);
16413498266Sopenharmony_ci  }
16513498266Sopenharmony_ci  else {
16613498266Sopenharmony_ci    char *newp;
16713498266Sopenharmony_ci
16813498266Sopenharmony_ci    /* Otherwise, drop / and the first character (i.e., item type) ... */
16913498266Sopenharmony_ci    newp = gopherpath;
17013498266Sopenharmony_ci    newp += 2;
17113498266Sopenharmony_ci
17213498266Sopenharmony_ci    /* ... and finally unescape */
17313498266Sopenharmony_ci    result = Curl_urldecode(newp, 0, &sel, &len, REJECT_ZERO);
17413498266Sopenharmony_ci    free(gopherpath);
17513498266Sopenharmony_ci    if(result)
17613498266Sopenharmony_ci      return result;
17713498266Sopenharmony_ci    sel_org = sel;
17813498266Sopenharmony_ci  }
17913498266Sopenharmony_ci
18013498266Sopenharmony_ci  k = curlx_uztosz(len);
18113498266Sopenharmony_ci
18213498266Sopenharmony_ci  for(;;) {
18313498266Sopenharmony_ci    /* Break out of the loop if the selector is empty because OpenSSL and/or
18413498266Sopenharmony_ci       LibreSSL fail with errno 0 if this is the case. */
18513498266Sopenharmony_ci    if(strlen(sel) < 1)
18613498266Sopenharmony_ci      break;
18713498266Sopenharmony_ci
18813498266Sopenharmony_ci    result = Curl_nwrite(data, FIRSTSOCKET, sel, k, &amount);
18913498266Sopenharmony_ci    if(!result) { /* Which may not have written it all! */
19013498266Sopenharmony_ci      result = Curl_client_write(data, CLIENTWRITE_HEADER, sel, amount);
19113498266Sopenharmony_ci      if(result)
19213498266Sopenharmony_ci        break;
19313498266Sopenharmony_ci
19413498266Sopenharmony_ci      k -= amount;
19513498266Sopenharmony_ci      sel += amount;
19613498266Sopenharmony_ci      if(k < 1)
19713498266Sopenharmony_ci        break; /* but it did write it all */
19813498266Sopenharmony_ci    }
19913498266Sopenharmony_ci    else
20013498266Sopenharmony_ci      break;
20113498266Sopenharmony_ci
20213498266Sopenharmony_ci    timeout_ms = Curl_timeleft(data, NULL, FALSE);
20313498266Sopenharmony_ci    if(timeout_ms < 0) {
20413498266Sopenharmony_ci      result = CURLE_OPERATION_TIMEDOUT;
20513498266Sopenharmony_ci      break;
20613498266Sopenharmony_ci    }
20713498266Sopenharmony_ci    if(!timeout_ms)
20813498266Sopenharmony_ci      timeout_ms = TIMEDIFF_T_MAX;
20913498266Sopenharmony_ci
21013498266Sopenharmony_ci    /* Don't busyloop. The entire loop thing is a work-around as it causes a
21113498266Sopenharmony_ci       BLOCKING behavior which is a NO-NO. This function should rather be
21213498266Sopenharmony_ci       split up in a do and a doing piece where the pieces that aren't
21313498266Sopenharmony_ci       possible to send now will be sent in the doing function repeatedly
21413498266Sopenharmony_ci       until the entire request is sent.
21513498266Sopenharmony_ci    */
21613498266Sopenharmony_ci    what = SOCKET_WRITABLE(sockfd, timeout_ms);
21713498266Sopenharmony_ci    if(what < 0) {
21813498266Sopenharmony_ci      result = CURLE_SEND_ERROR;
21913498266Sopenharmony_ci      break;
22013498266Sopenharmony_ci    }
22113498266Sopenharmony_ci    else if(!what) {
22213498266Sopenharmony_ci      result = CURLE_OPERATION_TIMEDOUT;
22313498266Sopenharmony_ci      break;
22413498266Sopenharmony_ci    }
22513498266Sopenharmony_ci  }
22613498266Sopenharmony_ci
22713498266Sopenharmony_ci  free(sel_org);
22813498266Sopenharmony_ci
22913498266Sopenharmony_ci  if(!result)
23013498266Sopenharmony_ci    result = Curl_nwrite(data, FIRSTSOCKET, "\r\n", 2, &amount);
23113498266Sopenharmony_ci  if(result) {
23213498266Sopenharmony_ci    failf(data, "Failed sending Gopher request");
23313498266Sopenharmony_ci    return result;
23413498266Sopenharmony_ci  }
23513498266Sopenharmony_ci  result = Curl_client_write(data, CLIENTWRITE_HEADER, (char *)"\r\n", 2);
23613498266Sopenharmony_ci  if(result)
23713498266Sopenharmony_ci    return result;
23813498266Sopenharmony_ci
23913498266Sopenharmony_ci  Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
24013498266Sopenharmony_ci  return CURLE_OK;
24113498266Sopenharmony_ci}
24213498266Sopenharmony_ci#endif /* CURL_DISABLE_GOPHER */
243