Coverage Report

Created: 2025-12-04 06:52

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