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