Coverage Report

Created: 2024-02-25 06:14

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