Coverage Report

Created: 2023-03-26 06:11

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