Coverage Report

Created: 2024-09-08 06:32

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