Coverage Report

Created: 2026-02-26 06:33

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