Coverage Report

Created: 2025-06-09 07:43

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