Coverage Report

Created: 2026-09-14 07:05

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