Coverage Report

Created: 2026-08-31 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/pop3.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
 * RFC1734 POP3 Authentication
24
 * RFC1939 POP3 protocol
25
 * RFC2195 CRAM-MD5 authentication
26
 * RFC2384 POP URL Scheme
27
 * RFC2449 POP3 Extension Mechanism
28
 * RFC2595 Using TLS with IMAP, POP3 and ACAP
29
 * RFC2831 DIGEST-MD5 authentication
30
 * RFC4422 Simple Authentication and Security Layer (SASL)
31
 * RFC4616 PLAIN authentication
32
 * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
33
 * RFC5034 POP3 SASL Authentication Mechanism
34
 * RFC6749 OAuth 2.0 Authorization Framework
35
 * RFC8314 Use of TLS for Email Submission and Access
36
 * Draft   LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
37
 *
38
 ***************************************************************************/
39
#include "curl_setup.h"
40
#include "urldata.h"
41
#include "pop3.h"
42
43
#ifndef CURL_DISABLE_POP3
44
45
#ifdef HAVE_NETINET_IN_H
46
#include <netinet/in.h>
47
#endif
48
#ifdef HAVE_ARPA_INET_H
49
#include <arpa/inet.h>
50
#endif
51
#ifdef HAVE_NETDB_H
52
#include <netdb.h>
53
#endif
54
#ifdef __VMS
55
#include <in.h>
56
#include <inet.h>
57
#endif
58
59
#include "sendf.h"
60
#include "curl_trc.h"
61
#include "progress.h"
62
#include "transfer.h"
63
#include "escape.h"
64
#include "pingpong.h"
65
#include "vtls/vtls.h"
66
#include "cfilters.h"
67
#include "connect.h"
68
#include "select.h"
69
#include "url.h"
70
#include "bufref.h"
71
#include "curl_sasl.h"
72
#include "curl_md5.h"
73
#include "curlx/strdup.h"
74
75
/* Authentication type flags */
76
2.51k
#define POP3_TYPE_CLEARTEXT (1 << 0)
77
3.70k
#define POP3_TYPE_APOP      (1 << 1)
78
2.86k
#define POP3_TYPE_SASL      (1 << 2)
79
80
/* Authentication type values */
81
18
#define POP3_TYPE_NONE 0
82
2.40k
#define POP3_TYPE_ANY  (POP3_TYPE_CLEARTEXT | POP3_TYPE_APOP | POP3_TYPE_SASL)
83
84
/* This is the 5-bytes End-Of-Body marker for POP3 */
85
2.05k
#define POP3_EOB     "\x0d\x0a\x2e\x0d\x0a"
86
588
#define POP3_EOB_LEN 5
87
88
/* meta key for storing protocol meta at easy handle */
89
10.2k
#define CURL_META_POP3_EASY   "meta:proto:pop3:easy"
90
/* meta key for storing protocol meta at connection */
91
37.3k
#define CURL_META_POP3_CONN   "meta:proto:pop3:conn"
92
93
/*
94
 * POP3 easy handle state
95
 */
96
struct POP3 {
97
  curl_pp_transfer transfer;
98
  char *id;               /* Message ID */
99
  char *custom;           /* Custom Request */
100
};
101
102
/*
103
 * POP3 connection state
104
 */
105
typedef enum {
106
  POP3_STOP,         /* do nothing state, stops the state machine */
107
  POP3_SERVERGREET,  /* waiting for the initial greeting immediately after
108
                        a connect */
109
  POP3_CAPA,
110
  POP3_STARTTLS,
111
  POP3_UPGRADETLS,   /* asynchronously upgrade the connection to SSL/TLS
112
                       (multi mode only) */
113
  POP3_AUTH,
114
  POP3_APOP,
115
  POP3_USER,
116
  POP3_PASS,
117
  POP3_COMMAND,
118
  POP3_QUIT,
119
  POP3_LAST          /* never used */
120
} pop3state;
121
122
struct pop3_conn {
123
  struct pingpong pp;
124
  pop3state state;        /* Always use pop3.c:state() to change state! */
125
  size_t eob;             /* Number of bytes of the EOB (End Of Body) that
126
                             have been received so far */
127
  size_t strip;           /* Number of bytes from the start to ignore as
128
                             non-body */
129
  struct SASL sasl;       /* SASL-related storage */
130
  char *apoptimestamp;    /* APOP timestamp from the server greeting */
131
  unsigned char authtypes; /* Accepted authentication types */
132
  unsigned char preftype;  /* Preferred authentication type */
133
  BIT(ssldone);           /* Is connect() over SSL done? */
134
  BIT(tls_supported);     /* StartTLS capability supported by server */
135
};
136
137
struct pop3_cmd {
138
  const char *name;
139
  unsigned short nlen;
140
  BIT(multiline); /* response is multi-line with last '.' line */
141
  BIT(multiline_with_args); /* is multi-line when command has args */
142
};
143
144
static const struct pop3_cmd pop3cmds[] = {
145
  { "APOP", 4, FALSE, FALSE },
146
  { "AUTH", 4, FALSE, FALSE },
147
  { "CAPA", 4, TRUE, TRUE },
148
  { "DELE", 4, FALSE, FALSE },
149
  { "LIST", 4, TRUE, FALSE },
150
  { "MSG",  3, TRUE, TRUE },
151
  { "NOOP", 4, FALSE, FALSE },
152
  { "PASS", 4, FALSE, FALSE },
153
  { "QUIT", 4, FALSE, FALSE },
154
  { "RETR", 4, TRUE, TRUE },
155
  { "RSET", 4, FALSE, FALSE },
156
  { "STAT", 4, FALSE, FALSE },
157
  { "STLS", 4, FALSE, FALSE },
158
  { "TOP",  3, TRUE, TRUE },
159
  { "UIDL", 4, TRUE, FALSE },
160
  { "USER", 4, FALSE, FALSE },
161
  { "UTF8", 4, FALSE, FALSE },
162
  { "XTND", 4, TRUE, TRUE },
163
};
164
165
/***********************************************************************
166
 *
167
 * pop3_parse_url_options()
168
 *
169
 * Parse the URL login options.
170
 */
171
static CURLcode pop3_parse_url_options(struct connectdata *conn)
172
1.23k
{
173
1.23k
  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
174
1.23k
  CURLcode result = CURLE_OK;
175
1.23k
  const char *ptr = conn->options;
176
177
1.23k
  if(!pop3c)
178
0
    return CURLE_FAILED_INIT;
179
180
1.32k
  while(!result && ptr && *ptr) {
181
95
    const char *key = ptr;
182
95
    const char *value;
183
184
593
    while(*ptr && *ptr != '=')
185
498
      ptr++;
186
187
95
    value = ptr + 1;
188
189
586
    while(*ptr && *ptr != ';')
190
491
      ptr++;
191
192
95
    if(curl_strnequal(key, "AUTH=", 5)) {
193
72
      result = Curl_sasl_parse_url_auth_option(&pop3c->sasl,
194
72
                                               value, ptr - value);
195
196
72
      if(result && curl_strnequal(value, "+APOP", ptr - value)) {
197
29
        pop3c->preftype = POP3_TYPE_APOP;
198
29
        pop3c->sasl.prefmech = SASL_AUTH_NONE;
199
29
        result = CURLE_OK;
200
29
      }
201
72
    }
202
23
    else
203
23
      result = CURLE_URL_MALFORMAT;
204
205
95
    if(*ptr == ';')
206
43
      ptr++;
207
95
  }
208
209
1.23k
  if(pop3c->preftype != POP3_TYPE_APOP)
210
1.22k
    switch(pop3c->sasl.prefmech) {
211
18
    case SASL_AUTH_NONE:
212
18
      pop3c->preftype = POP3_TYPE_NONE;
213
18
      break;
214
1.17k
    case SASL_AUTH_DEFAULT:
215
1.17k
      pop3c->preftype = POP3_TYPE_ANY;
216
1.17k
      break;
217
34
    default:
218
34
      pop3c->preftype = POP3_TYPE_SASL;
219
34
      break;
220
1.22k
    }
221
222
1.23k
  return result;
223
1.23k
}
224
225
/***********************************************************************
226
 *
227
 * pop3_parse_url_path()
228
 *
229
 * Parse the URL path into separate path components.
230
 */
231
static CURLcode pop3_parse_url_path(struct Curl_easy *data)
232
421
{
233
  /* The POP3 struct is already initialized in pop3_connect() */
234
421
  struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY);
235
421
  const char *path = &data->state.up.path[1]; /* skip leading path */
236
237
421
  if(!pop3)
238
0
    return CURLE_FAILED_INIT;
239
  /* URL decode the path for the message ID */
240
421
  return Curl_urldecode(path, 0, &pop3->id, NULL, REJECT_CTRL);
241
421
}
242
243
/***********************************************************************
244
 *
245
 * pop3_parse_custom_request()
246
 *
247
 * Parse the custom request.
248
 */
249
static CURLcode pop3_parse_custom_request(struct Curl_easy *data)
250
420
{
251
420
  CURLcode result = CURLE_OK;
252
420
  struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY);
253
420
  const char *custom = CURL_EASY_STR(data, STRING_CUSTOMREQUEST);
254
255
420
  if(!pop3)
256
0
    return CURLE_FAILED_INIT;
257
  /* URL decode the custom request */
258
420
  if(custom)
259
8
    result = Curl_urldecode(custom, 0, &pop3->custom, NULL, REJECT_CTRL);
260
261
420
  return result;
262
420
}
263
264
/* Return iff a command is defined as "multi-line" (RFC 1939),
265
 * has a response terminated by a last line with a '.'.
266
 */
267
static bool pop3_is_multiline(const char *cmdline)
268
419
{
269
419
  size_t i;
270
2.16k
  for(i = 0; i < CURL_ARRAYSIZE(pop3cmds); ++i) {
271
2.15k
    if(curl_strnequal(pop3cmds[i].name, cmdline, pop3cmds[i].nlen)) {
272
418
      if(!cmdline[pop3cmds[i].nlen])
273
416
        return (bool)pop3cmds[i].multiline;
274
2
      else if(cmdline[pop3cmds[i].nlen] == ' ')
275
1
        return (bool)pop3cmds[i].multiline_with_args;
276
418
    }
277
2.15k
  }
278
  /* Unknown command, assume multi-line for backward compatibility with
279
   * earlier curl versions that only could do multi-line responses. */
280
2
  return TRUE;
281
419
}
282
283
/***********************************************************************
284
 *
285
 * pop3_endofresp()
286
 *
287
 * Checks for an ending POP3 status code at the start of the given string, but
288
 * also detects the APOP timestamp from the server greeting and various
289
 * capabilities from the CAPA response including the supported authentication
290
 * types and allowed SASL mechanisms.
291
 */
292
static bool pop3_endofresp(struct Curl_easy *data, struct connectdata *conn,
293
                           const char *line, size_t len, int *resp)
294
5.20k
{
295
5.20k
  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
296
5.20k
  (void)data;
297
5.20k
  DEBUGASSERT(pop3c);
298
5.20k
  if(!pop3c) /* internal error */
299
0
    return TRUE;
300
301
  /* Do we have an error response? */
302
5.20k
  if(len >= 4 && !memcmp("-ERR", line, 4)) {
303
92
    *resp = '-';
304
305
92
    return TRUE;
306
92
  }
307
308
  /* Are we processing CAPA command responses? */
309
5.11k
  if(pop3c->state == POP3_CAPA) {
310
    /* Do we have the terminating line? Per RFC 2449 this is a line
311
       containing only a single dot */
312
3.03k
    if((len == 3 && line[0] == '.' && line[1] == '\r') ||
313
3.00k
       (len == 2 && line[0] == '.' && line[1] == '\n'))
314
      /* Treat the response as a success */
315
463
      *resp = '+';
316
2.56k
    else
317
      /* Treat the response as an untagged continuation */
318
2.56k
      *resp = '*';
319
320
3.03k
    return TRUE;
321
3.03k
  }
322
323
  /* Do we have a success response? */
324
2.07k
  if(len >= 3 && !memcmp("+OK", line, 3)) {
325
1.07k
    *resp = '+';
326
327
1.07k
    return TRUE;
328
1.07k
  }
329
330
  /* Do we have a continuation response? */
331
1.00k
  if(len >= 1 && line[0] == '+') {
332
173
    *resp = '*';
333
334
173
    return TRUE;
335
173
  }
336
337
829
  return FALSE; /* Nothing for us */
338
1.00k
}
339
340
/***********************************************************************
341
 *
342
 * pop3_get_message()
343
 *
344
 * Gets the authentication message from the response buffer.
345
 */
346
static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out)
347
114
{
348
114
  struct pop3_conn *pop3c =
349
114
    Curl_conn_meta_get(data->conn, CURL_META_POP3_CONN);
350
114
  char *message;
351
114
  size_t len;
352
353
114
  if(!pop3c)
354
0
    return CURLE_FAILED_INIT;
355
114
  message = curlx_dyn_ptr(&pop3c->pp.recvbuf);
356
114
  len = pop3c->pp.nfinal;
357
114
  if(len > 2) {
358
    /* Find the start of the message */
359
92
    len -= 2;
360
292
    for(message += 2; ISBLANK(*message); message++, len--)
361
200
      ;
362
363
    /* Find the end of the message */
364
432
    while(len--)
365
417
      if(!ISBLANK(message[len]) && !ISNEWLINE(message[len]))
366
77
        break;
367
368
    /* Terminate the message */
369
92
    message[++len] = '\0';
370
92
    Curl_bufref_set(out, message, len, NULL);
371
92
  }
372
22
  else
373
    /* junk input => zero length output */
374
22
    Curl_bufref_set(out, "", 0, NULL);
375
376
114
  return CURLE_OK;
377
114
}
378
379
/***********************************************************************
380
 *
381
 * pop3_state()
382
 *
383
 * This is the ONLY way to change POP3 state!
384
 */
385
static void pop3_state(struct Curl_easy *data, pop3state newstate)
386
3.60k
{
387
3.60k
  struct pop3_conn *pop3c =
388
3.60k
    Curl_conn_meta_get(data->conn, CURL_META_POP3_CONN);
389
3.60k
  if(pop3c) {
390
3.60k
#if defined(DEBUGBUILD) && defined(CURLVERBOSE)
391
    /* for debug purposes */
392
3.60k
    static const char * const names[] = {
393
3.60k
      "STOP",
394
3.60k
      "SERVERGREET",
395
3.60k
      "CAPA",
396
3.60k
      "STARTTLS",
397
3.60k
      "UPGRADETLS",
398
3.60k
      "AUTH",
399
3.60k
      "APOP",
400
3.60k
      "USER",
401
3.60k
      "PASS",
402
3.60k
      "COMMAND",
403
3.60k
      "QUIT",
404
      /* LAST */
405
3.60k
    };
406
407
3.60k
    if(pop3c->state != newstate)
408
3.60k
      infof(data, "POP3 %p state change from %s to %s",
409
3.60k
            (void *)pop3c, names[pop3c->state], names[newstate]);
410
3.60k
#endif
411
412
3.60k
    pop3c->state = newstate;
413
3.60k
  }
414
3.60k
}
415
416
/***********************************************************************
417
 *
418
 * pop3_perform_capa()
419
 *
420
 * Sends the CAPA command in order to obtain a list of server side supported
421
 * capabilities.
422
 */
423
static CURLcode pop3_perform_capa(struct Curl_easy *data,
424
                                  struct connectdata *conn)
425
710
{
426
710
  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
427
710
  CURLcode result = CURLE_OK;
428
429
710
  if(!pop3c)
430
0
    return CURLE_FAILED_INIT;
431
432
710
  pop3c->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */
433
710
  pop3c->sasl.authused = SASL_AUTH_NONE;  /* Clear the auth. mechanism used */
434
710
  pop3c->tls_supported = FALSE;           /* Clear the TLS capability */
435
436
  /* Send the CAPA command */
437
710
  result = Curl_pp_sendf(data, &pop3c->pp, "%s", "CAPA");
438
439
710
  if(!result)
440
710
    pop3_state(data, POP3_CAPA);
441
442
710
  return result;
443
710
}
444
445
/***********************************************************************
446
 *
447
 * pop3_perform_starttls()
448
 *
449
 * Sends the STLS command to start the upgrade to TLS.
450
 */
451
static CURLcode pop3_perform_starttls(struct Curl_easy *data,
452
                                      struct connectdata *conn)
453
0
{
454
0
  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
455
0
  CURLcode result;
456
457
0
  if(!pop3c)
458
0
    return CURLE_FAILED_INIT;
459
460
  /* Send the STLS command */
461
0
  result = Curl_pp_sendf(data, &pop3c->pp, "%s", "STLS");
462
0
  if(!result)
463
0
    pop3_state(data, POP3_STARTTLS);
464
465
0
  return result;
466
0
}
467
468
/***********************************************************************
469
 *
470
 * pop3_perform_upgrade_tls()
471
 *
472
 * Performs the upgrade to TLS.
473
 */
474
static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data,
475
                                         struct connectdata *conn)
476
0
{
477
0
#ifdef USE_SSL
478
  /* Start the SSL connection */
479
0
  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
480
0
  CURLcode result;
481
0
  bool ssldone = FALSE;
482
483
0
  if(!pop3c)
484
0
    return CURLE_FAILED_INIT;
485
486
0
  if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
487
0
    result = Curl_ssl_cfilter_add(
488
0
      data, Curl_conn_get_origin(conn, FIRSTSOCKET), conn, FIRSTSOCKET);
489
0
    if(result)
490
0
      goto out;
491
    /* Change the connection handler */
492
0
    conn->scheme = &Curl_scheme_pop3s;
493
0
  }
494
495
0
  DEBUGASSERT(!pop3c->ssldone);
496
0
  result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
497
0
  DEBUGF(infof(data, "pop3_perform_upgrade_tls, connect -> %d, %d",
498
0
               (int)result, ssldone));
499
0
  if(!result && ssldone) {
500
0
    pop3c->ssldone = ssldone;
501
    /* perform CAPA now, changes pop3c->state out of POP3_UPGRADETLS */
502
0
    result = pop3_perform_capa(data, conn);
503
0
  }
504
0
out:
505
0
  return result;
506
#else
507
  (void)data;
508
  (void)conn;
509
  return CURLE_NOT_BUILT_IN;
510
#endif
511
0
}
512
513
/***********************************************************************
514
 *
515
 * pop3_perform_user()
516
 *
517
 * Sends a clear text USER command to authenticate with.
518
 */
519
static CURLcode pop3_perform_user(struct Curl_easy *data,
520
                                  struct connectdata *conn)
521
10
{
522
10
  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
523
10
  CURLcode result = CURLE_OK;
524
525
10
  if(!pop3c)
526
0
    return CURLE_FAILED_INIT;
527
528
  /* Check we have a username and password to authenticate with and end the
529
     connect phase if we do not */
530
10
  if(!conn->creds) {
531
0
    pop3_state(data, POP3_STOP);
532
533
0
    return result;
534
0
  }
535
536
  /* Send the USER command */
537
10
  result = Curl_pp_sendf(data, &pop3c->pp, "USER %s",
538
10
                         Curl_creds_user(conn->creds));
539
10
  if(!result)
540
10
    pop3_state(data, POP3_USER);
541
542
10
  return result;
543
10
}
544
545
#ifndef CURL_DISABLE_DIGEST_AUTH
546
/***********************************************************************
547
 *
548
 * pop3_perform_apop()
549
 *
550
 * Sends an APOP command to authenticate with.
551
 */
552
static CURLcode pop3_perform_apop(struct Curl_easy *data,
553
                                  struct connectdata *conn)
554
10
{
555
10
  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
556
10
  CURLcode result = CURLE_OK;
557
10
  size_t i;
558
10
  struct MD5_context *ctxt;
559
10
  unsigned char digest[MD5_DIGEST_LEN];
560
10
  char secret[(2 * MD5_DIGEST_LEN) + 1];
561
562
10
  if(!pop3c)
563
0
    return CURLE_FAILED_INIT;
564
565
  /* Check we have a username and password to authenticate with and end the
566
     connect phase if we do not */
567
10
  if(!data->state.creds) {
568
0
    pop3_state(data, POP3_STOP);
569
570
0
    return result;
571
0
  }
572
573
  /* Create the digest */
574
10
  ctxt = Curl_MD5_init(&Curl_DIGEST_MD5);
575
10
  if(!ctxt)
576
0
    return CURLE_OUT_OF_MEMORY;
577
578
10
  Curl_MD5_update(ctxt, (const unsigned char *)pop3c->apoptimestamp,
579
10
                  curlx_uztoui(strlen(pop3c->apoptimestamp)));
580
581
10
  Curl_MD5_update(ctxt, (const unsigned char *)Curl_creds_passwd(conn->creds),
582
10
                  curlx_uztoui(strlen(Curl_creds_passwd(conn->creds))));
583
584
  /* Finalise the digest */
585
10
  Curl_MD5_final(ctxt, digest);
586
587
  /* Convert the calculated 16 octet digest into a 32-byte hex string */
588
170
  for(i = 0; i < MD5_DIGEST_LEN; i++)
589
160
    curl_msnprintf(&secret[2 * i], 3, "%02x", digest[i]);
590
591
10
  result = Curl_pp_sendf(data, &pop3c->pp, "APOP %s %s",
592
10
                         Curl_creds_user(conn->creds), secret);
593
594
10
  if(!result)
595
10
    pop3_state(data, POP3_APOP);
596
597
10
  return result;
598
10
}
599
#endif
600
601
/***********************************************************************
602
 *
603
 * pop3_perform_auth()
604
 *
605
 * Sends an AUTH command allowing the client to login with the given SASL
606
 * authentication mechanism.
607
 */
608
static CURLcode pop3_perform_auth(struct Curl_easy *data,
609
                                  const char *mech,
610
                                  const struct bufref *initresp)
611
118
{
612
118
  struct pop3_conn *pop3c =
613
118
    Curl_conn_meta_get(data->conn, CURL_META_POP3_CONN);
614
118
  CURLcode result = CURLE_OK;
615
118
  const char *ir = Curl_bufref_ptr(initresp);
616
617
118
  if(!pop3c)
618
0
    return CURLE_FAILED_INIT;
619
620
118
  if(ir) {                                  /* AUTH <mech> ...<crlf> */
621
    /* Send the AUTH command with the initial response */
622
0
    result = Curl_pp_sendf(data, &pop3c->pp, "AUTH %s %s",
623
0
                           mech, *ir ? ir : "=");
624
0
  }
625
118
  else {
626
    /* Send the AUTH command */
627
118
    result = Curl_pp_sendf(data, &pop3c->pp, "AUTH %s", mech);
628
118
  }
629
630
118
  return result;
631
118
}
632
633
/***********************************************************************
634
 *
635
 * pop3_continue_auth()
636
 *
637
 * Sends SASL continuation data.
638
 */
639
static CURLcode pop3_continue_auth(struct Curl_easy *data,
640
                                   const char *mech,
641
                                   const struct bufref *resp)
642
34
{
643
34
  struct pop3_conn *pop3c =
644
34
    Curl_conn_meta_get(data->conn, CURL_META_POP3_CONN);
645
646
34
  (void)mech;
647
34
  if(!pop3c)
648
0
    return CURLE_FAILED_INIT;
649
650
34
  return Curl_pp_sendf(data, &pop3c->pp, "%s", Curl_bufref_ptr(resp));
651
34
}
652
653
/***********************************************************************
654
 *
655
 * pop3_cancel_auth()
656
 *
657
 * Sends SASL cancellation.
658
 */
659
static CURLcode pop3_cancel_auth(struct Curl_easy *data, const char *mech)
660
80
{
661
80
  struct pop3_conn *pop3c =
662
80
    Curl_conn_meta_get(data->conn, CURL_META_POP3_CONN);
663
664
80
  (void)mech;
665
80
  if(!pop3c)
666
0
    return CURLE_FAILED_INIT;
667
668
80
  return Curl_pp_sendf(data, &pop3c->pp, "*");
669
80
}
670
671
/***********************************************************************
672
 *
673
 * pop3_perform_authentication()
674
 *
675
 * Initiates the authentication sequence, with the appropriate SASL
676
 * authentication mechanism, falling back to APOP and clear text should a
677
 * common mechanism not be available between the client and server.
678
 */
679
static CURLcode pop3_perform_authentication(struct Curl_easy *data,
680
                                            struct connectdata *conn)
681
547
{
682
547
  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
683
547
  CURLcode result = CURLE_OK;
684
547
  saslprogress progress = SASL_IDLE;
685
686
547
  if(!pop3c)
687
0
    return CURLE_FAILED_INIT;
688
689
  /* Check we have enough data to authenticate with and end the
690
     connect phase if we do not */
691
547
  if(!Curl_sasl_can_authenticate(&pop3c->sasl, data)) {
692
419
    pop3_state(data, POP3_STOP);
693
419
    return result;
694
419
  }
695
696
128
  if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_SASL) {
697
    /* Calculate the SASL login details */
698
112
    result = Curl_sasl_start(&pop3c->sasl, data, FALSE, &progress);
699
700
112
    if(!result)
701
112
      if(progress == SASL_INPROGRESS)
702
108
        pop3_state(data, POP3_AUTH);
703
112
  }
704
705
128
  if(!result && progress == SASL_IDLE) {
706
20
#ifndef CURL_DISABLE_DIGEST_AUTH
707
20
    if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP)
708
      /* Perform APOP authentication */
709
10
      result = pop3_perform_apop(data, conn);
710
10
    else
711
10
#endif
712
10
    if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT)
713
      /* Perform clear text authentication */
714
5
      result = pop3_perform_user(data, conn);
715
5
    else
716
5
      result = Curl_sasl_is_blocked(&pop3c->sasl, data);
717
20
  }
718
719
128
  return result;
720
547
}
721
722
/***********************************************************************
723
 *
724
 * pop3_perform_command()
725
 *
726
 * Sends a POP3 based command.
727
 */
728
static CURLcode pop3_perform_command(struct Curl_easy *data)
729
419
{
730
419
  CURLcode result = CURLE_OK;
731
419
  struct connectdata *conn = data->conn;
732
419
  struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY);
733
419
  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
734
419
  const char *command = NULL;
735
736
419
  if(!pop3 || !pop3c)
737
0
    return CURLE_FAILED_INIT;
738
739
  /* Calculate the default command */
740
419
  if(pop3->id[0] == '\0' || data->set.list_only) {
741
418
    command = "LIST";
742
743
418
    if(pop3->id[0] != '\0')
744
      /* Message specific LIST so skip the BODY transfer */
745
0
      pop3->transfer = PPTRANSFER_INFO;
746
418
  }
747
1
  else
748
1
    command = "RETR";
749
750
419
  if(pop3->custom && pop3->custom[0] != '\0')
751
6
    command = pop3->custom;
752
753
  /* Send the command */
754
419
  if(pop3->id[0] != '\0')
755
1
    result = Curl_pp_sendf(data, &pop3c->pp, "%s %s", command, pop3->id);
756
418
  else
757
418
    result = Curl_pp_sendf(data, &pop3c->pp, "%s", command);
758
759
419
  if(!result) {
760
419
    pop3_state(data, POP3_COMMAND);
761
419
    data->req.no_body = !pop3_is_multiline(command);
762
419
  }
763
764
419
  return result;
765
419
}
766
767
/***********************************************************************
768
 *
769
 * pop3_perform_quit()
770
 *
771
 * Performs the quit action prior to sclose() be called.
772
 */
773
static CURLcode pop3_perform_quit(struct Curl_easy *data,
774
                                  struct connectdata *conn)
775
355
{
776
355
  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
777
355
  CURLcode result;
778
779
355
  if(!pop3c)
780
0
    return CURLE_FAILED_INIT;
781
782
  /* Send the QUIT command */
783
355
  result = Curl_pp_sendf(data, &pop3c->pp, "%s", "QUIT");
784
355
  if(!result)
785
355
    pop3_state(data, POP3_QUIT);
786
787
355
  return result;
788
355
}
789
790
/* For the initial server greeting */
791
static CURLcode pop3_state_servergreet_resp(struct Curl_easy *data,
792
                                            int pop3code,
793
                                            pop3state instate)
794
720
{
795
720
  CURLcode result = CURLE_OK;
796
720
  struct connectdata *conn = data->conn;
797
720
  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
798
720
  const char *line;
799
720
  size_t len;
800
801
720
  (void)instate;
802
720
  if(!pop3c)
803
0
    return CURLE_FAILED_INIT;
804
805
720
  line = curlx_dyn_ptr(&pop3c->pp.recvbuf);
806
720
  len = pop3c->pp.nfinal;
807
808
720
  if(pop3code != '+') {
809
10
    failf(data, "Got unexpected pop3-server response");
810
10
    result = CURLE_WEIRD_SERVER_REPLY;
811
10
  }
812
710
  else if(len > 3) {
813
    /* Does the server support APOP authentication? */
814
710
    const char *lt;
815
710
    const char *gt = NULL;
816
817
    /* Look for the APOP timestamp */
818
710
    lt = memchr(line, '<', len);
819
710
    if(lt)
820
      /* search the remainder for '>' */
821
14
      gt = memchr(lt, '>', len - (lt - line));
822
710
    if(gt) {
823
      /* the length of the timestamp, including the brackets */
824
12
      size_t timestamplen = gt - lt + 1;
825
12
      const char *at = memchr(lt, '@', timestamplen);
826
      /* If the timestamp does not contain '@' it is not (as required by
827
         RFC-1939) conformant to the RFC-822 message id syntax, and we
828
         therefore do not use APOP authentication. */
829
12
      if(at) {
830
        /* dupe the timestamp */
831
11
        pop3c->apoptimestamp = curlx_memdup0(lt, timestamplen);
832
11
        if(!pop3c->apoptimestamp)
833
0
          return CURLE_OUT_OF_MEMORY;
834
        /* Store the APOP capability */
835
11
        pop3c->authtypes |= POP3_TYPE_APOP;
836
11
      }
837
12
    }
838
839
710
    if(!result)
840
710
      result = pop3_perform_capa(data, conn);
841
710
  }
842
843
720
  return result;
844
720
}
845
846
/* For CAPA responses */
847
static CURLcode pop3_state_capa_resp(struct Curl_easy *data, int pop3code,
848
                                     pop3state instate)
849
3.11k
{
850
3.11k
  CURLcode result = CURLE_OK;
851
3.11k
  struct connectdata *conn = data->conn;
852
3.11k
  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
853
3.11k
  const char *line;
854
3.11k
  size_t len;
855
856
3.11k
  (void)instate;
857
3.11k
  if(!pop3c)
858
0
    return CURLE_FAILED_INIT;
859
860
3.11k
  line = curlx_dyn_ptr(&pop3c->pp.recvbuf);
861
3.11k
  len = pop3c->pp.nfinal;
862
863
  /* Do we have an untagged continuation response? */
864
3.11k
  if(pop3code == '*') {
865
    /* Does the server support the STLS capability? */
866
2.56k
    if(len >= 4 && curl_strnequal(line, "STLS", 4))
867
18
      pop3c->tls_supported = TRUE;
868
869
    /* Does the server support clear text authentication? */
870
2.55k
    else if(len >= 4 && curl_strnequal(line, "USER", 4))
871
6
      pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
872
873
    /* Does the server support SASL based authentication? */
874
2.54k
    else if(len >= 5 && curl_strnequal(line, "SASL ", 5)) {
875
292
      pop3c->authtypes |= POP3_TYPE_SASL;
876
877
      /* Advance past the SASL keyword */
878
292
      line += 5;
879
292
      len -= 5;
880
881
      /* Loop through the data line */
882
1.46k
      for(;;) {
883
1.46k
        size_t llen;
884
1.46k
        size_t wordlen = 0;
885
1.46k
        unsigned short mechbit;
886
887
3.32k
        while(len && (ISBLANK(*line) || ISNEWLINE(*line))) {
888
1.85k
          line++;
889
1.85k
          len--;
890
1.85k
        }
891
892
1.46k
        if(!len)
893
292
          break;
894
895
        /* Extract the word */
896
9.10k
        while(wordlen < len && !ISBLANK(line[wordlen]) &&
897
8.35k
              !ISNEWLINE(line[wordlen]))
898
7.92k
          wordlen++;
899
900
        /* Test the word for a matching authentication mechanism */
901
1.17k
        mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
902
1.17k
        if(mechbit && llen == wordlen)
903
141
          pop3c->sasl.authmechs |= mechbit;
904
905
1.17k
        line += wordlen;
906
1.17k
        len -= wordlen;
907
1.17k
      }
908
292
    }
909
2.56k
  }
910
548
  else {
911
    /* Clear text is supported when CAPA is not recognised */
912
548
    if(pop3code != '+')
913
85
      pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
914
915
548
    if(!data->set.use_ssl || Curl_conn_is_ssl(conn, FIRSTSOCKET))
916
545
      result = pop3_perform_authentication(data, conn);
917
3
    else if(pop3code == '+' && pop3c->tls_supported)
918
      /* Switch to TLS connection now */
919
0
      result = pop3_perform_starttls(data, conn);
920
3
    else if(data->set.use_ssl <= CURLUSESSL_TRY)
921
      /* Fallback and carry on with authentication */
922
2
      result = pop3_perform_authentication(data, conn);
923
1
    else {
924
1
      failf(data, "STLS not supported.");
925
1
      result = CURLE_USE_SSL_FAILED;
926
1
    }
927
548
  }
928
929
3.11k
  return result;
930
3.11k
}
931
932
/* For STARTTLS responses */
933
static CURLcode pop3_state_starttls_resp(struct Curl_easy *data,
934
                                         struct connectdata *conn,
935
                                         int pop3code,
936
                                         pop3state instate)
937
0
{
938
0
  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
939
0
  CURLcode result = CURLE_OK;
940
0
  (void)instate;
941
942
0
  if(!pop3c)
943
0
    return CURLE_FAILED_INIT;
944
945
  /* Pipelining in response is forbidden. */
946
0
  if(pop3c->pp.overflow)
947
0
    return CURLE_WEIRD_SERVER_REPLY;
948
949
0
  if(pop3code != '+') {
950
0
    if(data->set.use_ssl != CURLUSESSL_TRY) {
951
0
      failf(data, "STARTTLS denied");
952
0
      result = CURLE_USE_SSL_FAILED;
953
0
    }
954
0
    else
955
0
      result = pop3_perform_authentication(data, conn);
956
0
  }
957
0
  else
958
0
    pop3_state(data, POP3_UPGRADETLS);
959
960
0
  return result;
961
0
}
962
963
/* For SASL authentication responses */
964
static CURLcode pop3_state_auth_resp(struct Curl_easy *data,
965
                                     int pop3code,
966
                                     pop3state instate)
967
139
{
968
139
  CURLcode result = CURLE_OK;
969
139
  struct connectdata *conn = data->conn;
970
139
  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
971
139
  saslprogress progress;
972
973
139
  (void)instate;
974
139
  if(!pop3c)
975
0
    return CURLE_FAILED_INIT;
976
977
139
  result = Curl_sasl_continue(&pop3c->sasl, data, pop3code, &progress);
978
139
  if(!result)
979
130
    switch(progress) {
980
1
    case SASL_DONE:
981
1
      pop3_state(data, POP3_STOP);  /* Authenticated */
982
1
      break;
983
8
    case SASL_IDLE:            /* No mechanism left after cancellation */
984
8
#ifndef CURL_DISABLE_DIGEST_AUTH
985
8
      if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP)
986
        /* Perform APOP authentication */
987
0
        result = pop3_perform_apop(data, conn);
988
8
      else
989
8
#endif
990
8
      if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT)
991
        /* Perform clear text authentication */
992
5
        result = pop3_perform_user(data, conn);
993
3
      else {
994
3
        failf(data, "Authentication cancelled");
995
3
        result = CURLE_LOGIN_DENIED;
996
3
      }
997
8
      break;
998
121
    default:
999
121
      break;
1000
130
    }
1001
1002
139
  return result;
1003
139
}
1004
1005
#ifndef CURL_DISABLE_DIGEST_AUTH
1006
/* For APOP responses */
1007
static CURLcode pop3_state_apop_resp(struct Curl_easy *data, int pop3code,
1008
                                     pop3state instate)
1009
2
{
1010
2
  CURLcode result = CURLE_OK;
1011
2
  (void)instate;
1012
1013
2
  if(pop3code != '+') {
1014
1
    failf(data, "Authentication failed: %d", pop3code);
1015
1
    result = CURLE_LOGIN_DENIED;
1016
1
  }
1017
1
  else
1018
    /* End of connect phase */
1019
1
    pop3_state(data, POP3_STOP);
1020
1021
2
  return result;
1022
2
}
1023
#endif
1024
1025
/* For USER responses */
1026
static CURLcode pop3_state_user_resp(struct Curl_easy *data, int pop3code,
1027
                                     pop3state instate)
1028
5
{
1029
5
  CURLcode result = CURLE_OK;
1030
5
  struct connectdata *conn = data->conn;
1031
5
  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
1032
5
  (void)instate;
1033
1034
5
  if(!pop3c)
1035
0
    return CURLE_FAILED_INIT;
1036
1037
5
  if(pop3code != '+') {
1038
3
    failf(data, "Access denied. %c", pop3code);
1039
3
    result = CURLE_LOGIN_DENIED;
1040
3
  }
1041
2
  else
1042
    /* Send the PASS command */
1043
2
    result = Curl_pp_sendf(data, &pop3c->pp, "PASS %s",
1044
2
                           Curl_creds_passwd(conn->creds));
1045
5
  if(!result)
1046
2
    pop3_state(data, POP3_PASS);
1047
1048
5
  return result;
1049
5
}
1050
1051
/* For PASS responses */
1052
static CURLcode pop3_state_pass_resp(struct Curl_easy *data, int pop3code,
1053
                                     pop3state instate)
1054
2
{
1055
2
  CURLcode result = CURLE_OK;
1056
2
  (void)instate;
1057
1058
2
  if(pop3code != '+') {
1059
1
    failf(data, "Access denied. %c", pop3code);
1060
1
    result = CURLE_LOGIN_DENIED;
1061
1
  }
1062
1
  else
1063
    /* End of connect phase */
1064
1
    pop3_state(data, POP3_STOP);
1065
1066
2
  return result;
1067
2
}
1068
1069
/***********************************************************************
1070
 *
1071
 * pop3_write()
1072
 *
1073
 * This function scans the body after the end-of-body and writes everything
1074
 * until the end is found.
1075
 */
1076
static CURLcode pop3_write(struct Curl_easy *data, const char *str,
1077
                           size_t nread, bool is_eos)
1078
590
{
1079
  /* This code could be made into a special function in the handler struct */
1080
590
  CURLcode result = CURLE_OK;
1081
590
  struct connectdata *conn = data->conn;
1082
590
  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
1083
590
  bool strip_dot = FALSE;
1084
590
  size_t last = 0;
1085
590
  size_t i;
1086
590
  (void)is_eos;
1087
1088
590
  if(!pop3c)
1089
0
    return CURLE_FAILED_INIT;
1090
1091
  /* Search through the buffer looking for the end-of-body marker which is
1092
     5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches
1093
     the eob so the server will have prefixed it with an extra dot which we
1094
     need to strip out. Additionally the marker could of course be spread out
1095
     over 5 different data chunks. */
1096
13.6k
  for(i = 0; i < nread; i++) {
1097
13.0k
    size_t prev = pop3c->eob;
1098
1099
13.0k
    switch(str[i]) {
1100
2.12k
    case 0x0d:
1101
2.12k
      if(pop3c->eob == 0) {
1102
820
        pop3c->eob++;
1103
1104
820
        if(i) {
1105
          /* Write out the body part that did not match */
1106
819
          result = Curl_client_write(data, CLIENTWRITE_BODY, &str[last],
1107
819
                                     i - last);
1108
1109
819
          if(result)
1110
1
            return result;
1111
1112
818
          last = i;
1113
818
        }
1114
820
      }
1115
1.30k
      else if(pop3c->eob == 3)
1116
103
        pop3c->eob++;
1117
1.19k
      else
1118
        /* If the character match was not at position 0 or 3 then restart the
1119
           pattern matching */
1120
1.19k
        pop3c->eob = 1;
1121
2.12k
      break;
1122
1123
2.12k
    case 0x0a:
1124
1.08k
      if(pop3c->eob == 1 || pop3c->eob == 4)
1125
391
        pop3c->eob++;
1126
692
      else
1127
        /* If the character match was not at position 1 or 4 then start the
1128
           search again */
1129
692
        pop3c->eob = 0;
1130
1.08k
      break;
1131
1132
758
    case 0x2e:
1133
758
      if(pop3c->eob == 2)
1134
226
        pop3c->eob++;
1135
532
      else if(pop3c->eob == 3) {
1136
        /* We have an extra dot after the CRLF which we need to strip off */
1137
46
        strip_dot = TRUE;
1138
46
        pop3c->eob = 0;
1139
46
      }
1140
486
      else
1141
        /* If the character match was not at position 2 then start the search
1142
           again */
1143
486
        pop3c->eob = 0;
1144
758
      break;
1145
1146
9.12k
    default:
1147
9.12k
      pop3c->eob = 0;
1148
9.12k
      break;
1149
13.0k
    }
1150
1151
    /* Did we have a partial match which has subsequently failed? */
1152
13.0k
    if(prev && prev >= pop3c->eob) {
1153
      /* Strip can only be non-zero for the first mismatch after CRLF and
1154
         then both prev and strip are equal and nothing will be output below */
1155
2.82k
      while(prev && pop3c->strip) {
1156
558
        prev--;
1157
558
        pop3c->strip--;
1158
558
      }
1159
1160
2.26k
      if(prev) {
1161
        /* If the partial match was the CRLF and dot then only write the CRLF
1162
           as the server would have inserted the dot */
1163
2.05k
        if(strip_dot && prev - 1 > 0) {
1164
45
          result = Curl_client_write(data, CLIENTWRITE_BODY, POP3_EOB,
1165
45
                                     prev - 1);
1166
45
        }
1167
2.00k
        else if(!strip_dot) {
1168
2.00k
          result = Curl_client_write(data, CLIENTWRITE_BODY, POP3_EOB,
1169
2.00k
                                     prev);
1170
2.00k
        }
1171
1
        else {
1172
1
          result = CURLE_OK;
1173
1
        }
1174
1175
2.05k
        if(result)
1176
1
          return result;
1177
1178
2.04k
        last = i;
1179
2.04k
        strip_dot = FALSE;
1180
2.04k
      }
1181
2.26k
    }
1182
13.0k
  }
1183
1184
588
  if(pop3c->eob == POP3_EOB_LEN) {
1185
    /* We have a full match so the transfer is done, however we must transfer
1186
    the CRLF at the start of the EOB as this is considered to be part of the
1187
    message as per RFC-1939, sect. 3 */
1188
3
    result = Curl_client_write(data, CLIENTWRITE_BODY, POP3_EOB, 2);
1189
1190
3
    CURL_REQ_CLEAR_RECV(data);
1191
3
    pop3c->eob = 0;
1192
1193
3
    return result;
1194
3
  }
1195
1196
585
  if(pop3c->eob)
1197
    /* While EOB is matching nothing should be output */
1198
131
    return CURLE_OK;
1199
1200
454
  if(nread - last) {
1201
261
    result = Curl_client_write(data, CLIENTWRITE_BODY, &str[last],
1202
261
                               nread - last);
1203
261
  }
1204
1205
454
  return result;
1206
585
}
1207
1208
/* For command responses */
1209
static CURLcode pop3_state_command_resp(struct Curl_easy *data,
1210
                                        int pop3code,
1211
                                        pop3state instate)
1212
383
{
1213
383
  CURLcode result = CURLE_OK;
1214
383
  struct connectdata *conn = data->conn;
1215
383
  struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY);
1216
383
  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
1217
383
  struct pingpong *pp;
1218
1219
383
  (void)instate;
1220
383
  if(!pop3 || !pop3c)
1221
0
    return CURLE_FAILED_INIT;
1222
1223
383
  pp = &pop3c->pp;
1224
383
  if(pop3code != '+') {
1225
27
    pop3_state(data, POP3_STOP);
1226
27
    return CURLE_WEIRD_SERVER_REPLY;
1227
27
  }
1228
1229
  /* This 'OK' line ends with a CR LF pair which is the two first bytes of the
1230
     EOB string so count this is two matching bytes. This is necessary to make
1231
     the code detect the EOB if the only data than comes now is %2e CR LF like
1232
     when there is no body to return. */
1233
356
  pop3c->eob = 2;
1234
1235
  /* Since this initial CR LF pair is not part of the actual body, we set
1236
     the strip counter here so that these bytes will not be delivered. */
1237
356
  pop3c->strip = 2;
1238
1239
356
  if(pop3->transfer == PPTRANSFER_BODY) {
1240
    /* POP3 download */
1241
353
    Curl_xfer_setup_recv(data, FIRSTSOCKET, -1);
1242
1243
353
    if(pp->overflow) {
1244
      /* The recv buffer contains data that is actually body content so send
1245
         it as such. Note that there may even be additional "headers" after
1246
         the body */
1247
1248
      /* keep only the overflow */
1249
263
      curlx_dyn_tail(&pp->recvbuf, pp->overflow);
1250
263
      pp->nfinal = 0; /* done */
1251
1252
263
      if(!data->req.no_body) {
1253
263
        result = pop3_write(data, curlx_dyn_ptr(&pp->recvbuf),
1254
263
                            curlx_dyn_len(&pp->recvbuf), FALSE);
1255
263
        if(result)
1256
6
          return result;
1257
263
      }
1258
1259
      /* reset the buffer */
1260
257
      curlx_dyn_reset(&pp->recvbuf);
1261
257
      pp->overflow = 0;
1262
257
    }
1263
353
  }
1264
3
  else
1265
3
    pp->overflow = 0;
1266
1267
  /* End of DO phase */
1268
350
  pop3_state(data, POP3_STOP);
1269
1270
350
  return result;
1271
356
}
1272
1273
static CURLcode pop3_statemachine(struct Curl_easy *data,
1274
                                  struct connectdata *conn)
1275
2.25k
{
1276
2.25k
  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
1277
2.25k
  CURLcode result = CURLE_OK;
1278
2.25k
  int pop3code;
1279
2.25k
  struct pingpong *pp;
1280
2.25k
  size_t nread = 0;
1281
2.25k
  (void)data;
1282
1283
2.25k
  if(!pop3c)
1284
0
    return CURLE_FAILED_INIT;
1285
1286
2.25k
  pp = &pop3c->pp;
1287
  /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */
1288
2.25k
upgrade_tls:
1289
2.25k
  if(pop3c->state == POP3_UPGRADETLS) {
1290
0
    result = pop3_perform_upgrade_tls(data, conn);
1291
0
    if(result || (pop3c->state == POP3_UPGRADETLS))
1292
0
      return result;
1293
0
  }
1294
1295
  /* Flush any data that needs to be sent */
1296
2.25k
  if(pp->sendleft)
1297
0
    return Curl_pp_flushsend(data, pp);
1298
1299
5.65k
  do {
1300
     /* Read the response from the server */
1301
5.65k
    result = Curl_pp_readresp(data, FIRSTSOCKET, pp, &pop3code, &nread);
1302
5.65k
    if(result)
1303
1.05k
      return result;
1304
1305
4.60k
    if(!pop3code)
1306
226
      break;
1307
1308
    /* We have now received a full POP3 server response */
1309
4.37k
    switch(pop3c->state) {
1310
720
    case POP3_SERVERGREET:
1311
720
      result = pop3_state_servergreet_resp(data, pop3code, pop3c->state);
1312
720
      break;
1313
1314
3.11k
    case POP3_CAPA:
1315
3.11k
      result = pop3_state_capa_resp(data, pop3code, pop3c->state);
1316
3.11k
      break;
1317
1318
0
    case POP3_STARTTLS:
1319
0
      result = pop3_state_starttls_resp(data, conn, pop3code, pop3c->state);
1320
      /* During UPGRADETLS, leave the read loop as we need to connect
1321
       * (e.g. TLS handshake) before we continue sending/receiving. */
1322
0
      if(!result && (pop3c->state == POP3_UPGRADETLS))
1323
0
        goto upgrade_tls;
1324
0
      break;
1325
1326
139
    case POP3_AUTH:
1327
139
      result = pop3_state_auth_resp(data, pop3code, pop3c->state);
1328
139
      break;
1329
1330
0
#ifndef CURL_DISABLE_DIGEST_AUTH
1331
2
    case POP3_APOP:
1332
2
      result = pop3_state_apop_resp(data, pop3code, pop3c->state);
1333
2
      break;
1334
0
#endif
1335
1336
5
    case POP3_USER:
1337
5
      result = pop3_state_user_resp(data, pop3code, pop3c->state);
1338
5
      break;
1339
1340
2
    case POP3_PASS:
1341
2
      result = pop3_state_pass_resp(data, pop3code, pop3c->state);
1342
2
      break;
1343
1344
383
    case POP3_COMMAND:
1345
383
      result = pop3_state_command_resp(data, pop3code, pop3c->state);
1346
383
      break;
1347
1348
6
    case POP3_QUIT:
1349
6
      pop3_state(data, POP3_STOP);
1350
6
      break;
1351
1352
0
    default:
1353
      /* internal error */
1354
0
      pop3_state(data, POP3_STOP);
1355
0
      break;
1356
4.37k
    }
1357
4.37k
  } while(!result && pop3c->state != POP3_STOP && Curl_pp_moredata(pp));
1358
1359
1.20k
  return result;
1360
2.25k
}
1361
1362
/* Called repeatedly until done from multi.c */
1363
static CURLcode pop3_multi_statemach(struct Curl_easy *data, bool *done)
1364
2.08k
{
1365
2.08k
  CURLcode result = CURLE_OK;
1366
2.08k
  struct connectdata *conn = data->conn;
1367
2.08k
  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
1368
1369
2.08k
  if(!pop3c)
1370
0
    return CURLE_FAILED_INIT;
1371
2.08k
  result = Curl_pp_statemach(data, &pop3c->pp, FALSE, FALSE);
1372
2.08k
  *done = (pop3c->state == POP3_STOP);
1373
1374
2.08k
  return result;
1375
2.08k
}
1376
1377
static CURLcode pop3_block_statemach(struct Curl_easy *data,
1378
                                     struct connectdata *conn,
1379
                                     bool disconnecting)
1380
355
{
1381
355
  CURLcode result = CURLE_OK;
1382
355
  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
1383
1384
355
  if(!pop3c)
1385
0
    return CURLE_FAILED_INIT;
1386
1387
731
  while(pop3c->state != POP3_STOP && !result)
1388
376
    result = Curl_pp_statemach(data, &pop3c->pp, TRUE, disconnecting);
1389
1390
355
  return result;
1391
355
}
1392
1393
/* For the POP3 "protocol connect" and "doing" phases only */
1394
static CURLcode pop3_pollset(struct Curl_easy *data,
1395
                             struct easy_pollset *ps)
1396
159
{
1397
159
  struct pop3_conn *pop3c =
1398
159
    Curl_conn_meta_get(data->conn, CURL_META_POP3_CONN);
1399
159
  return pop3c ? Curl_pp_pollset(data, &pop3c->pp, ps) : CURLE_OK;
1400
159
}
1401
1402
/* SASL parameters for the pop3 protocol */
1403
static const struct SASLproto saslpop3 = {
1404
  "pop",                /* The service name */
1405
  pop3_perform_auth,    /* Send authentication command */
1406
  pop3_continue_auth,   /* Send authentication continuation */
1407
  pop3_cancel_auth,     /* Send authentication cancellation */
1408
  pop3_get_message,     /* Get SASL response message */
1409
  255 - 8,              /* Max line len - strlen("AUTH ") - 1 space - CRLF */
1410
  '*',                  /* Code received when continuation is expected */
1411
  '+',                  /* Code to receive upon authentication success */
1412
  SASL_AUTH_DEFAULT,    /* Default mechanisms */
1413
  SASL_FLAG_BASE64      /* Configuration flags */
1414
};
1415
1416
/***********************************************************************
1417
 *
1418
 * pop3_connect()
1419
 *
1420
 * This function should do everything that is to be considered a part of the
1421
 * connection phase.
1422
 *
1423
 * The variable 'done' points to will be TRUE if the protocol-layer connect
1424
 * phase is done when this function returns, or FALSE if not.
1425
 */
1426
static CURLcode pop3_connect(struct Curl_easy *data, bool *done)
1427
1.23k
{
1428
1.23k
  CURLcode result = CURLE_OK;
1429
1.23k
  struct connectdata *conn = data->conn;
1430
1.23k
  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
1431
1.23k
  struct pingpong *pp = pop3c ? &pop3c->pp : NULL;
1432
1433
1.23k
  *done = FALSE; /* default to not done yet */
1434
1.23k
  if(!pop3c)
1435
0
    return CURLE_FAILED_INIT;
1436
1437
1.23k
  PINGPONG_SETUP(pp, pop3_statemachine, pop3_endofresp);
1438
1439
  /* Set the default preferred authentication type and mechanism */
1440
1.23k
  pop3c->preftype = POP3_TYPE_ANY;
1441
1.23k
  Curl_sasl_init(&pop3c->sasl, data, &saslpop3);
1442
1443
  /* Initialize the pingpong layer */
1444
1.23k
  Curl_pp_init(pp, Curl_pgrs_now(data));
1445
1446
  /* Parse the URL options */
1447
1.23k
  result = pop3_parse_url_options(conn);
1448
1.23k
  if(result)
1449
45
    return result;
1450
1451
  /* Start off waiting for the server greeting response */
1452
1.18k
  pop3_state(data, POP3_SERVERGREET);
1453
1454
1.18k
  result = pop3_multi_statemach(data, done);
1455
1456
1.18k
  return result;
1457
1.23k
}
1458
1459
/***********************************************************************
1460
 *
1461
 * pop3_done()
1462
 *
1463
 * The DONE function. This does what needs to be done after a single DO has
1464
 * performed.
1465
 *
1466
 * Input argument is already checked for validity.
1467
 */
1468
static CURLcode pop3_done(struct Curl_easy *data, CURLcode status,
1469
                          bool premature)
1470
1.23k
{
1471
1.23k
  CURLcode result = CURLE_OK;
1472
1.23k
  struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY);
1473
1474
1.23k
  (void)premature;
1475
1476
1.23k
  if(!pop3)
1477
0
    return CURLE_OK;
1478
1479
1.23k
  if(status) {
1480
839
    CURL_TRC_M(data, "POP3 done with bad status");
1481
839
    connclose(data->conn);
1482
839
    result = status;         /* use the already set error code */
1483
839
  }
1484
1485
  /* Cleanup our per-request based variables */
1486
1.23k
  curlx_safefree(pop3->id);
1487
1.23k
  curlx_safefree(pop3->custom);
1488
1489
  /* Clear the transfer mode for the next request */
1490
1.23k
  pop3->transfer = PPTRANSFER_BODY;
1491
1492
1.23k
  return result;
1493
1.23k
}
1494
1495
/***********************************************************************
1496
 *
1497
 * pop3_perform()
1498
 *
1499
 * This is the actual DO function for POP3. Get a message/listing according to
1500
 * the options previously setup.
1501
 */
1502
static CURLcode pop3_perform(struct Curl_easy *data, bool *connected,
1503
                             bool *dophase_done)
1504
419
{
1505
  /* This is POP3 and no proxy */
1506
419
  CURLcode result = CURLE_OK;
1507
419
  struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY);
1508
1509
419
  if(!pop3)
1510
0
    return CURLE_FAILED_INIT;
1511
1512
419
  DEBUGF(infof(data, "DO phase starts"));
1513
1514
  /* Start the first command in the DO phase, may alter data->req.no_body */
1515
419
  result = pop3_perform_command(data);
1516
419
  if(result)
1517
0
    return result;
1518
1519
419
  if(data->req.no_body)
1520
    /* Requested no body means no transfer */
1521
3
    pop3->transfer = PPTRANSFER_INFO;
1522
1523
419
  *dophase_done = FALSE; /* not done yet */
1524
1525
  /* Run the state-machine */
1526
419
  result = pop3_multi_statemach(data, dophase_done);
1527
419
  *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET);
1528
1529
419
  if(*dophase_done)
1530
371
    DEBUGF(infof(data, "DO phase is complete"));
1531
1532
419
  return result;
1533
419
}
1534
1535
/* Call this when the DO phase has completed */
1536
static CURLcode pop3_dophase_done(struct Curl_easy *data, bool connected)
1537
350
{
1538
350
  (void)data;
1539
350
  (void)connected;
1540
1541
350
  return CURLE_OK;
1542
350
}
1543
1544
/***********************************************************************
1545
 *
1546
 * pop3_regular_transfer()
1547
 *
1548
 * The input argument is already checked for validity.
1549
 *
1550
 * Performs all commands done before a regular transfer between a local and a
1551
 * remote host.
1552
 */
1553
static CURLcode pop3_regular_transfer(struct Curl_easy *data,
1554
                                      bool *dophase_done)
1555
419
{
1556
419
  CURLcode result = CURLE_OK;
1557
419
  bool connected = FALSE;
1558
1559
  /* Make sure size is unknown at this point */
1560
419
  data->req.size = -1;
1561
1562
  /* Set the progress data */
1563
419
  Curl_pgrsReset(data);
1564
1565
  /* Carry out the perform */
1566
419
  result = pop3_perform(data, &connected, dophase_done);
1567
1568
  /* Perform post DO phase operations if necessary */
1569
419
  if(!result && *dophase_done)
1570
344
    result = pop3_dophase_done(data, connected);
1571
1572
419
  return result;
1573
419
}
1574
1575
/***********************************************************************
1576
 *
1577
 * pop3_do()
1578
 *
1579
 * This function is registered as 'curl_do' function. It decodes the path
1580
 * parts etc as a wrapper to the actual DO function (pop3_perform).
1581
 *
1582
 * The input argument is already checked for validity.
1583
 */
1584
static CURLcode pop3_do(struct Curl_easy *data, bool *done)
1585
421
{
1586
421
  CURLcode result = CURLE_OK;
1587
421
  *done = FALSE; /* default to false */
1588
1589
  /* Parse the URL path */
1590
421
  result = pop3_parse_url_path(data);
1591
421
  if(result)
1592
1
    return result;
1593
1594
  /* Parse the custom request */
1595
420
  result = pop3_parse_custom_request(data);
1596
420
  if(result)
1597
1
    return result;
1598
1599
419
  result = pop3_regular_transfer(data, done);
1600
1601
419
  return result;
1602
420
}
1603
1604
/***********************************************************************
1605
 *
1606
 * pop3_disconnect()
1607
 *
1608
 * Disconnect from an POP3 server. Cleanup protocol-specific per-connection
1609
 * resources. BLOCKING.
1610
 */
1611
static CURLcode pop3_disconnect(struct Curl_easy *data,
1612
                                struct connectdata *conn, bool dead_connection)
1613
6.95k
{
1614
6.95k
  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
1615
6.95k
  (void)data;
1616
1617
6.95k
  if(!pop3c)
1618
1
    return CURLE_FAILED_INIT;
1619
1620
  /* We cannot send quit unconditionally. If this connection is stale or
1621
     bad in any way, sending quit and waiting around here will make the
1622
     disconnect wait in vain and cause more problems than we need to. */
1623
1624
6.94k
  if(!dead_connection && conn->bits.protoconnstart &&
1625
355
     !Curl_pp_needs_flush(data, &pop3c->pp)) {
1626
355
    if(!pop3_perform_quit(data, conn))
1627
355
      (void)pop3_block_statemach(data, conn, TRUE); /* ignore errors on QUIT */
1628
355
  }
1629
1630
  /* Disconnect from the server */
1631
6.94k
  Curl_pp_disconnect(&pop3c->pp);
1632
1633
  /* Cleanup our connection based variables */
1634
6.94k
  curlx_safefree(pop3c->apoptimestamp);
1635
1636
6.94k
  return CURLE_OK;
1637
6.95k
}
1638
1639
/* Called from multi.c while DOing */
1640
static CURLcode pop3_doing(struct Curl_easy *data, bool *dophase_done)
1641
57
{
1642
57
  CURLcode result = pop3_multi_statemach(data, dophase_done);
1643
1644
57
  if(result)
1645
22
    DEBUGF(infof(data, "DO phase failed"));
1646
35
  else if(*dophase_done) {
1647
6
    result = pop3_dophase_done(data, FALSE /* not connected */);
1648
1649
6
    DEBUGF(infof(data, "DO phase is complete"));
1650
6
  }
1651
1652
57
  return result;
1653
57
}
1654
1655
static void pop3_easy_dtor(void *key, size_t klen, void *entry)
1656
6.94k
{
1657
6.94k
  struct POP3 *pop3 = entry;
1658
6.94k
  (void)key;
1659
6.94k
  (void)klen;
1660
6.94k
  DEBUGASSERT(pop3);
1661
  /* Cleanup our per-request based variables */
1662
6.94k
  curlx_safefree(pop3->id);
1663
6.94k
  curlx_safefree(pop3->custom);
1664
6.94k
  curlx_free(pop3);
1665
6.94k
}
1666
1667
static void pop3_conn_dtor(void *key, size_t klen, void *entry)
1668
6.94k
{
1669
6.94k
  struct pop3_conn *pop3c = entry;
1670
6.94k
  (void)key;
1671
6.94k
  (void)klen;
1672
6.94k
  DEBUGASSERT(pop3c);
1673
6.94k
  Curl_pp_disconnect(&pop3c->pp);
1674
6.94k
  curlx_safefree(pop3c->apoptimestamp);
1675
6.94k
  curlx_free(pop3c);
1676
6.94k
}
1677
1678
static CURLcode pop3_setup_connection(struct Curl_easy *data,
1679
                                      struct connectdata *conn)
1680
6.94k
{
1681
6.94k
  struct pop3_conn *pop3c;
1682
6.94k
  struct POP3 *pop3 = curlx_calloc(1, sizeof(*pop3));
1683
6.94k
  if(!pop3 ||
1684
6.94k
     Curl_meta_set(data, CURL_META_POP3_EASY, pop3, pop3_easy_dtor))
1685
0
    return CURLE_OUT_OF_MEMORY;
1686
1687
6.94k
  pop3c = curlx_calloc(1, sizeof(*pop3c));
1688
6.94k
  if(!pop3c ||
1689
6.94k
     Curl_conn_meta_set(conn, CURL_META_POP3_CONN, pop3c, pop3_conn_dtor))
1690
0
    return CURLE_OUT_OF_MEMORY;
1691
1692
6.94k
  return CURLE_OK;
1693
6.94k
}
1694
1695
/*
1696
 * POP3 protocol.
1697
 */
1698
const struct Curl_protocol Curl_protocol_pop3 = {
1699
  pop3_setup_connection,            /* setup_connection */
1700
  pop3_do,                          /* do_it */
1701
  pop3_done,                        /* done */
1702
  ZERO_NULL,                        /* do_more */
1703
  pop3_connect,                     /* connect_it */
1704
  pop3_multi_statemach,             /* connecting */
1705
  pop3_doing,                       /* doing */
1706
  pop3_pollset,                     /* proto_pollset */
1707
  pop3_pollset,                     /* doing_pollset */
1708
  ZERO_NULL,                        /* domore_pollset */
1709
  ZERO_NULL,                        /* perform_pollset */
1710
  pop3_disconnect,                  /* disconnect */
1711
  pop3_write,                       /* write_resp */
1712
  ZERO_NULL,                        /* write_resp_hd */
1713
  ZERO_NULL,                        /* connection_is_dead */
1714
  ZERO_NULL,                        /* attach connection */
1715
  ZERO_NULL,                        /* follow */
1716
};
1717
1718
#endif /* CURL_DISABLE_POP3 */