blob: cf278a22ba885b6a8cac3bc321bae8dade459107 [file] [log] [blame]
Kristian Monsen5ab50182010-05-14 18:53:44 +01001/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
Elliott Hughes0128fe42018-02-27 14:57:55 -08008 * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
Kristian Monsen5ab50182010-05-14 18:53:44 +01009 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
Alex Deymod15eaac2016-06-28 14:49:26 -070012 * are also available at https://curl.haxx.se/docs/copyright.html.
Kristian Monsen5ab50182010-05-14 18:53:44 +010013 *
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 *
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070021 * RFC2195 CRAM-MD5 authentication
22 * RFC2595 Using TLS with IMAP, POP3 and ACAP
23 * RFC2831 DIGEST-MD5 authentication
Kristian Monsen5ab50182010-05-14 18:53:44 +010024 * RFC3501 IMAPv4 protocol
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070025 * RFC4422 Simple Authentication and Security Layer (SASL)
26 * RFC4616 PLAIN authentication
27 * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
28 * RFC4959 IMAP Extension for SASL Initial Client Response
Kristian Monsen5ab50182010-05-14 18:53:44 +010029 * RFC5092 IMAP URL Scheme
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070030 * RFC6749 OAuth 2.0 Authorization Framework
31 * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
Kristian Monsen5ab50182010-05-14 18:53:44 +010032 *
33 ***************************************************************************/
34
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070035#include "curl_setup.h"
Kristian Monsen5ab50182010-05-14 18:53:44 +010036
37#ifndef CURL_DISABLE_IMAP
Kristian Monsen5ab50182010-05-14 18:53:44 +010038
Kristian Monsen5ab50182010-05-14 18:53:44 +010039#ifdef HAVE_NETINET_IN_H
40#include <netinet/in.h>
41#endif
42#ifdef HAVE_ARPA_INET_H
43#include <arpa/inet.h>
44#endif
45#ifdef HAVE_UTSNAME_H
46#include <sys/utsname.h>
47#endif
48#ifdef HAVE_NETDB_H
49#include <netdb.h>
50#endif
51#ifdef __VMS
52#include <in.h>
53#include <inet.h>
54#endif
55
56#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
57#undef in_addr_t
58#define in_addr_t unsigned long
59#endif
60
61#include <curl/curl.h>
62#include "urldata.h"
63#include "sendf.h"
Kristian Monsen5ab50182010-05-14 18:53:44 +010064#include "hostip.h"
65#include "progress.h"
66#include "transfer.h"
67#include "escape.h"
68#include "http.h" /* for HTTP proxy tunnel stuff */
69#include "socks.h"
70#include "imap.h"
Alex Deymo486467e2017-12-19 19:04:07 +010071#include "mime.h"
Kristian Monsen5ab50182010-05-14 18:53:44 +010072#include "strtoofft.h"
Elliott Hughescee03382017-06-23 12:17:18 -070073#include "strcase.h"
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070074#include "vtls/vtls.h"
Kristian Monsen5ab50182010-05-14 18:53:44 +010075#include "connect.h"
76#include "strerror.h"
77#include "select.h"
78#include "multiif.h"
79#include "url.h"
Elliott Hughescee03382017-06-23 12:17:18 -070080#include "strcase.h"
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070081#include "curl_sasl.h"
82#include "warnless.h"
Kristian Monsen5ab50182010-05-14 18:53:44 +010083
Alex Deymod15eaac2016-06-28 14:49:26 -070084/* The last 3 #include files should be in this order */
85#include "curl_printf.h"
Kristian Monsen5ab50182010-05-14 18:53:44 +010086#include "curl_memory.h"
Kristian Monsen5ab50182010-05-14 18:53:44 +010087#include "memdebug.h"
88
89/* Local API functions */
Kristian Monsen5ab50182010-05-14 18:53:44 +010090static CURLcode imap_regular_transfer(struct connectdata *conn, bool *done);
91static CURLcode imap_do(struct connectdata *conn, bool *done);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070092static CURLcode imap_done(struct connectdata *conn, CURLcode status,
93 bool premature);
Kristian Monsen5ab50182010-05-14 18:53:44 +010094static CURLcode imap_connect(struct connectdata *conn, bool *done);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070095static CURLcode imap_disconnect(struct connectdata *conn, bool dead);
Kristian Monsen5ab50182010-05-14 18:53:44 +010096static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070097static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
Kristian Monsen5ab50182010-05-14 18:53:44 +010098 int numsocks);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070099static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done);
100static CURLcode imap_setup_connection(struct connectdata *conn);
Alex Deymod15eaac2016-06-28 14:49:26 -0700101static char *imap_atom(const char *str, bool escape_only);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700102static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...);
103static CURLcode imap_parse_url_options(struct connectdata *conn);
104static CURLcode imap_parse_url_path(struct connectdata *conn);
105static CURLcode imap_parse_custom_request(struct connectdata *conn);
106static CURLcode imap_perform_authenticate(struct connectdata *conn,
107 const char *mech,
108 const char *initresp);
109static CURLcode imap_continue_authenticate(struct connectdata *conn,
110 const char *resp);
Elliott Hughescee03382017-06-23 12:17:18 -0700111static void imap_get_message(char *buffer, char **outptr);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100112
113/*
114 * IMAP protocol handler.
115 */
116
117const struct Curl_handler Curl_handler_imap = {
118 "IMAP", /* scheme */
119 imap_setup_connection, /* setup_connection */
120 imap_do, /* do_it */
121 imap_done, /* done */
122 ZERO_NULL, /* do_more */
123 imap_connect, /* connect_it */
124 imap_multi_statemach, /* connecting */
125 imap_doing, /* doing */
126 imap_getsock, /* proto_getsock */
127 imap_getsock, /* doing_getsock */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700128 ZERO_NULL, /* domore_getsock */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100129 ZERO_NULL, /* perform_getsock */
130 imap_disconnect, /* disconnect */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700131 ZERO_NULL, /* readwrite */
Elliott Hughes82be86d2017-09-20 17:00:17 -0700132 ZERO_NULL, /* connection_check */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100133 PORT_IMAP, /* defport */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700134 CURLPROTO_IMAP, /* protocol */
Elliott Hughes82be86d2017-09-20 17:00:17 -0700135 PROTOPT_CLOSEACTION| /* flags */
136 PROTOPT_URLOPTIONS
Kristian Monsen5ab50182010-05-14 18:53:44 +0100137};
138
Kristian Monsen5ab50182010-05-14 18:53:44 +0100139#ifdef USE_SSL
140/*
141 * IMAPS protocol handler.
142 */
143
144const struct Curl_handler Curl_handler_imaps = {
145 "IMAPS", /* scheme */
146 imap_setup_connection, /* setup_connection */
147 imap_do, /* do_it */
148 imap_done, /* done */
149 ZERO_NULL, /* do_more */
150 imap_connect, /* connect_it */
151 imap_multi_statemach, /* connecting */
152 imap_doing, /* doing */
153 imap_getsock, /* proto_getsock */
154 imap_getsock, /* doing_getsock */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700155 ZERO_NULL, /* domore_getsock */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100156 ZERO_NULL, /* perform_getsock */
157 imap_disconnect, /* disconnect */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700158 ZERO_NULL, /* readwrite */
Elliott Hughes82be86d2017-09-20 17:00:17 -0700159 ZERO_NULL, /* connection_check */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100160 PORT_IMAPS, /* defport */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700161 CURLPROTO_IMAPS, /* protocol */
162 PROTOPT_CLOSEACTION | PROTOPT_SSL /* flags */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100163};
164#endif
165
Alex Deymo486467e2017-12-19 19:04:07 +0100166#define IMAP_RESP_OK 1
167#define IMAP_RESP_NOT_OK 2
168#define IMAP_RESP_PREAUTH 3
169
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700170/* SASL parameters for the imap protocol */
171static const struct SASLproto saslimap = {
172 "imap", /* The service name */
173 '+', /* Code received when continuation is expected */
Alex Deymo486467e2017-12-19 19:04:07 +0100174 IMAP_RESP_OK, /* Code to receive upon authentication success */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700175 0, /* Maximum initial response length (no max) */
176 imap_perform_authenticate, /* Send authentication command */
177 imap_continue_authenticate, /* Send authentication continuation */
178 imap_get_message /* Get SASL response message */
179};
180
181
182#ifdef USE_SSL
183static void imap_to_imaps(struct connectdata *conn)
184{
Alex Deymod15eaac2016-06-28 14:49:26 -0700185 /* Change the connection handler */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700186 conn->handler = &Curl_handler_imaps;
Alex Deymod15eaac2016-06-28 14:49:26 -0700187
188 /* Set the connection's upgraded to TLS flag */
189 conn->tls_upgraded = TRUE;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700190}
191#else
192#define imap_to_imaps(x) Curl_nop_stmt
193#endif
194
Kristian Monsen5ab50182010-05-14 18:53:44 +0100195/***********************************************************************
196 *
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700197 * imap_matchresp()
Kristian Monsen5ab50182010-05-14 18:53:44 +0100198 *
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700199 * Determines whether the untagged response is related to the specified
200 * command by checking if it is in format "* <command-name> ..." or
201 * "* <number> <command-name> ...".
Kristian Monsen5ab50182010-05-14 18:53:44 +0100202 *
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700203 * The "* " marker is assumed to have already been checked by the caller.
Kristian Monsen5ab50182010-05-14 18:53:44 +0100204 */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700205static bool imap_matchresp(const char *line, size_t len, const char *cmd)
Kristian Monsen5ab50182010-05-14 18:53:44 +0100206{
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700207 const char *end = line + len;
208 size_t cmd_len = strlen(cmd);
209
210 /* Skip the untagged response marker */
211 line += 2;
212
213 /* Do we have a number after the marker? */
214 if(line < end && ISDIGIT(*line)) {
215 /* Skip the number */
216 do
217 line++;
218 while(line < end && ISDIGIT(*line));
219
220 /* Do we have the space character? */
221 if(line == end || *line != ' ')
222 return FALSE;
223
224 line++;
225 }
226
227 /* Does the command name match and is it followed by a space character or at
228 the end of line? */
Elliott Hughescee03382017-06-23 12:17:18 -0700229 if(line + cmd_len <= end && strncasecompare(line, cmd, cmd_len) &&
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700230 (line[cmd_len] == ' ' || line + cmd_len + 2 == end))
231 return TRUE;
232
233 return FALSE;
234}
235
236/***********************************************************************
237 *
238 * imap_endofresp()
239 *
240 * Checks whether the given string is a valid tagged, untagged or continuation
241 * response which can be processed by the response handler.
242 */
243static bool imap_endofresp(struct connectdata *conn, char *line, size_t len,
244 int *resp)
245{
246 struct IMAP *imap = conn->data->req.protop;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100247 struct imap_conn *imapc = &conn->proto.imapc;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700248 const char *id = imapc->resptag;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100249 size_t id_len = strlen(id);
250
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700251 /* Do we have a tagged command response? */
252 if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') {
253 line += id_len + 1;
254 len -= id_len + 1;
255
256 if(len >= 2 && !memcmp(line, "OK", 2))
Alex Deymo486467e2017-12-19 19:04:07 +0100257 *resp = IMAP_RESP_OK;
258 else if(len >= 7 && !memcmp(line, "PREAUTH", 7))
259 *resp = IMAP_RESP_PREAUTH;
260 else
261 *resp = IMAP_RESP_NOT_OK;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700262
263 return TRUE;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100264 }
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700265
266 /* Do we have an untagged command response? */
267 if(len >= 2 && !memcmp("* ", line, 2)) {
268 switch(imapc->state) {
269 /* States which are interested in untagged responses */
270 case IMAP_CAPABILITY:
271 if(!imap_matchresp(line, len, "CAPABILITY"))
272 return FALSE;
273 break;
274
275 case IMAP_LIST:
276 if((!imap->custom && !imap_matchresp(line, len, "LIST")) ||
277 (imap->custom && !imap_matchresp(line, len, imap->custom) &&
Alex Deymo486467e2017-12-19 19:04:07 +0100278 (!strcasecompare(imap->custom, "STORE") ||
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700279 !imap_matchresp(line, len, "FETCH")) &&
Alex Deymo486467e2017-12-19 19:04:07 +0100280 !strcasecompare(imap->custom, "SELECT") &&
281 !strcasecompare(imap->custom, "EXAMINE") &&
282 !strcasecompare(imap->custom, "SEARCH") &&
283 !strcasecompare(imap->custom, "EXPUNGE") &&
284 !strcasecompare(imap->custom, "LSUB") &&
285 !strcasecompare(imap->custom, "UID") &&
286 !strcasecompare(imap->custom, "NOOP")))
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700287 return FALSE;
288 break;
289
290 case IMAP_SELECT:
291 /* SELECT is special in that its untagged responses do not have a
292 common prefix so accept anything! */
293 break;
294
295 case IMAP_FETCH:
296 if(!imap_matchresp(line, len, "FETCH"))
297 return FALSE;
298 break;
299
300 case IMAP_SEARCH:
301 if(!imap_matchresp(line, len, "SEARCH"))
302 return FALSE;
303 break;
304
305 /* Ignore other untagged responses */
306 default:
307 return FALSE;
308 }
309
310 *resp = '*';
311 return TRUE;
312 }
313
314 /* Do we have a continuation response? This should be a + symbol followed by
315 a space and optionally some text as per RFC-3501 for the AUTHENTICATE and
316 APPEND commands and as outlined in Section 4. Examples of RFC-4959 but
317 some e-mail servers ignore this and only send a single + instead. */
Alex Deymod15eaac2016-06-28 14:49:26 -0700318 if(imap && !imap->custom && ((len == 3 && !memcmp("+", line, 1)) ||
319 (len >= 2 && !memcmp("+ ", line, 2)))) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700320 switch(imapc->state) {
321 /* States which are interested in continuation responses */
322 case IMAP_AUTHENTICATE:
323 case IMAP_APPEND:
324 *resp = '+';
325 break;
326
327 default:
328 failf(conn->data, "Unexpected continuation response");
329 *resp = -1;
330 break;
331 }
332
333 return TRUE;
334 }
335
336 return FALSE; /* Nothing for us */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100337}
338
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700339/***********************************************************************
340 *
341 * imap_get_message()
342 *
343 * Gets the authentication message from the response buffer.
344 */
Elliott Hughescee03382017-06-23 12:17:18 -0700345static void imap_get_message(char *buffer, char **outptr)
Kristian Monsen5ab50182010-05-14 18:53:44 +0100346{
Elliott Hughes0128fe42018-02-27 14:57:55 -0800347 size_t len = strlen(buffer);
Elliott Hughescee03382017-06-23 12:17:18 -0700348 char *message = NULL;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700349
Elliott Hughes0128fe42018-02-27 14:57:55 -0800350 if(len > 2) {
351 /* Find the start of the message */
352 len -= 2;
353 for(message = buffer + 2; *message == ' ' || *message == '\t';
354 message++, len--)
355 ;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700356
Elliott Hughes0128fe42018-02-27 14:57:55 -0800357 /* Find the end of the message */
358 for(; len--;)
359 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
360 message[len] != '\t')
361 break;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700362
Elliott Hughes0128fe42018-02-27 14:57:55 -0800363 /* Terminate the message */
364 if(++len) {
365 message[len] = '\0';
366 }
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700367 }
Elliott Hughes0128fe42018-02-27 14:57:55 -0800368 else
369 /* junk input => zero length output */
370 message = &buffer[len];
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700371
372 *outptr = message;
373}
374
375/***********************************************************************
376 *
377 * state()
378 *
379 * This is the ONLY way to change IMAP state!
380 */
381static void state(struct connectdata *conn, imapstate newstate)
382{
383 struct imap_conn *imapc = &conn->proto.imapc;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100384#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
385 /* for debug purposes */
386 static const char * const names[]={
387 "STOP",
388 "SERVERGREET",
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700389 "CAPABILITY",
Kristian Monsen5ab50182010-05-14 18:53:44 +0100390 "STARTTLS",
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700391 "UPGRADETLS",
392 "AUTHENTICATE",
393 "LOGIN",
394 "LIST",
Kristian Monsen5ab50182010-05-14 18:53:44 +0100395 "SELECT",
396 "FETCH",
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700397 "FETCH_FINAL",
398 "APPEND",
399 "APPEND_FINAL",
400 "SEARCH",
Kristian Monsen5ab50182010-05-14 18:53:44 +0100401 "LOGOUT",
402 /* LAST */
403 };
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700404
Kristian Monsen5ab50182010-05-14 18:53:44 +0100405 if(imapc->state != newstate)
406 infof(conn->data, "IMAP %p state change from %s to %s\n",
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700407 (void *)imapc, names[imapc->state], names[newstate]);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100408#endif
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700409
Kristian Monsen5ab50182010-05-14 18:53:44 +0100410 imapc->state = newstate;
411}
412
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700413/***********************************************************************
414 *
415 * imap_perform_capability()
416 *
417 * Sends the CAPABILITY command in order to obtain a list of server side
418 * supported capabilities.
419 */
420static CURLcode imap_perform_capability(struct connectdata *conn)
Kristian Monsen5ab50182010-05-14 18:53:44 +0100421{
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700422 CURLcode result = CURLE_OK;
423 struct imap_conn *imapc = &conn->proto.imapc;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100424
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700425 imapc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */
426 imapc->sasl.authused = SASL_AUTH_NONE; /* Clear the auth. mechanism used */
427 imapc->tls_supported = FALSE; /* Clear the TLS capability */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100428
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700429 /* Send the CAPABILITY command */
430 result = imap_sendf(conn, "CAPABILITY");
Kristian Monsen5ab50182010-05-14 18:53:44 +0100431
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700432 if(!result)
433 state(conn, IMAP_CAPABILITY);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100434
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700435 return result;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100436}
437
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700438/***********************************************************************
439 *
440 * imap_perform_starttls()
441 *
442 * Sends the STARTTLS command to start the upgrade to TLS.
443 */
444static CURLcode imap_perform_starttls(struct connectdata *conn)
445{
446 CURLcode result = CURLE_OK;
447
448 /* Send the STARTTLS command */
449 result = imap_sendf(conn, "STARTTLS");
450
451 if(!result)
452 state(conn, IMAP_STARTTLS);
453
454 return result;
455}
456
457/***********************************************************************
458 *
459 * imap_perform_upgrade_tls()
460 *
461 * Performs the upgrade to TLS.
462 */
463static CURLcode imap_perform_upgrade_tls(struct connectdata *conn)
464{
465 CURLcode result = CURLE_OK;
466 struct imap_conn *imapc = &conn->proto.imapc;
467
468 /* Start the SSL connection */
469 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
470
471 if(!result) {
472 if(imapc->state != IMAP_UPGRADETLS)
473 state(conn, IMAP_UPGRADETLS);
474
475 if(imapc->ssldone) {
476 imap_to_imaps(conn);
477 result = imap_perform_capability(conn);
478 }
479 }
480
481 return result;
482}
483
484/***********************************************************************
485 *
486 * imap_perform_login()
487 *
488 * Sends a clear text LOGIN command to authenticate with.
489 */
490static CURLcode imap_perform_login(struct connectdata *conn)
491{
492 CURLcode result = CURLE_OK;
493 char *user;
494 char *passwd;
495
496 /* Check we have a username and password to authenticate with and end the
497 connect phase if we don't */
498 if(!conn->bits.user_passwd) {
499 state(conn, IMAP_STOP);
500
501 return result;
502 }
503
504 /* Make sure the username and password are in the correct atom format */
Alex Deymod15eaac2016-06-28 14:49:26 -0700505 user = imap_atom(conn->user, false);
506 passwd = imap_atom(conn->passwd, false);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700507
508 /* Send the LOGIN command */
509 result = imap_sendf(conn, "LOGIN %s %s", user ? user : "",
510 passwd ? passwd : "");
511
512 free(user);
513 free(passwd);
514
515 if(!result)
516 state(conn, IMAP_LOGIN);
517
518 return result;
519}
520
521/***********************************************************************
522 *
523 * imap_perform_authenticate()
524 *
525 * Sends an AUTHENTICATE command allowing the client to login with the given
526 * SASL authentication mechanism.
527 */
528static CURLcode imap_perform_authenticate(struct connectdata *conn,
529 const char *mech,
530 const char *initresp)
531{
532 CURLcode result = CURLE_OK;
533
534 if(initresp) {
535 /* Send the AUTHENTICATE command with the initial response */
536 result = imap_sendf(conn, "AUTHENTICATE %s %s", mech, initresp);
537 }
538 else {
539 /* Send the AUTHENTICATE command */
540 result = imap_sendf(conn, "AUTHENTICATE %s", mech);
541 }
542
543 return result;
544}
545
546/***********************************************************************
547 *
548 * imap_continue_authenticate()
549 *
550 * Sends SASL continuation data or cancellation.
551 */
552static CURLcode imap_continue_authenticate(struct connectdata *conn,
553 const char *resp)
554{
555 struct imap_conn *imapc = &conn->proto.imapc;
556
557 return Curl_pp_sendf(&imapc->pp, "%s", resp);
558}
559
560/***********************************************************************
561 *
562 * imap_perform_authentication()
563 *
564 * Initiates the authentication sequence, with the appropriate SASL
565 * authentication mechanism, falling back to clear text should a common
566 * mechanism not be available between the client and server.
567 */
568static CURLcode imap_perform_authentication(struct connectdata *conn)
569{
570 CURLcode result = CURLE_OK;
571 struct imap_conn *imapc = &conn->proto.imapc;
572 saslprogress progress;
573
Alex Deymo486467e2017-12-19 19:04:07 +0100574 /* Check if already authenticated OR if there is enough data to authenticate
575 with and end the connect phase if we don't */
576 if(imapc->preauth ||
577 !Curl_sasl_can_authenticate(&imapc->sasl, conn)) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700578 state(conn, IMAP_STOP);
579 return result;
580 }
581
582 /* Calculate the SASL login details */
583 result = Curl_sasl_start(&imapc->sasl, conn, imapc->ir_supported, &progress);
584
585 if(!result) {
586 if(progress == SASL_INPROGRESS)
587 state(conn, IMAP_AUTHENTICATE);
588 else if(!imapc->login_disabled && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
589 /* Perform clear text authentication */
590 result = imap_perform_login(conn);
591 else {
592 /* Other mechanisms not supported */
593 infof(conn->data, "No known authentication mechanisms supported!\n");
594 result = CURLE_LOGIN_DENIED;
595 }
596 }
597
598 return result;
599}
600
601/***********************************************************************
602 *
603 * imap_perform_list()
604 *
605 * Sends a LIST command or an alternative custom request.
606 */
607static CURLcode imap_perform_list(struct connectdata *conn)
608{
609 CURLcode result = CURLE_OK;
Alex Deymoe3149cc2016-10-05 11:18:42 -0700610 struct Curl_easy *data = conn->data;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700611 struct IMAP *imap = data->req.protop;
612 char *mailbox;
613
614 if(imap->custom)
615 /* Send the custom request */
616 result = imap_sendf(conn, "%s%s", imap->custom,
617 imap->custom_params ? imap->custom_params : "");
618 else {
Alex Deymod15eaac2016-06-28 14:49:26 -0700619 /* Make sure the mailbox is in the correct atom format if necessary */
620 mailbox = imap->mailbox ? imap_atom(imap->mailbox, true) : strdup("");
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700621 if(!mailbox)
622 return CURLE_OUT_OF_MEMORY;
623
624 /* Send the LIST command */
625 result = imap_sendf(conn, "LIST \"%s\" *", mailbox);
626
627 free(mailbox);
628 }
629
630 if(!result)
631 state(conn, IMAP_LIST);
632
633 return result;
634}
635
636/***********************************************************************
637 *
638 * imap_perform_select()
639 *
640 * Sends a SELECT command to ask the server to change the selected mailbox.
641 */
642static CURLcode imap_perform_select(struct connectdata *conn)
643{
644 CURLcode result = CURLE_OK;
Alex Deymoe3149cc2016-10-05 11:18:42 -0700645 struct Curl_easy *data = conn->data;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700646 struct IMAP *imap = data->req.protop;
647 struct imap_conn *imapc = &conn->proto.imapc;
648 char *mailbox;
649
650 /* Invalidate old information as we are switching mailboxes */
651 Curl_safefree(imapc->mailbox);
652 Curl_safefree(imapc->mailbox_uidvalidity);
653
654 /* Check we have a mailbox */
655 if(!imap->mailbox) {
656 failf(conn->data, "Cannot SELECT without a mailbox.");
657 return CURLE_URL_MALFORMAT;
658 }
659
660 /* Make sure the mailbox is in the correct atom format */
Alex Deymod15eaac2016-06-28 14:49:26 -0700661 mailbox = imap_atom(imap->mailbox, false);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700662 if(!mailbox)
663 return CURLE_OUT_OF_MEMORY;
664
665 /* Send the SELECT command */
666 result = imap_sendf(conn, "SELECT %s", mailbox);
667
668 free(mailbox);
669
670 if(!result)
671 state(conn, IMAP_SELECT);
672
673 return result;
674}
675
676/***********************************************************************
677 *
678 * imap_perform_fetch()
679 *
680 * Sends a FETCH command to initiate the download of a message.
681 */
682static CURLcode imap_perform_fetch(struct connectdata *conn)
683{
684 CURLcode result = CURLE_OK;
685 struct IMAP *imap = conn->data->req.protop;
686
687 /* Check we have a UID */
688 if(!imap->uid) {
689 failf(conn->data, "Cannot FETCH without a UID.");
690 return CURLE_URL_MALFORMAT;
691 }
692
693 /* Send the FETCH command */
694 if(imap->partial)
695 result = imap_sendf(conn, "FETCH %s BODY[%s]<%s>",
696 imap->uid,
697 imap->section ? imap->section : "",
698 imap->partial);
699 else
700 result = imap_sendf(conn, "FETCH %s BODY[%s]",
701 imap->uid,
702 imap->section ? imap->section : "");
703
704 if(!result)
705 state(conn, IMAP_FETCH);
706
707 return result;
708}
709
710/***********************************************************************
711 *
712 * imap_perform_append()
713 *
714 * Sends an APPEND command to initiate the upload of a message.
715 */
716static CURLcode imap_perform_append(struct connectdata *conn)
717{
718 CURLcode result = CURLE_OK;
Alex Deymo486467e2017-12-19 19:04:07 +0100719 struct Curl_easy *data = conn->data;
720 struct IMAP *imap = data->req.protop;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700721 char *mailbox;
722
723 /* Check we have a mailbox */
724 if(!imap->mailbox) {
Alex Deymo486467e2017-12-19 19:04:07 +0100725 failf(data, "Cannot APPEND without a mailbox.");
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700726 return CURLE_URL_MALFORMAT;
727 }
728
Alex Deymo486467e2017-12-19 19:04:07 +0100729 /* Prepare the mime data if some. */
730 if(data->set.mimepost.kind != MIMEKIND_NONE) {
731 /* Use the whole structure as data. */
732 data->set.mimepost.flags &= ~MIME_BODY_ONLY;
733
734 /* Add external headers and mime version. */
735 curl_mime_headers(&data->set.mimepost, data->set.headers, 0);
736 result = Curl_mime_prepare_headers(&data->set.mimepost, NULL,
737 NULL, MIMESTRATEGY_MAIL);
738
739 if(!result)
740 if(!Curl_checkheaders(conn, "Mime-Version"))
741 result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
742 "Mime-Version: 1.0");
743
744 /* Make sure we will read the entire mime structure. */
745 if(!result)
746 result = Curl_mime_rewind(&data->set.mimepost);
747
748 if(result)
749 return result;
750
751 data->state.infilesize = Curl_mime_size(&data->set.mimepost);
752
753 /* Read from mime structure. */
754 data->state.fread_func = (curl_read_callback) Curl_mime_read;
755 data->state.in = (void *) &data->set.mimepost;
756 }
757
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700758 /* Check we know the size of the upload */
Alex Deymo486467e2017-12-19 19:04:07 +0100759 if(data->state.infilesize < 0) {
760 failf(data, "Cannot APPEND with unknown input file size\n");
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700761 return CURLE_UPLOAD_FAILED;
762 }
763
764 /* Make sure the mailbox is in the correct atom format */
Alex Deymod15eaac2016-06-28 14:49:26 -0700765 mailbox = imap_atom(imap->mailbox, false);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700766 if(!mailbox)
767 return CURLE_OUT_OF_MEMORY;
768
769 /* Send the APPEND command */
770 result = imap_sendf(conn, "APPEND %s (\\Seen) {%" CURL_FORMAT_CURL_OFF_T "}",
Alex Deymo486467e2017-12-19 19:04:07 +0100771 mailbox, data->state.infilesize);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700772
773 free(mailbox);
774
775 if(!result)
776 state(conn, IMAP_APPEND);
777
778 return result;
779}
780
781/***********************************************************************
782 *
783 * imap_perform_search()
784 *
785 * Sends a SEARCH command.
786 */
787static CURLcode imap_perform_search(struct connectdata *conn)
788{
789 CURLcode result = CURLE_OK;
790 struct IMAP *imap = conn->data->req.protop;
791
792 /* Check we have a query string */
793 if(!imap->query) {
794 failf(conn->data, "Cannot SEARCH without a query string.");
795 return CURLE_URL_MALFORMAT;
796 }
797
798 /* Send the SEARCH command */
799 result = imap_sendf(conn, "SEARCH %s", imap->query);
800
801 if(!result)
802 state(conn, IMAP_SEARCH);
803
804 return result;
805}
806
807/***********************************************************************
808 *
809 * imap_perform_logout()
810 *
811 * Performs the logout action prior to sclose() being called.
812 */
813static CURLcode imap_perform_logout(struct connectdata *conn)
814{
815 CURLcode result = CURLE_OK;
816
817 /* Send the LOGOUT command */
818 result = imap_sendf(conn, "LOGOUT");
819
820 if(!result)
821 state(conn, IMAP_LOGOUT);
822
823 return result;
824}
825
826/* For the initial server greeting */
827static CURLcode imap_state_servergreet_resp(struct connectdata *conn,
828 int imapcode,
829 imapstate instate)
830{
Alex Deymoe3149cc2016-10-05 11:18:42 -0700831 struct Curl_easy *data = conn->data;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700832 (void)instate; /* no use for this yet */
833
Alex Deymo486467e2017-12-19 19:04:07 +0100834 if(imapcode == IMAP_RESP_PREAUTH) {
835 /* PREAUTH */
836 struct imap_conn *imapc = &conn->proto.imapc;
837 imapc->preauth = TRUE;
838 infof(data, "PREAUTH connection, already authenticated!\n");
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700839 }
Alex Deymo486467e2017-12-19 19:04:07 +0100840 else if(imapcode != IMAP_RESP_OK) {
841 failf(data, "Got unexpected imap-server response");
842 return CURLE_WEIRD_SERVER_REPLY;
843 }
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700844
Alex Deymo486467e2017-12-19 19:04:07 +0100845 return imap_perform_capability(conn);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700846}
847
848/* For CAPABILITY responses */
849static CURLcode imap_state_capability_resp(struct connectdata *conn,
850 int imapcode,
851 imapstate instate)
852{
853 CURLcode result = CURLE_OK;
Alex Deymoe3149cc2016-10-05 11:18:42 -0700854 struct Curl_easy *data = conn->data;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700855 struct imap_conn *imapc = &conn->proto.imapc;
856 const char *line = data->state.buffer;
857 size_t wordlen;
858
859 (void)instate; /* no use for this yet */
860
861 /* Do we have a untagged response? */
862 if(imapcode == '*') {
863 line += 2;
864
865 /* Loop through the data line */
866 for(;;) {
867 while(*line &&
868 (*line == ' ' || *line == '\t' ||
869 *line == '\r' || *line == '\n')) {
870
871 line++;
872 }
873
874 if(!*line)
875 break;
876
877 /* Extract the word */
878 for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' &&
879 line[wordlen] != '\t' && line[wordlen] != '\r' &&
880 line[wordlen] != '\n';)
881 wordlen++;
882
883 /* Does the server support the STARTTLS capability? */
884 if(wordlen == 8 && !memcmp(line, "STARTTLS", 8))
885 imapc->tls_supported = TRUE;
886
887 /* Has the server explicitly disabled clear text authentication? */
888 else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13))
889 imapc->login_disabled = TRUE;
890
891 /* Does the server support the SASL-IR capability? */
892 else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7))
893 imapc->ir_supported = TRUE;
894
895 /* Do we have a SASL based authentication mechanism? */
896 else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
897 size_t llen;
898 unsigned int mechbit;
899
900 line += 5;
901 wordlen -= 5;
902
903 /* Test the word for a matching authentication mechanism */
Alex Deymod15eaac2016-06-28 14:49:26 -0700904 mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
905 if(mechbit && llen == wordlen)
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700906 imapc->sasl.authmechs |= mechbit;
907 }
908
909 line += wordlen;
910 }
911 }
Alex Deymo486467e2017-12-19 19:04:07 +0100912 else if(imapcode == IMAP_RESP_OK) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700913 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
914 /* We don't have a SSL/TLS connection yet, but SSL is requested */
915 if(imapc->tls_supported)
916 /* Switch to TLS connection now */
917 result = imap_perform_starttls(conn);
918 else if(data->set.use_ssl == CURLUSESSL_TRY)
919 /* Fallback and carry on with authentication */
920 result = imap_perform_authentication(conn);
921 else {
922 failf(data, "STARTTLS not supported.");
923 result = CURLE_USE_SSL_FAILED;
924 }
925 }
926 else
927 result = imap_perform_authentication(conn);
928 }
929 else
930 result = imap_perform_authentication(conn);
931
932 return result;
933}
934
935/* For STARTTLS responses */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100936static CURLcode imap_state_starttls_resp(struct connectdata *conn,
937 int imapcode,
938 imapstate instate)
939{
940 CURLcode result = CURLE_OK;
Alex Deymoe3149cc2016-10-05 11:18:42 -0700941 struct Curl_easy *data = conn->data;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700942
Kristian Monsen5ab50182010-05-14 18:53:44 +0100943 (void)instate; /* no use for this yet */
944
Alex Deymo486467e2017-12-19 19:04:07 +0100945 if(imapcode != IMAP_RESP_OK) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700946 if(data->set.use_ssl != CURLUSESSL_TRY) {
Elliott Hughes82be86d2017-09-20 17:00:17 -0700947 failf(data, "STARTTLS denied");
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700948 result = CURLE_USE_SSL_FAILED;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100949 }
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700950 else
951 result = imap_perform_authentication(conn);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100952 }
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700953 else
954 result = imap_perform_upgrade_tls(conn);
955
Kristian Monsen5ab50182010-05-14 18:53:44 +0100956 return result;
957}
958
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700959/* For SASL authentication responses */
960static CURLcode imap_state_auth_resp(struct connectdata *conn,
961 int imapcode,
962 imapstate instate)
963{
964 CURLcode result = CURLE_OK;
Alex Deymoe3149cc2016-10-05 11:18:42 -0700965 struct Curl_easy *data = conn->data;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700966 struct imap_conn *imapc = &conn->proto.imapc;
967 saslprogress progress;
968
969 (void)instate; /* no use for this yet */
970
971 result = Curl_sasl_continue(&imapc->sasl, conn, imapcode, &progress);
972 if(!result)
973 switch(progress) {
974 case SASL_DONE:
975 state(conn, IMAP_STOP); /* Authenticated */
976 break;
977 case SASL_IDLE: /* No mechanism left after cancellation */
978 if((!imapc->login_disabled) && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
979 /* Perform clear text authentication */
980 result = imap_perform_login(conn);
981 else {
982 failf(data, "Authentication cancelled");
983 result = CURLE_LOGIN_DENIED;
984 }
985 break;
986 default:
987 break;
988 }
989
990 return result;
991}
992
993/* For LOGIN responses */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100994static CURLcode imap_state_login_resp(struct connectdata *conn,
995 int imapcode,
996 imapstate instate)
997{
998 CURLcode result = CURLE_OK;
Alex Deymoe3149cc2016-10-05 11:18:42 -0700999 struct Curl_easy *data = conn->data;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001000
Kristian Monsen5ab50182010-05-14 18:53:44 +01001001 (void)instate; /* no use for this yet */
1002
Alex Deymo486467e2017-12-19 19:04:07 +01001003 if(imapcode != IMAP_RESP_OK) {
Kristian Monsen5ab50182010-05-14 18:53:44 +01001004 failf(data, "Access denied. %c", imapcode);
1005 result = CURLE_LOGIN_DENIED;
1006 }
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001007 else
1008 /* End of connect phase */
1009 state(conn, IMAP_STOP);
Kristian Monsen5ab50182010-05-14 18:53:44 +01001010
Kristian Monsen5ab50182010-05-14 18:53:44 +01001011 return result;
1012}
1013
Alex Deymod15eaac2016-06-28 14:49:26 -07001014/* For LIST and SEARCH responses */
1015static CURLcode imap_state_listsearch_resp(struct connectdata *conn,
1016 int imapcode,
1017 imapstate instate)
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001018{
1019 CURLcode result = CURLE_OK;
1020 char *line = conn->data->state.buffer;
1021 size_t len = strlen(line);
1022
1023 (void)instate; /* No use for this yet */
1024
1025 if(imapcode == '*') {
1026 /* Temporarily add the LF character back and send as body to the client */
1027 line[len] = '\n';
1028 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1029 line[len] = '\0';
1030 }
Alex Deymo486467e2017-12-19 19:04:07 +01001031 else if(imapcode != IMAP_RESP_OK)
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001032 result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
1033 else
1034 /* End of DO phase */
1035 state(conn, IMAP_STOP);
1036
1037 return result;
1038}
1039
1040/* For SELECT responses */
1041static CURLcode imap_state_select_resp(struct connectdata *conn, int imapcode,
1042 imapstate instate)
1043{
1044 CURLcode result = CURLE_OK;
Alex Deymoe3149cc2016-10-05 11:18:42 -07001045 struct Curl_easy *data = conn->data;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001046 struct IMAP *imap = conn->data->req.protop;
1047 struct imap_conn *imapc = &conn->proto.imapc;
1048 const char *line = data->state.buffer;
1049 char tmp[20];
1050
1051 (void)instate; /* no use for this yet */
1052
1053 if(imapcode == '*') {
1054 /* See if this is an UIDVALIDITY response */
1055 if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) {
1056 Curl_safefree(imapc->mailbox_uidvalidity);
1057 imapc->mailbox_uidvalidity = strdup(tmp);
1058 }
1059 }
Alex Deymo486467e2017-12-19 19:04:07 +01001060 else if(imapcode == IMAP_RESP_OK) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001061 /* Check if the UIDVALIDITY has been specified and matches */
1062 if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
Alex Deymo486467e2017-12-19 19:04:07 +01001063 !strcasecompare(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001064 failf(conn->data, "Mailbox UIDVALIDITY has changed");
1065 result = CURLE_REMOTE_FILE_NOT_FOUND;
1066 }
1067 else {
1068 /* Note the currently opened mailbox on this connection */
1069 imapc->mailbox = strdup(imap->mailbox);
1070
1071 if(imap->custom)
1072 result = imap_perform_list(conn);
1073 else if(imap->query)
1074 result = imap_perform_search(conn);
1075 else
1076 result = imap_perform_fetch(conn);
1077 }
1078 }
1079 else {
1080 failf(data, "Select failed");
1081 result = CURLE_LOGIN_DENIED;
1082 }
1083
1084 return result;
1085}
1086
1087/* For the (first line of the) FETCH responses */
1088static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode,
Kristian Monsen5ab50182010-05-14 18:53:44 +01001089 imapstate instate)
1090{
1091 CURLcode result = CURLE_OK;
Alex Deymoe3149cc2016-10-05 11:18:42 -07001092 struct Curl_easy *data = conn->data;
Kristian Monsen5ab50182010-05-14 18:53:44 +01001093 struct imap_conn *imapc = &conn->proto.imapc;
Kristian Monsen5ab50182010-05-14 18:53:44 +01001094 struct pingpong *pp = &imapc->pp;
1095 const char *ptr = data->state.buffer;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001096 bool parsed = FALSE;
Alex Deymod15eaac2016-06-28 14:49:26 -07001097 curl_off_t size = 0;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001098
Kristian Monsen5ab50182010-05-14 18:53:44 +01001099 (void)instate; /* no use for this yet */
1100
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001101 if(imapcode != '*') {
1102 Curl_pgrsSetDownloadSize(data, -1);
Kristian Monsen5ab50182010-05-14 18:53:44 +01001103 state(conn, IMAP_STOP);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001104 return CURLE_REMOTE_FILE_NOT_FOUND; /* TODO: Fix error code */
Kristian Monsen5ab50182010-05-14 18:53:44 +01001105 }
1106
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001107 /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
1108 the continuation data contained within the curly brackets */
Kristian Monsen5ab50182010-05-14 18:53:44 +01001109 while(*ptr && (*ptr != '{'))
1110 ptr++;
1111
1112 if(*ptr == '{') {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001113 char *endptr;
Alex Deymo486467e2017-12-19 19:04:07 +01001114 if(!curlx_strtoofft(ptr + 1, &endptr, 10, &size)) {
1115 if(endptr - ptr > 1 && endptr[0] == '}' &&
1116 endptr[1] == '\r' && endptr[2] == '\0')
1117 parsed = TRUE;
1118 }
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001119 }
Kristian Monsen5ab50182010-05-14 18:53:44 +01001120
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001121 if(parsed) {
1122 infof(data, "Found %" CURL_FORMAT_CURL_OFF_TU " bytes to download\n",
1123 size);
1124 Curl_pgrsSetDownloadSize(data, size);
Kristian Monsen5ab50182010-05-14 18:53:44 +01001125
1126 if(pp->cache) {
1127 /* At this point there is a bunch of data in the header "cache" that is
1128 actually body content, send it as body and then skip it. Do note
1129 that there may even be additional "headers" after the body. */
1130 size_t chunk = pp->cache_size;
1131
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001132 if(chunk > (size_t)size)
1133 /* The conversion from curl_off_t to size_t is always fine here */
1134 chunk = (size_t)size;
Kristian Monsen5ab50182010-05-14 18:53:44 +01001135
Alex Deymo486467e2017-12-19 19:04:07 +01001136 if(!chunk) {
1137 /* no size, we're done with the data */
1138 state(conn, IMAP_STOP);
1139 return CURLE_OK;
1140 }
Kristian Monsen5ab50182010-05-14 18:53:44 +01001141 result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
1142 if(result)
1143 return result;
1144
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001145 data->req.bytecount += chunk;
Kristian Monsen5ab50182010-05-14 18:53:44 +01001146
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001147 infof(data, "Written %" CURL_FORMAT_CURL_OFF_TU
1148 " bytes, %" CURL_FORMAT_CURL_OFF_TU
1149 " bytes are left for transfer\n", (curl_off_t)chunk,
1150 size - chunk);
1151
1152 /* Have we used the entire cache or just part of it?*/
Kristian Monsen5ab50182010-05-14 18:53:44 +01001153 if(pp->cache_size > chunk) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001154 /* Only part of it so shrink the cache to fit the trailing data */
1155 memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk);
Kristian Monsen5ab50182010-05-14 18:53:44 +01001156 pp->cache_size -= chunk;
1157 }
1158 else {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001159 /* Free the cache */
1160 Curl_safefree(pp->cache);
1161
1162 /* Reset the cache size */
Kristian Monsen5ab50182010-05-14 18:53:44 +01001163 pp->cache_size = 0;
1164 }
1165 }
1166
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001167 if(data->req.bytecount == size)
1168 /* The entire data is already transferred! */
Lucas Eckels9bd90e62012-08-06 15:07:02 -07001169 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001170 else {
Kristian Monsen5ab50182010-05-14 18:53:44 +01001171 /* IMAP download */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001172 data->req.maxdownload = size;
1173 Curl_setup_transfer(conn, FIRSTSOCKET, size, FALSE, NULL, -1, NULL);
1174 }
Kristian Monsen5ab50182010-05-14 18:53:44 +01001175 }
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001176 else {
Kristian Monsen5ab50182010-05-14 18:53:44 +01001177 /* We don't know how to parse this line */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001178 failf(pp->conn->data, "Failed to parse FETCH response.");
Elliott Hughescee03382017-06-23 12:17:18 -07001179 result = CURLE_WEIRD_SERVER_REPLY;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001180 }
Kristian Monsen5ab50182010-05-14 18:53:44 +01001181
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001182 /* End of DO phase */
Kristian Monsen5ab50182010-05-14 18:53:44 +01001183 state(conn, IMAP_STOP);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001184
Kristian Monsen5ab50182010-05-14 18:53:44 +01001185 return result;
1186}
1187
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001188/* For final FETCH responses performed after the download */
1189static CURLcode imap_state_fetch_final_resp(struct connectdata *conn,
1190 int imapcode,
1191 imapstate instate)
Kristian Monsen5ab50182010-05-14 18:53:44 +01001192{
1193 CURLcode result = CURLE_OK;
Kristian Monsen5ab50182010-05-14 18:53:44 +01001194
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001195 (void)instate; /* No use for this yet */
Kristian Monsen5ab50182010-05-14 18:53:44 +01001196
Alex Deymo486467e2017-12-19 19:04:07 +01001197 if(imapcode != IMAP_RESP_OK)
Elliott Hughescee03382017-06-23 12:17:18 -07001198 result = CURLE_WEIRD_SERVER_REPLY;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001199 else
1200 /* End of DONE phase */
1201 state(conn, IMAP_STOP);
Kristian Monsen5ab50182010-05-14 18:53:44 +01001202
Kristian Monsen5ab50182010-05-14 18:53:44 +01001203 return result;
1204}
1205
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001206/* For APPEND responses */
1207static CURLcode imap_state_append_resp(struct connectdata *conn, int imapcode,
Kristian Monsen5ab50182010-05-14 18:53:44 +01001208 imapstate instate)
1209{
1210 CURLcode result = CURLE_OK;
Alex Deymoe3149cc2016-10-05 11:18:42 -07001211 struct Curl_easy *data = conn->data;
Kristian Monsen5ab50182010-05-14 18:53:44 +01001212
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001213 (void)instate; /* No use for this yet */
1214
1215 if(imapcode != '+') {
1216 result = CURLE_UPLOAD_FAILED;
Kristian Monsen5ab50182010-05-14 18:53:44 +01001217 }
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001218 else {
1219 /* Set the progress upload size */
1220 Curl_pgrsSetUploadSize(data, data->state.infilesize);
1221
1222 /* IMAP upload */
1223 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1224
1225 /* End of DO phase */
1226 state(conn, IMAP_STOP);
1227 }
1228
1229 return result;
1230}
1231
1232/* For final APPEND responses performed after the upload */
1233static CURLcode imap_state_append_final_resp(struct connectdata *conn,
1234 int imapcode,
1235 imapstate instate)
1236{
1237 CURLcode result = CURLE_OK;
1238
1239 (void)instate; /* No use for this yet */
1240
Alex Deymo486467e2017-12-19 19:04:07 +01001241 if(imapcode != IMAP_RESP_OK)
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001242 result = CURLE_UPLOAD_FAILED;
Kristian Monsen5ab50182010-05-14 18:53:44 +01001243 else
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001244 /* End of DONE phase */
1245 state(conn, IMAP_STOP);
1246
1247 return result;
1248}
1249
Kristian Monsen5ab50182010-05-14 18:53:44 +01001250static CURLcode imap_statemach_act(struct connectdata *conn)
1251{
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001252 CURLcode result = CURLE_OK;
Kristian Monsen5ab50182010-05-14 18:53:44 +01001253 curl_socket_t sock = conn->sock[FIRSTSOCKET];
Kristian Monsen5ab50182010-05-14 18:53:44 +01001254 int imapcode;
1255 struct imap_conn *imapc = &conn->proto.imapc;
1256 struct pingpong *pp = &imapc->pp;
1257 size_t nread = 0;
1258
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001259 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
1260 if(imapc->state == IMAP_UPGRADETLS)
1261 return imap_perform_upgrade_tls(conn);
1262
1263 /* Flush any data that needs to be sent */
Kristian Monsen5ab50182010-05-14 18:53:44 +01001264 if(pp->sendleft)
1265 return Curl_pp_flushsend(pp);
1266
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001267 do {
1268 /* Read the response from the server */
1269 result = Curl_pp_readresp(sock, pp, &imapcode, &nread);
Kristian Monsen5ab50182010-05-14 18:53:44 +01001270 if(result)
1271 return result;
Kristian Monsen5ab50182010-05-14 18:53:44 +01001272
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001273 /* Was there an error parsing the response line? */
1274 if(imapcode == -1)
Elliott Hughescee03382017-06-23 12:17:18 -07001275 return CURLE_WEIRD_SERVER_REPLY;
Kristian Monsen5ab50182010-05-14 18:53:44 +01001276
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001277 if(!imapcode)
Kristian Monsen5ab50182010-05-14 18:53:44 +01001278 break;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001279
1280 /* We have now received a full IMAP server response */
1281 switch(imapc->state) {
1282 case IMAP_SERVERGREET:
1283 result = imap_state_servergreet_resp(conn, imapcode, imapc->state);
1284 break;
1285
1286 case IMAP_CAPABILITY:
1287 result = imap_state_capability_resp(conn, imapcode, imapc->state);
1288 break;
1289
1290 case IMAP_STARTTLS:
1291 result = imap_state_starttls_resp(conn, imapcode, imapc->state);
1292 break;
1293
1294 case IMAP_AUTHENTICATE:
1295 result = imap_state_auth_resp(conn, imapcode, imapc->state);
1296 break;
1297
1298 case IMAP_LOGIN:
1299 result = imap_state_login_resp(conn, imapcode, imapc->state);
1300 break;
1301
1302 case IMAP_LIST:
Alex Deymod15eaac2016-06-28 14:49:26 -07001303 result = imap_state_listsearch_resp(conn, imapcode, imapc->state);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001304 break;
1305
1306 case IMAP_SELECT:
1307 result = imap_state_select_resp(conn, imapcode, imapc->state);
1308 break;
1309
1310 case IMAP_FETCH:
1311 result = imap_state_fetch_resp(conn, imapcode, imapc->state);
1312 break;
1313
1314 case IMAP_FETCH_FINAL:
1315 result = imap_state_fetch_final_resp(conn, imapcode, imapc->state);
1316 break;
1317
1318 case IMAP_APPEND:
1319 result = imap_state_append_resp(conn, imapcode, imapc->state);
1320 break;
1321
1322 case IMAP_APPEND_FINAL:
1323 result = imap_state_append_final_resp(conn, imapcode, imapc->state);
1324 break;
1325
1326 case IMAP_SEARCH:
Alex Deymod15eaac2016-06-28 14:49:26 -07001327 result = imap_state_listsearch_resp(conn, imapcode, imapc->state);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001328 break;
1329
1330 case IMAP_LOGOUT:
1331 /* fallthrough, just stop! */
1332 default:
1333 /* internal error */
1334 state(conn, IMAP_STOP);
1335 break;
1336 }
1337 } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
Kristian Monsen5ab50182010-05-14 18:53:44 +01001338
1339 return result;
1340}
1341
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001342/* Called repeatedly until done from multi.c */
1343static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done)
1344{
1345 CURLcode result = CURLE_OK;
1346 struct imap_conn *imapc = &conn->proto.imapc;
1347
1348 if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) {
1349 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
1350 if(result || !imapc->ssldone)
1351 return result;
1352 }
1353
1354 result = Curl_pp_statemach(&imapc->pp, FALSE);
1355 *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
1356
1357 return result;
1358}
1359
1360static CURLcode imap_block_statemach(struct connectdata *conn)
1361{
1362 CURLcode result = CURLE_OK;
1363 struct imap_conn *imapc = &conn->proto.imapc;
1364
1365 while(imapc->state != IMAP_STOP && !result)
1366 result = Curl_pp_statemach(&imapc->pp, TRUE);
1367
1368 return result;
1369}
1370
Alex Deymoe3149cc2016-10-05 11:18:42 -07001371/* Allocate and initialize the struct IMAP for the current Curl_easy if
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001372 required */
Kristian Monsen5ab50182010-05-14 18:53:44 +01001373static CURLcode imap_init(struct connectdata *conn)
1374{
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001375 CURLcode result = CURLE_OK;
Alex Deymoe3149cc2016-10-05 11:18:42 -07001376 struct Curl_easy *data = conn->data;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001377 struct IMAP *imap;
Kristian Monsen5ab50182010-05-14 18:53:44 +01001378
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001379 imap = data->req.protop = calloc(sizeof(struct IMAP), 1);
1380 if(!imap)
1381 result = CURLE_OUT_OF_MEMORY;
Kristian Monsen5ab50182010-05-14 18:53:44 +01001382
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001383 return result;
Kristian Monsen5ab50182010-05-14 18:53:44 +01001384}
1385
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001386/* For the IMAP "protocol connect" and "doing" phases only */
1387static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
1388 int numsocks)
1389{
1390 return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks);
1391}
1392
1393/***********************************************************************
1394 *
1395 * imap_connect()
1396 *
1397 * This function should do everything that is to be considered a part of the
1398 * connection phase.
Kristian Monsen5ab50182010-05-14 18:53:44 +01001399 *
1400 * The variable 'done' points to will be TRUE if the protocol-layer connect
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001401 * phase is done when this function returns, or FALSE if not.
Kristian Monsen5ab50182010-05-14 18:53:44 +01001402 */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001403static CURLcode imap_connect(struct connectdata *conn, bool *done)
Kristian Monsen5ab50182010-05-14 18:53:44 +01001404{
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001405 CURLcode result = CURLE_OK;
Kristian Monsen5ab50182010-05-14 18:53:44 +01001406 struct imap_conn *imapc = &conn->proto.imapc;
Kristian Monsen5ab50182010-05-14 18:53:44 +01001407 struct pingpong *pp = &imapc->pp;
1408
1409 *done = FALSE; /* default to not done yet */
1410
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001411 /* We always support persistent connections in IMAP */
1412 connkeep(conn, "IMAP default");
Kristian Monsen5ab50182010-05-14 18:53:44 +01001413
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001414 /* Set the default response time-out */
1415 pp->response_time = RESP_TIMEOUT;
Kristian Monsen5ab50182010-05-14 18:53:44 +01001416 pp->statemach_act = imap_statemach_act;
1417 pp->endofresp = imap_endofresp;
1418 pp->conn = conn;
1419
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001420 /* Set the default preferred authentication type and mechanism */
1421 imapc->preftype = IMAP_TYPE_ANY;
1422 Curl_sasl_init(&imapc->sasl, &saslimap);
Kristian Monsen5ab50182010-05-14 18:53:44 +01001423
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001424 /* Initialise the pingpong layer */
1425 Curl_pp_init(pp);
Kristian Monsen5ab50182010-05-14 18:53:44 +01001426
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001427 /* Parse the URL options */
1428 result = imap_parse_url_options(conn);
1429 if(result)
1430 return result;
Kristian Monsen5ab50182010-05-14 18:53:44 +01001431
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001432 /* Start off waiting for the server greeting response */
Kristian Monsen5ab50182010-05-14 18:53:44 +01001433 state(conn, IMAP_SERVERGREET);
Kristian Monsen5ab50182010-05-14 18:53:44 +01001434
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001435 /* Start off with an response id of '*' */
1436 strcpy(imapc->resptag, "*");
1437
1438 result = imap_multi_statemach(conn, done);
Kristian Monsen5ab50182010-05-14 18:53:44 +01001439
1440 return result;
1441}
1442
1443/***********************************************************************
1444 *
1445 * imap_done()
1446 *
1447 * The DONE function. This does what needs to be done after a single DO has
1448 * performed.
1449 *
1450 * Input argument is already checked for validity.
1451 */
1452static CURLcode imap_done(struct connectdata *conn, CURLcode status,
1453 bool premature)
1454{
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001455 CURLcode result = CURLE_OK;
Alex Deymoe3149cc2016-10-05 11:18:42 -07001456 struct Curl_easy *data = conn->data;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001457 struct IMAP *imap = data->req.protop;
1458
Kristian Monsen5ab50182010-05-14 18:53:44 +01001459 (void)premature;
1460
1461 if(!imap)
Kristian Monsen5ab50182010-05-14 18:53:44 +01001462 return CURLE_OK;
1463
1464 if(status) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001465 connclose(conn, "IMAP done with bad status"); /* marked for closure */
1466 result = status; /* use the already set error code */
1467 }
1468 else if(!data->set.connect_only && !imap->custom &&
Alex Deymo486467e2017-12-19 19:04:07 +01001469 (imap->uid || data->set.upload ||
1470 data->set.mimepost.kind != MIMEKIND_NONE)) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001471 /* Handle responses after FETCH or APPEND transfer has finished */
Alex Deymo486467e2017-12-19 19:04:07 +01001472 if(!data->set.upload && data->set.mimepost.kind == MIMEKIND_NONE)
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001473 state(conn, IMAP_FETCH_FINAL);
1474 else {
1475 /* End the APPEND command first by sending an empty line */
1476 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "");
1477 if(!result)
1478 state(conn, IMAP_APPEND_FINAL);
1479 }
1480
1481 /* Run the state-machine
1482
1483 TODO: when the multi interface is used, this _really_ should be using
1484 the imap_multi_statemach function but we have no general support for
Alex Deymod15eaac2016-06-28 14:49:26 -07001485 non-blocking DONE operations!
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001486 */
1487 if(!result)
1488 result = imap_block_statemach(conn);
Kristian Monsen5ab50182010-05-14 18:53:44 +01001489 }
1490
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001491 /* Cleanup our per-request based variables */
1492 Curl_safefree(imap->mailbox);
1493 Curl_safefree(imap->uidvalidity);
1494 Curl_safefree(imap->uid);
1495 Curl_safefree(imap->section);
1496 Curl_safefree(imap->partial);
1497 Curl_safefree(imap->query);
1498 Curl_safefree(imap->custom);
1499 Curl_safefree(imap->custom_params);
1500
1501 /* Clear the transfer mode for the next request */
Kristian Monsen5ab50182010-05-14 18:53:44 +01001502 imap->transfer = FTPTRANSFER_BODY;
1503
1504 return result;
1505}
1506
1507/***********************************************************************
1508 *
1509 * imap_perform()
1510 *
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001511 * This is the actual DO function for IMAP. Fetch or append a message, or do
1512 * other things according to the options previously setup.
Kristian Monsen5ab50182010-05-14 18:53:44 +01001513 */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001514static CURLcode imap_perform(struct connectdata *conn, bool *connected,
1515 bool *dophase_done)
Kristian Monsen5ab50182010-05-14 18:53:44 +01001516{
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001517 /* This is IMAP and no proxy */
1518 CURLcode result = CURLE_OK;
Alex Deymoe3149cc2016-10-05 11:18:42 -07001519 struct Curl_easy *data = conn->data;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001520 struct IMAP *imap = data->req.protop;
1521 struct imap_conn *imapc = &conn->proto.imapc;
1522 bool selected = FALSE;
Kristian Monsen5ab50182010-05-14 18:53:44 +01001523
1524 DEBUGF(infof(conn->data, "DO phase starts\n"));
1525
1526 if(conn->data->set.opt_no_body) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001527 /* Requested no body means no transfer */
Kristian Monsen5ab50182010-05-14 18:53:44 +01001528 imap->transfer = FTPTRANSFER_INFO;
1529 }
1530
1531 *dophase_done = FALSE; /* not done yet */
1532
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001533 /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
1534 has already been selected on this connection */
1535 if(imap->mailbox && imapc->mailbox &&
Alex Deymo486467e2017-12-19 19:04:07 +01001536 strcasecompare(imap->mailbox, imapc->mailbox) &&
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001537 (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
Alex Deymo486467e2017-12-19 19:04:07 +01001538 strcasecompare(imap->uidvalidity, imapc->mailbox_uidvalidity)))
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001539 selected = TRUE;
1540
1541 /* Start the first command in the DO phase */
Alex Deymo486467e2017-12-19 19:04:07 +01001542 if(conn->data->set.upload || data->set.mimepost.kind != MIMEKIND_NONE)
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001543 /* APPEND can be executed directly */
1544 result = imap_perform_append(conn);
1545 else if(imap->custom && (selected || !imap->mailbox))
1546 /* Custom command using the same mailbox or no mailbox */
1547 result = imap_perform_list(conn);
1548 else if(!imap->custom && selected && imap->uid)
1549 /* FETCH from the same mailbox */
1550 result = imap_perform_fetch(conn);
1551 else if(!imap->custom && selected && imap->query)
1552 /* SEARCH the current mailbox */
1553 result = imap_perform_search(conn);
1554 else if(imap->mailbox && !selected &&
1555 (imap->custom || imap->uid || imap->query))
1556 /* SELECT the mailbox */
1557 result = imap_perform_select(conn);
1558 else
1559 /* LIST */
1560 result = imap_perform_list(conn);
1561
Kristian Monsen5ab50182010-05-14 18:53:44 +01001562 if(result)
1563 return result;
1564
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001565 /* Run the state-machine */
1566 result = imap_multi_statemach(conn, dophase_done);
1567
1568 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
Kristian Monsen5ab50182010-05-14 18:53:44 +01001569
1570 if(*dophase_done)
1571 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1572
1573 return result;
1574}
1575
1576/***********************************************************************
1577 *
1578 * imap_do()
1579 *
1580 * This function is registered as 'curl_do' function. It decodes the path
1581 * parts etc as a wrapper to the actual DO function (imap_perform).
1582 *
1583 * The input argument is already checked for validity.
1584 */
1585static CURLcode imap_do(struct connectdata *conn, bool *done)
1586{
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001587 CURLcode result = CURLE_OK;
Kristian Monsen5ab50182010-05-14 18:53:44 +01001588
1589 *done = FALSE; /* default to false */
1590
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001591 /* Parse the URL path */
1592 result = imap_parse_url_path(conn);
Kristian Monsen5ab50182010-05-14 18:53:44 +01001593 if(result)
1594 return result;
Kristian Monsen5ab50182010-05-14 18:53:44 +01001595
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001596 /* Parse the custom request */
1597 result = imap_parse_custom_request(conn);
1598 if(result)
1599 return result;
1600
1601 result = imap_regular_transfer(conn, done);
Kristian Monsen5ab50182010-05-14 18:53:44 +01001602
1603 return result;
1604}
1605
1606/***********************************************************************
1607 *
1608 * imap_disconnect()
1609 *
1610 * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
1611 * resources. BLOCKING.
1612 */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001613static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
Kristian Monsen5ab50182010-05-14 18:53:44 +01001614{
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001615 struct imap_conn *imapc = &conn->proto.imapc;
1616
1617 /* We cannot send quit unconditionally. If this connection is stale or
1618 bad in any way, sending quit and waiting around here will make the
1619 disconnect wait in vain and cause more problems than we need to. */
Kristian Monsen5ab50182010-05-14 18:53:44 +01001620
1621 /* The IMAP session may or may not have been allocated/setup at this
1622 point! */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001623 if(!dead_connection && imapc->pp.conn && imapc->pp.conn->bits.protoconnstart)
1624 if(!imap_perform_logout(conn))
1625 (void)imap_block_statemach(conn); /* ignore errors on LOGOUT */
Kristian Monsen5ab50182010-05-14 18:53:44 +01001626
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001627 /* Disconnect from the server */
Kristian Monsen5ab50182010-05-14 18:53:44 +01001628 Curl_pp_disconnect(&imapc->pp);
1629
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001630 /* Cleanup the SASL module */
1631 Curl_sasl_cleanup(conn, imapc->sasl.authused);
1632
1633 /* Cleanup our connection based variables */
Kristian Monsen5ab50182010-05-14 18:53:44 +01001634 Curl_safefree(imapc->mailbox);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001635 Curl_safefree(imapc->mailbox_uidvalidity);
Kristian Monsen5ab50182010-05-14 18:53:44 +01001636
1637 return CURLE_OK;
1638}
1639
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001640/* Call this when the DO phase has completed */
1641static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
Kristian Monsen5ab50182010-05-14 18:53:44 +01001642{
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001643 struct IMAP *imap = conn->data->req.protop;
Kristian Monsen5ab50182010-05-14 18:53:44 +01001644
Kristian Monsen5ab50182010-05-14 18:53:44 +01001645 (void)connected;
1646
1647 if(imap->transfer != FTPTRANSFER_BODY)
1648 /* no data to transfer */
Lucas Eckels9bd90e62012-08-06 15:07:02 -07001649 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
Kristian Monsen5ab50182010-05-14 18:53:44 +01001650
Lucas Eckels9bd90e62012-08-06 15:07:02 -07001651 return CURLE_OK;
Kristian Monsen5ab50182010-05-14 18:53:44 +01001652}
1653
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001654/* Called from multi.c while DOing */
1655static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
Kristian Monsen5ab50182010-05-14 18:53:44 +01001656{
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001657 CURLcode result = imap_multi_statemach(conn, dophase_done);
Kristian Monsen5ab50182010-05-14 18:53:44 +01001658
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001659 if(result)
1660 DEBUGF(infof(conn->data, "DO phase failed\n"));
1661 else if(*dophase_done) {
Kristian Monsen5ab50182010-05-14 18:53:44 +01001662 result = imap_dophase_done(conn, FALSE /* not connected */);
1663
1664 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1665 }
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001666
Kristian Monsen5ab50182010-05-14 18:53:44 +01001667 return result;
1668}
1669
1670/***********************************************************************
1671 *
1672 * imap_regular_transfer()
1673 *
1674 * The input argument is already checked for validity.
1675 *
1676 * Performs all commands done before a regular transfer between a local and a
1677 * remote host.
Kristian Monsen5ab50182010-05-14 18:53:44 +01001678 */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001679static CURLcode imap_regular_transfer(struct connectdata *conn,
1680 bool *dophase_done)
Kristian Monsen5ab50182010-05-14 18:53:44 +01001681{
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001682 CURLcode result = CURLE_OK;
1683 bool connected = FALSE;
Alex Deymoe3149cc2016-10-05 11:18:42 -07001684 struct Curl_easy *data = conn->data;
Kristian Monsen5ab50182010-05-14 18:53:44 +01001685
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001686 /* Make sure size is unknown at this point */
1687 data->req.size = -1;
1688
1689 /* Set the progress data */
Kristian Monsen5ab50182010-05-14 18:53:44 +01001690 Curl_pgrsSetUploadCounter(data, 0);
1691 Curl_pgrsSetDownloadCounter(data, 0);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001692 Curl_pgrsSetUploadSize(data, -1);
1693 Curl_pgrsSetDownloadSize(data, -1);
Kristian Monsen5ab50182010-05-14 18:53:44 +01001694
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001695 /* Carry out the perform */
1696 result = imap_perform(conn, &connected, dophase_done);
Kristian Monsen5ab50182010-05-14 18:53:44 +01001697
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001698 /* Perform post DO phase operations if necessary */
1699 if(!result && *dophase_done)
Kristian Monsen5ab50182010-05-14 18:53:44 +01001700 result = imap_dophase_done(conn, connected);
Kristian Monsen5ab50182010-05-14 18:53:44 +01001701
1702 return result;
1703}
1704
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001705static CURLcode imap_setup_connection(struct connectdata *conn)
Kristian Monsen5ab50182010-05-14 18:53:44 +01001706{
Alex Deymoe3149cc2016-10-05 11:18:42 -07001707 struct Curl_easy *data = conn->data;
Kristian Monsen5ab50182010-05-14 18:53:44 +01001708
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001709 /* Initialise the IMAP layer */
1710 CURLcode result = imap_init(conn);
1711 if(result)
1712 return result;
1713
Alex Deymod15eaac2016-06-28 14:49:26 -07001714 /* Clear the TLS upgraded flag */
1715 conn->tls_upgraded = FALSE;
Kristian Monsen5ab50182010-05-14 18:53:44 +01001716 data->state.path++; /* don't include the initial slash */
1717
1718 return CURLE_OK;
1719}
1720
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001721/***********************************************************************
1722 *
1723 * imap_sendf()
1724 *
Elliott Hughes82be86d2017-09-20 17:00:17 -07001725 * Sends the formatted string as an IMAP command to the server.
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001726 *
1727 * Designed to never block.
1728 */
1729static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...)
1730{
1731 CURLcode result = CURLE_OK;
1732 struct imap_conn *imapc = &conn->proto.imapc;
1733 char *taggedfmt;
1734 va_list ap;
1735
1736 DEBUGASSERT(fmt);
1737
1738 /* Calculate the next command ID wrapping at 3 digits */
1739 imapc->cmdid = (imapc->cmdid + 1) % 1000;
1740
1741 /* Calculate the tag based on the connection ID and command ID */
1742 snprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
1743 'A' + curlx_sltosi(conn->connection_id % 26), imapc->cmdid);
1744
1745 /* Prefix the format with the tag */
1746 taggedfmt = aprintf("%s %s", imapc->resptag, fmt);
1747 if(!taggedfmt)
1748 return CURLE_OUT_OF_MEMORY;
1749
1750 /* Send the data with the tag */
1751 va_start(ap, fmt);
1752 result = Curl_pp_vsendf(&imapc->pp, taggedfmt, ap);
1753 va_end(ap);
1754
1755 free(taggedfmt);
1756
1757 return result;
1758}
1759
1760/***********************************************************************
1761 *
1762 * imap_atom()
1763 *
1764 * Checks the input string for characters that need escaping and returns an
1765 * atom ready for sending to the server.
1766 *
1767 * The returned string needs to be freed.
1768 *
1769 */
Alex Deymod15eaac2016-06-28 14:49:26 -07001770static char *imap_atom(const char *str, bool escape_only)
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001771{
Alex Deymod15eaac2016-06-28 14:49:26 -07001772 /* !checksrc! disable PARENBRACE 1 */
1773 const char atom_specials[] = "(){ %*]";
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001774 const char *p1;
1775 char *p2;
1776 size_t backsp_count = 0;
1777 size_t quote_count = 0;
Alex Deymod15eaac2016-06-28 14:49:26 -07001778 bool others_exists = FALSE;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001779 size_t newlen = 0;
1780 char *newstr = NULL;
1781
1782 if(!str)
1783 return NULL;
1784
Alex Deymod15eaac2016-06-28 14:49:26 -07001785 /* Look for "atom-specials", counting the backslash and quote characters as
1786 these will need escapping */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001787 p1 = str;
1788 while(*p1) {
1789 if(*p1 == '\\')
1790 backsp_count++;
1791 else if(*p1 == '"')
1792 quote_count++;
Alex Deymod15eaac2016-06-28 14:49:26 -07001793 else if(!escape_only) {
1794 const char *p3 = atom_specials;
1795
1796 while(*p3 && !others_exists) {
1797 if(*p1 == *p3)
1798 others_exists = TRUE;
1799
1800 p3++;
1801 }
1802 }
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001803
1804 p1++;
1805 }
1806
Alex Deymod15eaac2016-06-28 14:49:26 -07001807 /* Does the input contain any "atom-special" characters? */
1808 if(!backsp_count && !quote_count && !others_exists)
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001809 return strdup(str);
1810
1811 /* Calculate the new string length */
Alex Deymo486467e2017-12-19 19:04:07 +01001812 newlen = strlen(str) + backsp_count + quote_count + (escape_only ? 0 : 2);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001813
1814 /* Allocate the new string */
1815 newstr = (char *) malloc((newlen + 1) * sizeof(char));
1816 if(!newstr)
1817 return NULL;
1818
1819 /* Surround the string in quotes if necessary */
1820 p2 = newstr;
Alex Deymo486467e2017-12-19 19:04:07 +01001821 if(!escape_only) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001822 newstr[0] = '"';
1823 newstr[newlen - 1] = '"';
1824 p2++;
1825 }
1826
1827 /* Copy the string, escaping backslash and quote characters along the way */
1828 p1 = str;
1829 while(*p1) {
1830 if(*p1 == '\\' || *p1 == '"') {
1831 *p2 = '\\';
1832 p2++;
1833 }
1834
1835 *p2 = *p1;
1836
1837 p1++;
1838 p2++;
1839 }
1840
1841 /* Terminate the string */
1842 newstr[newlen] = '\0';
1843
1844 return newstr;
1845}
1846
1847/***********************************************************************
1848 *
1849 * imap_is_bchar()
1850 *
1851 * Portable test of whether the specified char is a "bchar" as defined in the
1852 * grammar of RFC-5092.
1853 */
1854static bool imap_is_bchar(char ch)
1855{
1856 switch(ch) {
1857 /* bchar */
1858 case ':': case '@': case '/':
1859 /* bchar -> achar */
1860 case '&': case '=':
1861 /* bchar -> achar -> uchar -> unreserved */
1862 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
1863 case '7': case '8': case '9':
1864 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
1865 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
1866 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
1867 case 'V': case 'W': case 'X': case 'Y': case 'Z':
1868 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
1869 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
1870 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
1871 case 'v': case 'w': case 'x': case 'y': case 'z':
1872 case '-': case '.': case '_': case '~':
1873 /* bchar -> achar -> uchar -> sub-delims-sh */
1874 case '!': case '$': case '\'': case '(': case ')': case '*':
1875 case '+': case ',':
1876 /* bchar -> achar -> uchar -> pct-encoded */
1877 case '%': /* HEXDIG chars are already included above */
1878 return true;
1879
1880 default:
1881 return false;
1882 }
1883}
1884
1885/***********************************************************************
1886 *
1887 * imap_parse_url_options()
1888 *
1889 * Parse the URL login options.
1890 */
1891static CURLcode imap_parse_url_options(struct connectdata *conn)
1892{
1893 CURLcode result = CURLE_OK;
1894 struct imap_conn *imapc = &conn->proto.imapc;
1895 const char *ptr = conn->options;
1896
1897 imapc->sasl.resetprefs = TRUE;
1898
1899 while(!result && ptr && *ptr) {
1900 const char *key = ptr;
1901 const char *value;
1902
1903 while(*ptr && *ptr != '=')
1904 ptr++;
1905
1906 value = ptr + 1;
1907
1908 while(*ptr && *ptr != ';')
1909 ptr++;
1910
Elliott Hughescee03382017-06-23 12:17:18 -07001911 if(strncasecompare(key, "AUTH=", 5))
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001912 result = Curl_sasl_parse_url_auth_option(&imapc->sasl,
1913 value, ptr - value);
1914 else
1915 result = CURLE_URL_MALFORMAT;
1916
1917 if(*ptr == ';')
1918 ptr++;
1919 }
1920
1921 switch(imapc->sasl.prefmech) {
1922 case SASL_AUTH_NONE:
1923 imapc->preftype = IMAP_TYPE_NONE;
1924 break;
1925 case SASL_AUTH_DEFAULT:
1926 imapc->preftype = IMAP_TYPE_ANY;
1927 break;
1928 default:
1929 imapc->preftype = IMAP_TYPE_SASL;
1930 break;
1931 }
1932
1933 return result;
1934}
1935
1936/***********************************************************************
1937 *
1938 * imap_parse_url_path()
1939 *
1940 * Parse the URL path into separate path components.
1941 *
1942 */
1943static CURLcode imap_parse_url_path(struct connectdata *conn)
1944{
1945 /* The imap struct is already initialised in imap_connect() */
1946 CURLcode result = CURLE_OK;
Alex Deymoe3149cc2016-10-05 11:18:42 -07001947 struct Curl_easy *data = conn->data;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001948 struct IMAP *imap = data->req.protop;
1949 const char *begin = data->state.path;
1950 const char *ptr = begin;
1951
1952 /* See how much of the URL is a valid path and decode it */
1953 while(imap_is_bchar(*ptr))
1954 ptr++;
1955
1956 if(ptr != begin) {
1957 /* Remove the trailing slash if present */
1958 const char *end = ptr;
1959 if(end > begin && end[-1] == '/')
1960 end--;
1961
1962 result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL,
1963 TRUE);
1964 if(result)
1965 return result;
1966 }
1967 else
1968 imap->mailbox = NULL;
1969
1970 /* There can be any number of parameters in the form ";NAME=VALUE" */
1971 while(*ptr == ';') {
1972 char *name;
1973 char *value;
1974 size_t valuelen;
1975
1976 /* Find the length of the name parameter */
1977 begin = ++ptr;
1978 while(*ptr && *ptr != '=')
1979 ptr++;
1980
1981 if(!*ptr)
1982 return CURLE_URL_MALFORMAT;
1983
1984 /* Decode the name parameter */
1985 result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE);
1986 if(result)
1987 return result;
1988
1989 /* Find the length of the value parameter */
1990 begin = ++ptr;
1991 while(imap_is_bchar(*ptr))
1992 ptr++;
1993
1994 /* Decode the value parameter */
1995 result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE);
1996 if(result) {
1997 free(name);
1998 return result;
1999 }
2000
2001 DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value));
2002
2003 /* Process the known hierarchical parameters (UIDVALIDITY, UID, SECTION and
2004 PARTIAL) stripping of the trailing slash character if it is present.
2005
2006 Note: Unknown parameters trigger a URL_MALFORMAT error. */
Elliott Hughescee03382017-06-23 12:17:18 -07002007 if(strcasecompare(name, "UIDVALIDITY") && !imap->uidvalidity) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07002008 if(valuelen > 0 && value[valuelen - 1] == '/')
2009 value[valuelen - 1] = '\0';
2010
2011 imap->uidvalidity = value;
2012 value = NULL;
2013 }
Elliott Hughescee03382017-06-23 12:17:18 -07002014 else if(strcasecompare(name, "UID") && !imap->uid) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07002015 if(valuelen > 0 && value[valuelen - 1] == '/')
2016 value[valuelen - 1] = '\0';
2017
2018 imap->uid = value;
2019 value = NULL;
2020 }
Elliott Hughescee03382017-06-23 12:17:18 -07002021 else if(strcasecompare(name, "SECTION") && !imap->section) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07002022 if(valuelen > 0 && value[valuelen - 1] == '/')
2023 value[valuelen - 1] = '\0';
2024
2025 imap->section = value;
2026 value = NULL;
2027 }
Elliott Hughescee03382017-06-23 12:17:18 -07002028 else if(strcasecompare(name, "PARTIAL") && !imap->partial) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07002029 if(valuelen > 0 && value[valuelen - 1] == '/')
2030 value[valuelen - 1] = '\0';
2031
2032 imap->partial = value;
2033 value = NULL;
2034 }
2035 else {
2036 free(name);
2037 free(value);
2038
2039 return CURLE_URL_MALFORMAT;
2040 }
2041
2042 free(name);
2043 free(value);
2044 }
2045
2046 /* Does the URL contain a query parameter? Only valid when we have a mailbox
2047 and no UID as per RFC-5092 */
2048 if(imap->mailbox && !imap->uid && *ptr == '?') {
2049 /* Find the length of the query parameter */
2050 begin = ++ptr;
2051 while(imap_is_bchar(*ptr))
2052 ptr++;
2053
2054 /* Decode the query parameter */
2055 result = Curl_urldecode(data, begin, ptr - begin, &imap->query, NULL,
2056 TRUE);
2057 if(result)
2058 return result;
2059 }
2060
2061 /* Any extra stuff at the end of the URL is an error */
2062 if(*ptr)
2063 return CURLE_URL_MALFORMAT;
2064
2065 return CURLE_OK;
2066}
2067
2068/***********************************************************************
2069 *
2070 * imap_parse_custom_request()
2071 *
2072 * Parse the custom request.
2073 */
2074static CURLcode imap_parse_custom_request(struct connectdata *conn)
2075{
2076 CURLcode result = CURLE_OK;
Alex Deymoe3149cc2016-10-05 11:18:42 -07002077 struct Curl_easy *data = conn->data;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07002078 struct IMAP *imap = data->req.protop;
2079 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2080
2081 if(custom) {
2082 /* URL decode the custom request */
2083 result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE);
2084
2085 /* Extract the parameters if specified */
2086 if(!result) {
2087 const char *params = imap->custom;
2088
2089 while(*params && *params != ' ')
2090 params++;
2091
2092 if(*params) {
2093 imap->custom_params = strdup(params);
2094 imap->custom[params - imap->custom] = '\0';
2095
2096 if(!imap->custom_params)
2097 result = CURLE_OUT_OF_MEMORY;
2098 }
2099 }
2100 }
2101
2102 return result;
2103}
2104
Kristian Monsen5ab50182010-05-14 18:53:44 +01002105#endif /* CURL_DISABLE_IMAP */