Coverage Report

Created: 2026-01-09 07:25

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