Coverage Report

Created: 2023-12-08 06:48

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