Coverage Report

Created: 2025-07-11 06:33

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