Coverage Report

Created: 2026-01-25 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/gopher.c
Line
Count
Source
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, 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
#include "urldata.h"
26
#include "gopher.h"
27
28
#ifndef CURL_DISABLE_GOPHER
29
30
#include "transfer.h"
31
#include "sendf.h"
32
#include "curl_trc.h"
33
#include "cfilters.h"
34
#include "connect.h"
35
#include "select.h"
36
#include "url.h"
37
#include "escape.h"
38
39
#ifdef USE_SSL
40
static CURLcode gopher_connect(struct Curl_easy *data, bool *done)
41
0
{
42
0
  (void)data;
43
0
  (void)done;
44
0
  return CURLE_OK;
45
0
}
46
47
static CURLcode gopher_connecting(struct Curl_easy *data, bool *done)
48
0
{
49
0
  struct connectdata *conn = data->conn;
50
0
  CURLcode result;
51
52
0
  result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done);
53
0
  if(result)
54
0
    connclose(conn, "Failed TLS connection");
55
0
  *done = TRUE;
56
0
  return result;
57
0
}
58
#endif
59
60
static CURLcode gopher_do(struct Curl_easy *data, bool *done)
61
0
{
62
0
  CURLcode result = CURLE_OK;
63
0
  struct connectdata *conn = data->conn;
64
0
  curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
65
0
  char *gopherpath;
66
0
  char *path = data->state.up.path;
67
0
  char *query = data->state.up.query;
68
0
  const char *buf = NULL;
69
0
  char *buf_alloc = NULL;
70
0
  size_t nwritten, buf_len;
71
0
  timediff_t timeout_ms;
72
0
  int what;
73
74
0
  *done = TRUE; /* unconditionally */
75
76
  /* path is guaranteed non-NULL */
77
0
  DEBUGASSERT(path);
78
79
0
  if(query)
80
0
    gopherpath = curl_maprintf("%s?%s", path, query);
81
0
  else
82
0
    gopherpath = curlx_strdup(path);
83
84
0
  if(!gopherpath)
85
0
    return CURLE_OUT_OF_MEMORY;
86
87
  /* Create selector. Degenerate cases: / and /1 => convert to "" */
88
0
  if(strlen(gopherpath) <= 2) {
89
0
    buf = "";
90
0
    buf_len = 0;
91
0
    curlx_free(gopherpath);
92
0
  }
93
0
  else {
94
0
    char *newp;
95
96
    /* Otherwise, drop / and the first character (i.e., item type) ... */
97
0
    newp = gopherpath;
98
0
    newp += 2;
99
100
    /* ... and finally unescape */
101
0
    result = Curl_urldecode(newp, 0, &buf_alloc, &buf_len, REJECT_ZERO);
102
0
    curlx_free(gopherpath);
103
0
    if(result)
104
0
      return result;
105
0
    buf = buf_alloc;
106
0
  }
107
108
0
  for(; buf_len;) {
109
110
0
    result = Curl_xfer_send(data, buf, buf_len, FALSE, &nwritten);
111
0
    if(!result) { /* Which may not have written it all! */
112
0
      result = Curl_client_write(data, CLIENTWRITE_HEADER, buf, nwritten);
113
0
      if(result)
114
0
        break;
115
116
0
      if(nwritten > buf_len) {
117
0
        DEBUGASSERT(0);
118
0
        break;
119
0
      }
120
0
      buf_len -= nwritten;
121
0
      buf += nwritten;
122
0
      if(!buf_len)
123
0
        break; /* but it did write it all */
124
0
    }
125
0
    else
126
0
      break;
127
128
0
    timeout_ms = Curl_timeleft_ms(data);
129
0
    if(timeout_ms < 0) {
130
0
      result = CURLE_OPERATION_TIMEDOUT;
131
0
      break;
132
0
    }
133
0
    if(!timeout_ms)
134
0
      timeout_ms = TIMEDIFF_T_MAX;
135
136
    /* Do not busyloop. The entire loop thing is a work-around as it causes a
137
       BLOCKING behavior which is a NO-NO. This function should rather be
138
       split up in a do and a doing piece where the pieces that are not
139
       possible to send now will be sent in the doing function repeatedly
140
       until the entire request is sent.
141
    */
142
0
    what = SOCKET_WRITABLE(sockfd, timeout_ms);
143
0
    if(what < 0) {
144
0
      result = CURLE_SEND_ERROR;
145
0
      break;
146
0
    }
147
0
    else if(!what) {
148
0
      result = CURLE_OPERATION_TIMEDOUT;
149
0
      break;
150
0
    }
151
0
  }
152
153
0
  curlx_free(buf_alloc);
154
155
0
  if(!result)
156
0
    result = Curl_xfer_send(data, "\r\n", 2, FALSE, &nwritten);
157
0
  if(result) {
158
0
    failf(data, "Failed sending Gopher request");
159
0
    return result;
160
0
  }
161
0
  result = Curl_client_write(data, CLIENTWRITE_HEADER, "\r\n", 2);
162
0
  if(result)
163
0
    return result;
164
165
0
  Curl_xfer_setup_recv(data, FIRSTSOCKET, -1);
166
0
  return CURLE_OK;
167
0
}
168
169
/*
170
 * Gopher protocol handler.
171
 * This is also a nice simple template to build off for simple
172
 * connect-command-download protocols.
173
 */
174
175
static const struct Curl_protocol Curl_protocol_gopher = {
176
  ZERO_NULL,                            /* setup_connection */
177
  gopher_do,                            /* do_it */
178
  ZERO_NULL,                            /* done */
179
  ZERO_NULL,                            /* do_more */
180
  ZERO_NULL,                            /* connect_it */
181
  ZERO_NULL,                            /* connecting */
182
  ZERO_NULL,                            /* doing */
183
  ZERO_NULL,                            /* proto_pollset */
184
  ZERO_NULL,                            /* doing_pollset */
185
  ZERO_NULL,                            /* domore_pollset */
186
  ZERO_NULL,                            /* perform_pollset */
187
  ZERO_NULL,                            /* disconnect */
188
  ZERO_NULL,                            /* write_resp */
189
  ZERO_NULL,                            /* write_resp_hd */
190
  ZERO_NULL,                            /* connection_check */
191
  ZERO_NULL,                            /* attach connection */
192
  ZERO_NULL,                            /* follow */
193
};
194
195
#ifdef USE_SSL
196
static const struct Curl_protocol Curl_protocol_gophers = {
197
  ZERO_NULL,                            /* setup_connection */
198
  gopher_do,                            /* do_it */
199
  ZERO_NULL,                            /* done */
200
  ZERO_NULL,                            /* do_more */
201
  gopher_connect,                       /* connect_it */
202
  gopher_connecting,                    /* connecting */
203
  ZERO_NULL,                            /* doing */
204
  ZERO_NULL,                            /* proto_pollset */
205
  ZERO_NULL,                            /* doing_pollset */
206
  ZERO_NULL,                            /* domore_pollset */
207
  ZERO_NULL,                            /* perform_pollset */
208
  ZERO_NULL,                            /* disconnect */
209
  ZERO_NULL,                            /* write_resp */
210
  ZERO_NULL,                            /* write_resp_hd */
211
  ZERO_NULL,                            /* connection_check */
212
  ZERO_NULL,                            /* attach connection */
213
  ZERO_NULL,                            /* follow */
214
};
215
#endif
216
217
#endif /* CURL_DISABLE_GOPHER */
218
219
const struct Curl_scheme Curl_scheme_gopher = {
220
  "gopher",                             /* scheme */
221
#ifdef CURL_DISABLE_GOPHER
222
  ZERO_NULL,
223
#else
224
  &Curl_protocol_gopher,
225
#endif
226
  CURLPROTO_GOPHER,                     /* protocol */
227
  CURLPROTO_GOPHER,                     /* family */
228
  PROTOPT_NONE,                         /* flags */
229
  PORT_GOPHER,                          /* defport */
230
};
231
232
const struct Curl_scheme Curl_scheme_gophers = {
233
  "gophers",                            /* scheme */
234
#if defined(CURL_DISABLE_GOPHER) || !defined(USE_SSL)
235
  ZERO_NULL,
236
#else
237
  &Curl_protocol_gophers,
238
#endif
239
  CURLPROTO_GOPHERS,                    /* protocol */
240
  CURLPROTO_GOPHER,                     /* family */
241
  PROTOPT_SSL,                          /* flags */
242
  PORT_GOPHER,                          /* defport */
243
};