Coverage Report

Created: 2026-03-11 07:23

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