Coverage Report

Created: 2025-07-12 06:36

/src/curl/lib/imap.c
Line
Count
Source (jump to first uncovered line)
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9
 *
10
 * This software is licensed as described in the file COPYING, which
11
 * you should have received as part of this distribution. The terms
12
 * are also available at https://curl.se/docs/copyright.html.
13
 *
14
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15
 * copies of the Software, and permit persons to whom the Software is
16
 * furnished to do so, under the terms of the COPYING file.
17
 *
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
 * KIND, either express or implied.
20
 *
21
 * SPDX-License-Identifier: curl
22
 *
23
 * RFC2195 CRAM-MD5 authentication
24
 * RFC2595 Using TLS with IMAP, POP3 and ACAP
25
 * RFC2831 DIGEST-MD5 authentication
26
 * RFC3501 IMAPv4 protocol
27
 * RFC4422 Simple Authentication and Security Layer (SASL)
28
 * RFC4616 PLAIN authentication
29
 * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
30
 * RFC4959 IMAP Extension for SASL Initial Client Response
31
 * RFC5092 IMAP URL Scheme
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
#include "curlx/dynbuf.h"
40
41
#ifndef CURL_DISABLE_IMAP
42
43
#ifdef HAVE_NETINET_IN_H
44
#include <netinet/in.h>
45
#endif
46
#ifdef HAVE_ARPA_INET_H
47
#include <arpa/inet.h>
48
#endif
49
#ifdef HAVE_NETDB_H
50
#include <netdb.h>
51
#endif
52
#ifdef __VMS
53
#include <in.h>
54
#include <inet.h>
55
#endif
56
57
#include <curl/curl.h>
58
#include "urldata.h"
59
#include "sendf.h"
60
#include "hostip.h"
61
#include "progress.h"
62
#include "transfer.h"
63
#include "escape.h"
64
#include "http.h" /* for HTTP proxy tunnel stuff */
65
#include "socks.h"
66
#include "imap.h"
67
#include "mime.h"
68
#include "curlx/strparse.h"
69
#include "strcase.h"
70
#include "vtls/vtls.h"
71
#include "cfilters.h"
72
#include "connect.h"
73
#include "select.h"
74
#include "multiif.h"
75
#include "url.h"
76
#include "bufref.h"
77
#include "curl_sasl.h"
78
#include "curlx/warnless.h"
79
#include "curl_ctype.h"
80
81
/* The last 3 #include files should be in this order */
82
#include "curl_printf.h"
83
#include "curl_memory.h"
84
#include "memdebug.h"
85
86
87
/* meta key for storing protocol meta at easy handle */
88
5.76M
#define CURL_META_IMAP_EASY   "meta:proto:imap:easy"
89
/* meta key for storing protocol meta at connection */
90
5.78M
#define CURL_META_IMAP_CONN   "meta:proto:imap:conn"
91
92
typedef enum {
93
  IMAP_STOP,         /* do nothing state, stops the state machine */
94
  IMAP_SERVERGREET,  /* waiting for the initial greeting immediately after
95
                        a connect */
96
  IMAP_CAPABILITY,
97
  IMAP_STARTTLS,
98
  IMAP_UPGRADETLS,   /* asynchronously upgrade the connection to SSL/TLS
99
                       (multi mode only) */
100
  IMAP_AUTHENTICATE,
101
  IMAP_LOGIN,
102
  IMAP_LIST,
103
  IMAP_SELECT,
104
  IMAP_FETCH,
105
  IMAP_FETCH_FINAL,
106
  IMAP_APPEND,
107
  IMAP_APPEND_FINAL,
108
  IMAP_SEARCH,
109
  IMAP_LOGOUT,
110
  IMAP_LAST          /* never used */
111
} imapstate;
112
113
/* imap_conn is used for struct connection-oriented data */
114
struct imap_conn {
115
  struct pingpong pp;
116
  struct SASL sasl;           /* SASL-related parameters */
117
  struct dynbuf dyn;          /* for the IMAP commands */
118
  char *mailbox;              /* The last selected mailbox */
119
  char *mailbox_uidvalidity;  /* UIDVALIDITY parsed from select response */
120
  imapstate state;            /* Always use imap.c:state() to change state! */
121
  char resptag[5];            /* Response tag to wait for */
122
  unsigned char preftype;     /* Preferred authentication type */
123
  unsigned char cmdid;        /* Last used command ID */
124
  BIT(ssldone);               /* Is connect() over SSL done? */
125
  BIT(preauth);               /* Is this connection PREAUTH? */
126
  BIT(tls_supported);         /* StartTLS capability supported by server */
127
  BIT(login_disabled);        /* LOGIN command disabled by server */
128
  BIT(ir_supported);          /* Initial response supported by server */
129
};
130
131
/* This IMAP struct is used in the Curl_easy. All IMAP data that is
132
   connection-oriented must be in imap_conn to properly deal with the fact that
133
   perhaps the Curl_easy is changed between the times the connection is
134
   used. */
135
struct IMAP {
136
  curl_pp_transfer transfer;
137
  char *mailbox;          /* Mailbox to select */
138
  char *uidvalidity;      /* UIDVALIDITY to check in select */
139
  char *uid;              /* Message UID to fetch */
140
  char *mindex;           /* Index in mail box of mail to fetch */
141
  char *section;          /* Message SECTION to fetch */
142
  char *partial;          /* Message PARTIAL to fetch */
143
  char *query;            /* Query to search for */
144
  char *custom;           /* Custom request */
145
  char *custom_params;    /* Parameters for the custom request */
146
};
147
148
149
/* Local API functions */
150
static CURLcode imap_regular_transfer(struct Curl_easy *data,
151
                                      struct IMAP *imap,
152
                                      bool *done);
153
static CURLcode imap_do(struct Curl_easy *data, bool *done);
154
static CURLcode imap_done(struct Curl_easy *data, CURLcode status,
155
                          bool premature);
156
static CURLcode imap_connect(struct Curl_easy *data, bool *done);
157
static CURLcode imap_disconnect(struct Curl_easy *data,
158
                                struct connectdata *conn, bool dead);
159
static CURLcode imap_multi_statemach(struct Curl_easy *data, bool *done);
160
static int imap_getsock(struct Curl_easy *data, struct connectdata *conn,
161
                        curl_socket_t *socks);
162
static CURLcode imap_doing(struct Curl_easy *data, bool *dophase_done);
163
static CURLcode imap_setup_connection(struct Curl_easy *data,
164
                                      struct connectdata *conn);
165
static char *imap_atom(const char *str, bool escape_only);
166
static CURLcode imap_sendf(struct Curl_easy *data,
167
                           struct imap_conn *imapc,
168
                           const char *fmt, ...) CURL_PRINTF(3, 4);
169
static CURLcode imap_parse_url_options(struct connectdata *conn,
170
                                       struct imap_conn *imapc);
171
static CURLcode imap_parse_url_path(struct Curl_easy *data,
172
                                    struct IMAP *imap);
173
static CURLcode imap_parse_custom_request(struct Curl_easy *data,
174
                                          struct IMAP *imap);
175
static CURLcode imap_perform_authenticate(struct Curl_easy *data,
176
                                          const char *mech,
177
                                          const struct bufref *initresp);
178
static CURLcode imap_continue_authenticate(struct Curl_easy *data,
179
                                           const char *mech,
180
                                           const struct bufref *resp);
181
static CURLcode imap_cancel_authenticate(struct Curl_easy *data,
182
                                         const char *mech);
183
static CURLcode imap_get_message(struct Curl_easy *data, struct bufref *out);
184
static void imap_easy_reset(struct IMAP *imap);
185
186
/*
187
 * IMAP protocol handler.
188
 */
189
190
const struct Curl_handler Curl_handler_imap = {
191
  "imap",                           /* scheme */
192
  imap_setup_connection,            /* setup_connection */
193
  imap_do,                          /* do_it */
194
  imap_done,                        /* done */
195
  ZERO_NULL,                        /* do_more */
196
  imap_connect,                     /* connect_it */
197
  imap_multi_statemach,             /* connecting */
198
  imap_doing,                       /* doing */
199
  imap_getsock,                     /* proto_getsock */
200
  imap_getsock,                     /* doing_getsock */
201
  ZERO_NULL,                        /* domore_getsock */
202
  ZERO_NULL,                        /* perform_getsock */
203
  imap_disconnect,                  /* disconnect */
204
  ZERO_NULL,                        /* write_resp */
205
  ZERO_NULL,                        /* write_resp_hd */
206
  ZERO_NULL,                        /* connection_check */
207
  ZERO_NULL,                        /* attach connection */
208
  ZERO_NULL,                        /* follow */
209
  PORT_IMAP,                        /* defport */
210
  CURLPROTO_IMAP,                   /* protocol */
211
  CURLPROTO_IMAP,                   /* family */
212
  PROTOPT_CLOSEACTION|              /* flags */
213
  PROTOPT_URLOPTIONS
214
};
215
216
#ifdef USE_SSL
217
/*
218
 * IMAPS protocol handler.
219
 */
220
221
const struct Curl_handler Curl_handler_imaps = {
222
  "imaps",                          /* scheme */
223
  imap_setup_connection,            /* setup_connection */
224
  imap_do,                          /* do_it */
225
  imap_done,                        /* done */
226
  ZERO_NULL,                        /* do_more */
227
  imap_connect,                     /* connect_it */
228
  imap_multi_statemach,             /* connecting */
229
  imap_doing,                       /* doing */
230
  imap_getsock,                     /* proto_getsock */
231
  imap_getsock,                     /* doing_getsock */
232
  ZERO_NULL,                        /* domore_getsock */
233
  ZERO_NULL,                        /* perform_getsock */
234
  imap_disconnect,                  /* disconnect */
235
  ZERO_NULL,                        /* write_resp */
236
  ZERO_NULL,                        /* write_resp_hd */
237
  ZERO_NULL,                        /* connection_check */
238
  ZERO_NULL,                        /* attach connection */
239
  ZERO_NULL,                        /* follow */
240
  PORT_IMAPS,                       /* defport */
241
  CURLPROTO_IMAPS,                  /* protocol */
242
  CURLPROTO_IMAP,                   /* family */
243
  PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */
244
  PROTOPT_URLOPTIONS
245
};
246
#endif
247
248
4.76k
#define IMAP_RESP_OK       1
249
1.67k
#define IMAP_RESP_NOT_OK   2
250
1.86k
#define IMAP_RESP_PREAUTH  3
251
252
/* SASL parameters for the imap protocol */
253
static const struct SASLproto saslimap = {
254
  "imap",                     /* The service name */
255
  imap_perform_authenticate,  /* Send authentication command */
256
  imap_continue_authenticate, /* Send authentication continuation */
257
  imap_cancel_authenticate,   /* Send authentication cancellation */
258
  imap_get_message,           /* Get SASL response message */
259
  0,                          /* No maximum initial response length */
260
  '+',                        /* Code received when continuation is expected */
261
  IMAP_RESP_OK,               /* Code to receive upon authentication success */
262
  SASL_AUTH_DEFAULT,          /* Default mechanisms */
263
  SASL_FLAG_BASE64            /* Configuration flags */
264
};
265
266
struct ulbits {
267
  int bit;
268
  const char *flag;
269
};
270
271
/***********************************************************************
272
 *
273
 * imap_matchresp()
274
 *
275
 * Determines whether the untagged response is related to the specified
276
 * command by checking if it is in format "* <command-name> ..." or
277
 * "* <number> <command-name> ...".
278
 *
279
 * The "* " marker is assumed to have already been checked by the caller.
280
 */
281
static bool imap_matchresp(const char *line, size_t len, const char *cmd)
282
31.0k
{
283
31.0k
  const char *end = line + len;
284
31.0k
  size_t cmd_len = strlen(cmd);
285
286
  /* Skip the untagged response marker */
287
31.0k
  line += 2;
288
289
  /* Do we have a number after the marker? */
290
31.0k
  if(line < end && ISDIGIT(*line)) {
291
    /* Skip the number */
292
10.2k
    do
293
19.3k
      line++;
294
19.3k
    while(line < end && ISDIGIT(*line));
295
296
    /* Do we have the space character? */
297
10.2k
    if(line == end || *line != ' ')
298
7.06k
      return FALSE;
299
300
3.20k
    line++;
301
3.20k
  }
302
303
  /* Does the command name match and is it followed by a space character or at
304
     the end of line? */
305
24.0k
  if(line + cmd_len <= end && curl_strnequal(line, cmd, cmd_len) &&
306
24.0k
     (line[cmd_len] == ' ' || line + cmd_len + 2 == end))
307
5.20k
    return TRUE;
308
309
18.8k
  return FALSE;
310
24.0k
}
311
312
/***********************************************************************
313
 *
314
 * imap_endofresp()
315
 *
316
 * Checks whether the given string is a valid tagged, untagged or continuation
317
 * response which can be processed by the response handler.
318
 */
319
static bool imap_endofresp(struct Curl_easy *data, struct connectdata *conn,
320
                           const char *line, size_t len, int *resp)
321
162k
{
322
162k
  struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN);
323
162k
  struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY);
324
162k
  const char *id;
325
162k
  size_t id_len;
326
327
162k
  DEBUGASSERT(imapc);
328
162k
  DEBUGASSERT(imap);
329
162k
  if(!imapc || !imap)
330
0
    return FALSE;
331
332
  /* Do we have a tagged command response? */
333
162k
  id = imapc->resptag;
334
162k
  id_len = strlen(id);
335
162k
  if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') {
336
4.12k
    line += id_len + 1;
337
4.12k
    len -= id_len + 1;
338
339
4.12k
    if(len >= 2 && !memcmp(line, "OK", 2))
340
2.35k
      *resp = IMAP_RESP_OK;
341
1.77k
    else if(len >= 7 && !memcmp(line, "PREAUTH", 7))
342
91
      *resp = IMAP_RESP_PREAUTH;
343
1.67k
    else
344
1.67k
      *resp = IMAP_RESP_NOT_OK;
345
346
4.12k
    return TRUE;
347
4.12k
  }
348
349
  /* Do we have an untagged command response? */
350
158k
  if(len >= 2 && !memcmp("* ", line, 2)) {
351
34.9k
    switch(imapc->state) {
352
      /* States which are interested in untagged responses */
353
6.20k
      case IMAP_CAPABILITY:
354
6.20k
        if(!imap_matchresp(line, len, "CAPABILITY"))
355
6.20k
          return FALSE;
356
0
        break;
357
358
10.4k
      case IMAP_LIST:
359
10.4k
        if((!imap->custom && !imap_matchresp(line, len, "LIST")) ||
360
10.4k
           (imap->custom && !imap_matchresp(line, len, imap->custom) &&
361
9.36k
            (!curl_strequal(imap->custom, "STORE") ||
362
7.60k
             !imap_matchresp(line, len, "FETCH")) &&
363
9.36k
            !curl_strequal(imap->custom, "SELECT") &&
364
9.36k
            !curl_strequal(imap->custom, "EXAMINE") &&
365
9.36k
            !curl_strequal(imap->custom, "SEARCH") &&
366
9.36k
            !curl_strequal(imap->custom, "EXPUNGE") &&
367
9.36k
            !curl_strequal(imap->custom, "LSUB") &&
368
9.36k
            !curl_strequal(imap->custom, "UID") &&
369
9.36k
            !curl_strequal(imap->custom, "GETQUOTAROOT") &&
370
9.36k
            !curl_strequal(imap->custom, "NOOP")))
371
3.85k
          return FALSE;
372
6.62k
        break;
373
374
6.62k
      case IMAP_SELECT:
375
        /* SELECT is special in that its untagged responses do not have a
376
           common prefix so accept anything! */
377
3.17k
        break;
378
379
4.90k
      case IMAP_FETCH:
380
4.90k
        if(!imap_matchresp(line, len, "FETCH"))
381
4.40k
          return FALSE;
382
497
        break;
383
384
9.12k
      case IMAP_SEARCH:
385
9.12k
        if(!imap_matchresp(line, len, "SEARCH"))
386
6.26k
          return FALSE;
387
2.85k
        break;
388
389
      /* Ignore other untagged responses */
390
2.85k
      default:
391
1.10k
        return FALSE;
392
34.9k
    }
393
394
13.1k
    *resp = '*';
395
13.1k
    return TRUE;
396
34.9k
  }
397
398
  /* Do we have a continuation response? This should be a + symbol followed by
399
     a space and optionally some text as per RFC-3501 for the AUTHENTICATE and
400
     APPEND commands and as outlined in Section 4. Examples of RFC-4959 but
401
     some email servers ignore this and only send a single + instead. */
402
123k
  if(!imap->custom && ((len == 3 && line[0] == '+') ||
403
117k
                       (len >= 2 && !memcmp("+ ", line, 2)))) {
404
483
    switch(imapc->state) {
405
      /* States which are interested in continuation responses */
406
0
      case IMAP_AUTHENTICATE:
407
441
      case IMAP_APPEND:
408
441
        *resp = '+';
409
441
        break;
410
411
42
      default:
412
42
        failf(data, "Unexpected continuation response");
413
42
        *resp = -1;
414
42
        break;
415
483
    }
416
417
483
    return TRUE;
418
483
  }
419
420
122k
  return FALSE; /* Nothing for us */
421
123k
}
422
423
/***********************************************************************
424
 *
425
 * imap_get_message()
426
 *
427
 * Gets the authentication message from the response buffer.
428
 */
429
static CURLcode imap_get_message(struct Curl_easy *data, struct bufref *out)
430
0
{
431
0
  struct imap_conn *imapc =
432
0
    Curl_conn_meta_get(data->conn, CURL_META_IMAP_CONN);
433
0
  char *message;
434
0
  size_t len;
435
436
0
  if(!imapc)
437
0
    return CURLE_FAILED_INIT;
438
439
0
  message = curlx_dyn_ptr(&imapc->pp.recvbuf);
440
0
  len = imapc->pp.nfinal;
441
0
  if(len > 2) {
442
    /* Find the start of the message */
443
0
    len -= 2;
444
0
    for(message += 2; *message == ' ' || *message == '\t'; message++, len--)
445
0
      ;
446
447
    /* Find the end of the message */
448
0
    while(len--)
449
0
      if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
450
0
         message[len] != '\t')
451
0
        break;
452
453
    /* Terminate the message */
454
0
    message[++len] = '\0';
455
0
    Curl_bufref_set(out, message, len, NULL);
456
0
  }
457
0
  else
458
    /* junk input => zero length output */
459
0
    Curl_bufref_set(out, "", 0, NULL);
460
461
0
  return CURLE_OK;
462
0
}
463
464
/***********************************************************************
465
 *
466
 * imap_state()
467
 *
468
 * This is the ONLY way to change IMAP state!
469
 */
470
static void imap_state(struct Curl_easy *data,
471
                       struct imap_conn *imapc,
472
                       imapstate newstate)
473
10.4k
{
474
10.4k
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
475
  /* for debug purposes */
476
10.4k
  static const char * const names[]={
477
10.4k
    "STOP",
478
10.4k
    "SERVERGREET",
479
10.4k
    "CAPABILITY",
480
10.4k
    "STARTTLS",
481
10.4k
    "UPGRADETLS",
482
10.4k
    "AUTHENTICATE",
483
10.4k
    "LOGIN",
484
10.4k
    "LIST",
485
10.4k
    "SELECT",
486
10.4k
    "FETCH",
487
10.4k
    "FETCH_FINAL",
488
10.4k
    "APPEND",
489
10.4k
    "APPEND_FINAL",
490
10.4k
    "SEARCH",
491
10.4k
    "LOGOUT",
492
    /* LAST */
493
10.4k
  };
494
495
10.4k
  if(imapc->state != newstate)
496
10.4k
    infof(data, "IMAP %p state change from %s to %s",
497
10.4k
          (void *)imapc, names[imapc->state], names[newstate]);
498
10.4k
#endif
499
10.4k
  (void)data;
500
10.4k
  imapc->state = newstate;
501
10.4k
}
502
503
/***********************************************************************
504
 *
505
 * imap_perform_capability()
506
 *
507
 * Sends the CAPABILITY command in order to obtain a list of server side
508
 * supported capabilities.
509
 */
510
static CURLcode imap_perform_capability(struct Curl_easy *data,
511
                                        struct imap_conn *imapc)
512
1.71k
{
513
1.71k
  CURLcode result = CURLE_OK;
514
515
1.71k
  imapc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */
516
1.71k
  imapc->sasl.authused = SASL_AUTH_NONE;  /* Clear the auth. mechanism used */
517
1.71k
  imapc->tls_supported = FALSE;           /* Clear the TLS capability */
518
519
  /* Send the CAPABILITY command */
520
1.71k
  result = imap_sendf(data, imapc, "CAPABILITY");
521
522
1.71k
  if(!result)
523
1.71k
    imap_state(data, imapc, IMAP_CAPABILITY);
524
525
1.71k
  return result;
526
1.71k
}
527
528
/***********************************************************************
529
 *
530
 * imap_perform_starttls()
531
 *
532
 * Sends the STARTTLS command to start the upgrade to TLS.
533
 */
534
static CURLcode imap_perform_starttls(struct Curl_easy *data,
535
                                      struct imap_conn *imapc)
536
0
{
537
  /* Send the STARTTLS command */
538
0
  CURLcode result = imap_sendf(data, imapc, "STARTTLS");
539
540
0
  if(!result)
541
0
    imap_state(data, imapc, IMAP_STARTTLS);
542
543
0
  return result;
544
0
}
545
546
/***********************************************************************
547
 *
548
 * imap_perform_upgrade_tls()
549
 *
550
 * Performs the upgrade to TLS.
551
 */
552
static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data,
553
                                         struct imap_conn *imapc,
554
                                         struct connectdata *conn)
555
0
{
556
0
#ifdef USE_SSL
557
  /* Start the SSL connection */
558
0
  CURLcode result;
559
0
  bool ssldone = FALSE;
560
561
0
  if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
562
0
    result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
563
0
    if(result)
564
0
      goto out;
565
    /* Change the connection handler */
566
0
    conn->handler = &Curl_handler_imaps;
567
0
  }
568
569
0
  DEBUGASSERT(!imapc->ssldone);
570
0
  result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
571
0
  DEBUGF(infof(data, "imap_perform_upgrade_tls, connect -> %d, %d",
572
0
         result, ssldone));
573
0
  if(!result && ssldone) {
574
0
    imapc->ssldone = ssldone;
575
     /* perform CAPA now, changes imapc->state out of IMAP_UPGRADETLS */
576
0
     result = imap_perform_capability(data, imapc);
577
0
  }
578
0
out:
579
0
  return result;
580
#else
581
  (void)data;
582
  (void)imapc;
583
  (void)conn;
584
  return CURLE_NOT_BUILT_IN;
585
#endif
586
0
}
587
588
/***********************************************************************
589
 *
590
 * imap_perform_login()
591
 *
592
 * Sends a clear text LOGIN command to authenticate with.
593
 */
594
static CURLcode imap_perform_login(struct Curl_easy *data,
595
                                   struct imap_conn *imapc,
596
                                   struct connectdata *conn)
597
48
{
598
48
  CURLcode result = CURLE_OK;
599
48
  char *user;
600
48
  char *passwd;
601
602
  /* Check we have a username and password to authenticate with and end the
603
     connect phase if we do not */
604
48
  if(!data->state.aptr.user) {
605
0
    imap_state(data, imapc, IMAP_STOP);
606
607
0
    return result;
608
0
  }
609
610
  /* Make sure the username and password are in the correct atom format */
611
48
  user = imap_atom(conn->user, FALSE);
612
48
  passwd = imap_atom(conn->passwd, FALSE);
613
614
  /* Send the LOGIN command */
615
48
  result = imap_sendf(data, imapc, "LOGIN %s %s", user ? user : "",
616
48
                      passwd ? passwd : "");
617
618
48
  free(user);
619
48
  free(passwd);
620
621
48
  if(!result)
622
48
    imap_state(data, imapc, IMAP_LOGIN);
623
624
48
  return result;
625
48
}
626
627
/***********************************************************************
628
 *
629
 * imap_perform_authenticate()
630
 *
631
 * Sends an AUTHENTICATE command allowing the client to login with the given
632
 * SASL authentication mechanism.
633
 */
634
static CURLcode imap_perform_authenticate(struct Curl_easy *data,
635
                                          const char *mech,
636
                                          const struct bufref *initresp)
637
0
{
638
0
  struct imap_conn *imapc =
639
0
    Curl_conn_meta_get(data->conn, CURL_META_IMAP_CONN);
640
0
  CURLcode result = CURLE_OK;
641
0
  const char *ir = (const char *) Curl_bufref_ptr(initresp);
642
643
0
  if(!imapc)
644
0
    return CURLE_FAILED_INIT;
645
0
  if(ir) {
646
    /* Send the AUTHENTICATE command with the initial response */
647
0
    result = imap_sendf(data, imapc, "AUTHENTICATE %s %s", mech, ir);
648
0
  }
649
0
  else {
650
    /* Send the AUTHENTICATE command */
651
0
    result = imap_sendf(data, imapc, "AUTHENTICATE %s", mech);
652
0
  }
653
654
0
  return result;
655
0
}
656
657
/***********************************************************************
658
 *
659
 * imap_continue_authenticate()
660
 *
661
 * Sends SASL continuation data.
662
 */
663
static CURLcode imap_continue_authenticate(struct Curl_easy *data,
664
                                           const char *mech,
665
                                           const struct bufref *resp)
666
0
{
667
0
  struct imap_conn *imapc =
668
0
    Curl_conn_meta_get(data->conn, CURL_META_IMAP_CONN);
669
670
0
  (void)mech;
671
0
  if(!imapc)
672
0
    return CURLE_FAILED_INIT;
673
0
  return Curl_pp_sendf(data, &imapc->pp,
674
0
                       "%s", (const char *) Curl_bufref_ptr(resp));
675
0
}
676
677
/***********************************************************************
678
 *
679
 * imap_cancel_authenticate()
680
 *
681
 * Sends SASL cancellation.
682
 */
683
static CURLcode imap_cancel_authenticate(struct Curl_easy *data,
684
                                         const char *mech)
685
0
{
686
0
  struct imap_conn *imapc =
687
0
    Curl_conn_meta_get(data->conn, CURL_META_IMAP_CONN);
688
689
0
  (void)mech;
690
0
  if(!imapc)
691
0
    return CURLE_FAILED_INIT;
692
0
  return Curl_pp_sendf(data, &imapc->pp, "*");
693
0
}
694
695
/***********************************************************************
696
 *
697
 * imap_perform_authentication()
698
 *
699
 * Initiates the authentication sequence, with the appropriate SASL
700
 * authentication mechanism, falling back to clear text should a common
701
 * mechanism not be available between the client and server.
702
 */
703
static CURLcode imap_perform_authentication(struct Curl_easy *data,
704
                                            struct imap_conn *imapc)
705
1.63k
{
706
1.63k
  CURLcode result = CURLE_OK;
707
1.63k
  saslprogress progress;
708
709
  /* Check if already authenticated OR if there is enough data to authenticate
710
     with and end the connect phase if we do not */
711
1.63k
  if(imapc->preauth ||
712
1.63k
     !Curl_sasl_can_authenticate(&imapc->sasl, data)) {
713
1.58k
    imap_state(data, imapc, IMAP_STOP);
714
1.58k
    return result;
715
1.58k
  }
716
717
  /* Calculate the SASL login details */
718
49
  result = Curl_sasl_start(&imapc->sasl, data, imapc->ir_supported, &progress);
719
720
49
  if(!result) {
721
49
    if(progress == SASL_INPROGRESS)
722
0
      imap_state(data, imapc, IMAP_AUTHENTICATE);
723
49
    else if(!imapc->login_disabled && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
724
      /* Perform clear text authentication */
725
48
      result = imap_perform_login(data, imapc, data->conn);
726
1
    else
727
1
      result = Curl_sasl_is_blocked(&imapc->sasl, data);
728
49
  }
729
730
49
  return result;
731
1.63k
}
732
733
/***********************************************************************
734
 *
735
 * imap_perform_list()
736
 *
737
 * Sends a LIST command or an alternative custom request.
738
 */
739
static CURLcode imap_perform_list(struct Curl_easy *data,
740
                                  struct imap_conn *imapc,
741
                                  struct IMAP *imap)
742
334
{
743
334
  CURLcode result = CURLE_OK;
744
745
334
  if(imap->custom)
746
    /* Send the custom request */
747
149
    result = imap_sendf(data, imapc, "%s%s", imap->custom,
748
149
                        imap->custom_params ? imap->custom_params : "");
749
185
  else {
750
    /* Make sure the mailbox is in the correct atom format if necessary */
751
185
    char *mailbox = imap->mailbox ? imap_atom(imap->mailbox, TRUE)
752
185
                                  : strdup("");
753
185
    if(!mailbox)
754
1
      return CURLE_OUT_OF_MEMORY;
755
756
    /* Send the LIST command */
757
184
    result = imap_sendf(data, imapc, "LIST \"%s\" *", mailbox);
758
759
184
    free(mailbox);
760
184
  }
761
762
333
  if(!result)
763
333
    imap_state(data, imapc, IMAP_LIST);
764
765
333
  return result;
766
334
}
767
768
/***********************************************************************
769
 *
770
 * imap_perform_select()
771
 *
772
 * Sends a SELECT command to ask the server to change the selected mailbox.
773
 */
774
static CURLcode imap_perform_select(struct Curl_easy *data,
775
                                    struct imap_conn *imapc,
776
                                    struct IMAP *imap)
777
725
{
778
725
  CURLcode result = CURLE_OK;
779
725
  char *mailbox;
780
781
  /* Invalidate old information as we are switching mailboxes */
782
725
  Curl_safefree(imapc->mailbox);
783
725
  Curl_safefree(imapc->mailbox_uidvalidity);
784
785
  /* Check we have a mailbox */
786
725
  if(!imap->mailbox) {
787
0
    failf(data, "Cannot SELECT without a mailbox.");
788
0
    return CURLE_URL_MALFORMAT;
789
0
  }
790
791
  /* Make sure the mailbox is in the correct atom format */
792
725
  mailbox = imap_atom(imap->mailbox, FALSE);
793
725
  if(!mailbox)
794
5
    return CURLE_OUT_OF_MEMORY;
795
796
  /* Send the SELECT command */
797
720
  result = imap_sendf(data, imapc, "SELECT %s", mailbox);
798
799
720
  free(mailbox);
800
801
720
  if(!result)
802
720
    imap_state(data, imapc, IMAP_SELECT);
803
804
720
  return result;
805
725
}
806
807
/***********************************************************************
808
 *
809
 * imap_perform_fetch()
810
 *
811
 * Sends a FETCH command to initiate the download of a message.
812
 */
813
static CURLcode imap_perform_fetch(struct Curl_easy *data,
814
                                   struct imap_conn *imapc,
815
                                   struct IMAP *imap)
816
560
{
817
560
  CURLcode result = CURLE_OK;
818
  /* Check we have a UID */
819
560
  if(imap->uid) {
820
821
    /* Send the FETCH command */
822
559
    if(imap->partial)
823
0
      result = imap_sendf(data, imapc, "UID FETCH %s BODY[%s]<%s>",
824
0
                          imap->uid, imap->section ? imap->section : "",
825
0
                          imap->partial);
826
559
    else
827
559
      result = imap_sendf(data, imapc, "UID FETCH %s BODY[%s]",
828
559
                          imap->uid, imap->section ? imap->section : "");
829
559
  }
830
1
  else if(imap->mindex) {
831
    /* Send the FETCH command */
832
1
    if(imap->partial)
833
0
      result = imap_sendf(data, imapc, "FETCH %s BODY[%s]<%s>",
834
0
                          imap->mindex, imap->section ? imap->section : "",
835
0
                          imap->partial);
836
1
    else
837
1
      result = imap_sendf(data, imapc, "FETCH %s BODY[%s]",
838
1
                          imap->mindex, imap->section ? imap->section : "");
839
1
  }
840
0
  else {
841
0
    failf(data, "Cannot FETCH without a UID.");
842
0
    return CURLE_URL_MALFORMAT;
843
0
  }
844
560
  if(!result)
845
560
    imap_state(data, imapc, IMAP_FETCH);
846
847
560
  return result;
848
560
}
849
850
/***********************************************************************
851
 *
852
 * imap_perform_append()
853
 *
854
 * Sends an APPEND command to initiate the upload of a message.
855
 */
856
static CURLcode imap_perform_append(struct Curl_easy *data,
857
                                    struct imap_conn *imapc,
858
                                    struct IMAP *imap)
859
534
{
860
534
  CURLcode result = CURLE_OK;
861
534
  char *mailbox;
862
534
  struct dynbuf flags;
863
864
  /* Check we have a mailbox */
865
534
  if(!imap->mailbox) {
866
2
    failf(data, "Cannot APPEND without a mailbox.");
867
2
    return CURLE_URL_MALFORMAT;
868
2
  }
869
870
532
#ifndef CURL_DISABLE_MIME
871
  /* Prepare the mime data if some. */
872
532
  if(data->set.mimepost.kind != MIMEKIND_NONE) {
873
    /* Use the whole structure as data. */
874
445
    data->set.mimepost.flags &= ~(unsigned int)MIME_BODY_ONLY;
875
876
    /* Add external headers and mime version. */
877
445
    curl_mime_headers(&data->set.mimepost, data->set.headers, 0);
878
445
    result = Curl_mime_prepare_headers(data, &data->set.mimepost, NULL,
879
445
                                       NULL, MIMESTRATEGY_MAIL);
880
881
445
    if(!result)
882
445
      if(!Curl_checkheaders(data, STRCONST("Mime-Version")))
883
445
        result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
884
445
                                      "Mime-Version: 1.0");
885
886
445
    if(!result)
887
445
      result = Curl_creader_set_mime(data, &data->set.mimepost);
888
445
    if(result)
889
0
      return result;
890
445
    data->state.infilesize = Curl_creader_client_length(data);
891
445
  }
892
87
  else
893
87
#endif
894
87
  {
895
87
    result = Curl_creader_set_fread(data, data->state.infilesize);
896
87
    if(result)
897
0
      return result;
898
87
  }
899
900
  /* Check we know the size of the upload */
901
532
  if(data->state.infilesize < 0) {
902
0
    failf(data, "Cannot APPEND with unknown input file size");
903
0
    return CURLE_UPLOAD_FAILED;
904
0
  }
905
906
  /* Make sure the mailbox is in the correct atom format */
907
532
  mailbox = imap_atom(imap->mailbox, FALSE);
908
532
  if(!mailbox)
909
1
    return CURLE_OUT_OF_MEMORY;
910
911
  /* Generate flags string and send the APPEND command */
912
531
  curlx_dyn_init(&flags, 100);
913
531
  if(data->set.upload_flags) {
914
0
    int i;
915
0
    struct ulbits ulflag[] = {
916
0
      {CURLULFLAG_ANSWERED, "Answered"},
917
0
      {CURLULFLAG_DELETED, "Deleted"},
918
0
      {CURLULFLAG_DRAFT, "Draft"},
919
0
      {CURLULFLAG_FLAGGED, "Flagged"},
920
0
      {CURLULFLAG_SEEN, "Seen"},
921
0
      {0, NULL}
922
0
    };
923
924
0
    result = CURLE_OUT_OF_MEMORY;
925
0
    if(curlx_dyn_add(&flags, " (")) {
926
0
      goto cleanup;
927
0
    }
928
929
0
    for(i = 0; ulflag[i].bit; i++) {
930
0
      if(data->set.upload_flags & ulflag[i].bit) {
931
0
        if((curlx_dyn_len(&flags) > 2 && curlx_dyn_add(&flags, " ")) ||
932
0
           curlx_dyn_add(&flags, "\\") ||
933
0
           curlx_dyn_add(&flags, ulflag[i].flag))
934
0
          goto cleanup;
935
0
      }
936
0
    }
937
938
0
    if(curlx_dyn_add(&flags, ")"))
939
0
      goto cleanup;
940
0
  }
941
531
  else if(curlx_dyn_add(&flags, ""))
942
0
    goto cleanup;
943
944
531
  result = imap_sendf(data, imapc, "APPEND %s%s {%" FMT_OFF_T "}",
945
531
                      mailbox, curlx_dyn_ptr(&flags), data->state.infilesize);
946
947
531
cleanup:
948
531
  curlx_dyn_free(&flags);
949
531
  free(mailbox);
950
951
531
  if(!result)
952
531
    imap_state(data, imapc, IMAP_APPEND);
953
954
531
  return result;
955
531
}
956
957
/***********************************************************************
958
 *
959
 * imap_perform_search()
960
 *
961
 * Sends a SEARCH command.
962
 */
963
static CURLcode imap_perform_search(struct Curl_easy *data,
964
                                    struct imap_conn *imapc,
965
                                    struct IMAP *imap)
966
71
{
967
71
  CURLcode result = CURLE_OK;
968
969
  /* Check we have a query string */
970
71
  if(!imap->query) {
971
0
    failf(data, "Cannot SEARCH without a query string.");
972
0
    return CURLE_URL_MALFORMAT;
973
0
  }
974
975
  /* Send the SEARCH command */
976
71
  result = imap_sendf(data, imapc, "SEARCH %s", imap->query);
977
978
71
  if(!result)
979
71
    imap_state(data, imapc, IMAP_SEARCH);
980
981
71
  return result;
982
71
}
983
984
/***********************************************************************
985
 *
986
 * imap_perform_logout()
987
 *
988
 * Performs the logout action prior to sclose() being called.
989
 */
990
static CURLcode imap_perform_logout(struct Curl_easy *data,
991
                                    struct imap_conn *imapc)
992
1.05k
{
993
  /* Send the LOGOUT command */
994
1.05k
  CURLcode result = imap_sendf(data, imapc, "LOGOUT");
995
996
1.05k
  if(!result)
997
1.05k
    imap_state(data, imapc, IMAP_LOGOUT);
998
999
1.05k
  return result;
1000
1.05k
}
1001
1002
/* For the initial server greeting */
1003
static CURLcode imap_state_servergreet_resp(struct Curl_easy *data,
1004
                                            struct imap_conn *imapc,
1005
                                            int imapcode,
1006
                                            imapstate instate)
1007
1.77k
{
1008
1.77k
  (void)instate; /* no use for this yet */
1009
1010
1.77k
  if(imapcode == IMAP_RESP_PREAUTH) {
1011
    /* PREAUTH */
1012
90
    imapc->preauth = TRUE;
1013
90
    infof(data, "PREAUTH connection, already authenticated");
1014
90
  }
1015
1.68k
  else if(imapcode != IMAP_RESP_OK) {
1016
56
    failf(data, "Got unexpected imap-server response");
1017
56
    return CURLE_WEIRD_SERVER_REPLY;
1018
56
  }
1019
1020
1.71k
  return imap_perform_capability(data, imapc);
1021
1.77k
}
1022
1023
/* For CAPABILITY responses */
1024
static CURLcode imap_state_capability_resp(struct Curl_easy *data,
1025
                                           struct imap_conn *imapc,
1026
                                           int imapcode,
1027
                                           imapstate instate)
1028
1.63k
{
1029
1.63k
  CURLcode result = CURLE_OK;
1030
1.63k
  struct connectdata *conn = data->conn;
1031
1.63k
  const char *line = curlx_dyn_ptr(&imapc->pp.recvbuf);
1032
1033
1.63k
  (void)instate; /* no use for this yet */
1034
1035
  /* Do we have an untagged response? */
1036
1.63k
  if(imapcode == '*') {
1037
0
    line += 2;
1038
1039
    /* Loop through the data line */
1040
0
    for(;;) {
1041
0
      size_t wordlen;
1042
0
      while(*line &&
1043
0
            (*line == ' ' || *line == '\t' ||
1044
0
              *line == '\r' || *line == '\n')) {
1045
1046
0
        line++;
1047
0
      }
1048
1049
0
      if(!*line)
1050
0
        break;
1051
1052
      /* Extract the word */
1053
0
      for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' &&
1054
0
            line[wordlen] != '\t' && line[wordlen] != '\r' &&
1055
0
            line[wordlen] != '\n';)
1056
0
        wordlen++;
1057
1058
      /* Does the server support the STARTTLS capability? */
1059
0
      if(wordlen == 8 && !memcmp(line, "STARTTLS", 8))
1060
0
        imapc->tls_supported = TRUE;
1061
1062
      /* Has the server explicitly disabled clear text authentication? */
1063
0
      else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13))
1064
0
        imapc->login_disabled = TRUE;
1065
1066
      /* Does the server support the SASL-IR capability? */
1067
0
      else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7))
1068
0
        imapc->ir_supported = TRUE;
1069
1070
      /* Do we have a SASL based authentication mechanism? */
1071
0
      else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
1072
0
        size_t llen;
1073
0
        unsigned short mechbit;
1074
1075
0
        line += 5;
1076
0
        wordlen -= 5;
1077
1078
        /* Test the word for a matching authentication mechanism */
1079
0
        mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
1080
0
        if(mechbit && llen == wordlen)
1081
0
          imapc->sasl.authmechs |= mechbit;
1082
0
      }
1083
1084
0
      line += wordlen;
1085
0
    }
1086
0
  }
1087
1.63k
  else if(data->set.use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
1088
    /* PREAUTH is not compatible with STARTTLS. */
1089
3
    if(imapcode == IMAP_RESP_OK && imapc->tls_supported && !imapc->preauth) {
1090
      /* Switch to TLS connection now */
1091
0
      result = imap_perform_starttls(data, imapc);
1092
0
    }
1093
3
    else if(data->set.use_ssl <= CURLUSESSL_TRY)
1094
1
      result = imap_perform_authentication(data, imapc);
1095
2
    else {
1096
2
      failf(data, "STARTTLS not available.");
1097
2
      result = CURLE_USE_SSL_FAILED;
1098
2
    }
1099
3
  }
1100
1.63k
  else
1101
1.63k
    result = imap_perform_authentication(data, imapc);
1102
1103
1.63k
  return result;
1104
1.63k
}
1105
1106
/* For STARTTLS responses */
1107
static CURLcode imap_state_starttls_resp(struct Curl_easy *data,
1108
                                         struct imap_conn *imapc,
1109
                                         int imapcode,
1110
                                         imapstate instate)
1111
0
{
1112
0
  CURLcode result = CURLE_OK;
1113
1114
0
  (void)instate; /* no use for this yet */
1115
1116
  /* Pipelining in response is forbidden. */
1117
0
  if(imapc->pp.overflow)
1118
0
    return CURLE_WEIRD_SERVER_REPLY;
1119
1120
0
  if(imapcode != IMAP_RESP_OK) {
1121
0
    if(data->set.use_ssl != CURLUSESSL_TRY) {
1122
0
      failf(data, "STARTTLS denied");
1123
0
      result = CURLE_USE_SSL_FAILED;
1124
0
    }
1125
0
    else
1126
0
      result = imap_perform_authentication(data, imapc);
1127
0
  }
1128
0
  else
1129
0
    imap_state(data, imapc, IMAP_UPGRADETLS);
1130
1131
0
  return result;
1132
0
}
1133
1134
/* For SASL authentication responses */
1135
static CURLcode imap_state_auth_resp(struct Curl_easy *data,
1136
                                     struct imap_conn *imapc,
1137
                                     int imapcode,
1138
                                     imapstate instate)
1139
0
{
1140
0
  CURLcode result = CURLE_OK;
1141
0
  saslprogress progress;
1142
1143
0
  (void)instate; /* no use for this yet */
1144
1145
0
  result = Curl_sasl_continue(&imapc->sasl, data, imapcode, &progress);
1146
0
  if(!result)
1147
0
    switch(progress) {
1148
0
    case SASL_DONE:
1149
0
      imap_state(data, imapc, IMAP_STOP);  /* Authenticated */
1150
0
      break;
1151
0
    case SASL_IDLE:            /* No mechanism left after cancellation */
1152
0
      if((!imapc->login_disabled) && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
1153
        /* Perform clear text authentication */
1154
0
        result = imap_perform_login(data, imapc, data->conn);
1155
0
      else {
1156
0
        failf(data, "Authentication cancelled");
1157
0
        result = CURLE_LOGIN_DENIED;
1158
0
      }
1159
0
      break;
1160
0
    default:
1161
0
      break;
1162
0
    }
1163
1164
0
  return result;
1165
0
}
1166
1167
/* For LOGIN responses */
1168
static CURLcode imap_state_login_resp(struct Curl_easy *data,
1169
                                      struct imap_conn *imapc,
1170
                                      int imapcode,
1171
                                      imapstate instate)
1172
17
{
1173
17
  CURLcode result = CURLE_OK;
1174
17
  (void)instate; /* no use for this yet */
1175
1176
17
  if(imapcode != IMAP_RESP_OK) {
1177
1
    failf(data, "Access denied. %c", imapcode);
1178
1
    result = CURLE_LOGIN_DENIED;
1179
1
  }
1180
16
  else
1181
    /* End of connect phase */
1182
16
    imap_state(data, imapc, IMAP_STOP);
1183
1184
17
  return result;
1185
17
}
1186
1187
/* For LIST and SEARCH responses */
1188
static CURLcode imap_state_listsearch_resp(struct Curl_easy *data,
1189
                                           struct imap_conn *imapc,
1190
                                           int imapcode,
1191
                                           imapstate instate)
1192
9.51k
{
1193
9.51k
  CURLcode result = CURLE_OK;
1194
9.51k
  char *line = curlx_dyn_ptr(&imapc->pp.recvbuf);
1195
9.51k
  size_t len = imapc->pp.nfinal;
1196
1197
9.51k
  (void)instate; /* No use for this yet */
1198
1199
9.51k
  if(imapcode == '*')
1200
9.48k
    result = Curl_client_write(data, CLIENTWRITE_BODY, line, len);
1201
25
  else if(imapcode != IMAP_RESP_OK)
1202
7
    result = CURLE_QUOTE_ERROR;
1203
18
  else
1204
    /* End of DO phase */
1205
18
    imap_state(data, imapc, IMAP_STOP);
1206
1207
9.51k
  return result;
1208
9.51k
}
1209
1210
/* For SELECT responses */
1211
static CURLcode imap_state_select_resp(struct Curl_easy *data,
1212
                                       struct imap_conn *imapc,
1213
                                       struct IMAP *imap,
1214
                                       int imapcode,
1215
                                       imapstate instate)
1216
3.83k
{
1217
3.83k
  CURLcode result = CURLE_OK;
1218
3.83k
  const char *line = curlx_dyn_ptr(&imapc->pp.recvbuf);
1219
1220
3.83k
  (void)instate; /* no use for this yet */
1221
1222
3.83k
  if(imapcode == '*') {
1223
    /* See if this is an UIDVALIDITY response */
1224
3.17k
    if(checkprefix("OK [UIDVALIDITY ", line + 2)) {
1225
0
      size_t len = 0;
1226
0
      const char *p = &line[2] + strlen("OK [UIDVALIDITY ");
1227
0
      while((len < 20) && p[len] && ISDIGIT(p[len]))
1228
0
        len++;
1229
0
      if(len && (p[len] == ']')) {
1230
0
        struct dynbuf uid;
1231
0
        curlx_dyn_init(&uid, 20);
1232
0
        if(curlx_dyn_addn(&uid, p, len))
1233
0
          return CURLE_OUT_OF_MEMORY;
1234
0
        free(imapc->mailbox_uidvalidity);
1235
0
        imapc->mailbox_uidvalidity = curlx_dyn_ptr(&uid);
1236
0
      }
1237
0
    }
1238
3.17k
  }
1239
668
  else if(imapcode == IMAP_RESP_OK) {
1240
    /* Check if the UIDVALIDITY has been specified and matches */
1241
665
    if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
1242
665
       !curl_strequal(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
1243
0
      failf(data, "Mailbox UIDVALIDITY has changed");
1244
0
      result = CURLE_REMOTE_FILE_NOT_FOUND;
1245
0
    }
1246
665
    else {
1247
      /* Note the currently opened mailbox on this connection */
1248
665
      DEBUGASSERT(!imapc->mailbox);
1249
665
      imapc->mailbox = strdup(imap->mailbox);
1250
665
      if(!imapc->mailbox)
1251
0
        return CURLE_OUT_OF_MEMORY;
1252
1253
665
      if(imap->custom)
1254
34
        result = imap_perform_list(data, imapc, imap);
1255
631
      else if(imap->query)
1256
71
        result = imap_perform_search(data, imapc, imap);
1257
560
      else
1258
560
        result = imap_perform_fetch(data, imapc, imap);
1259
665
    }
1260
665
  }
1261
3
  else {
1262
3
    failf(data, "Select failed");
1263
3
    result = CURLE_LOGIN_DENIED;
1264
3
  }
1265
1266
3.83k
  return result;
1267
3.83k
}
1268
1269
/* For the (first line of the) FETCH responses */
1270
static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
1271
                                      struct imap_conn *imapc,
1272
                                      int imapcode,
1273
                                      imapstate instate)
1274
498
{
1275
498
  CURLcode result = CURLE_OK;
1276
498
  struct pingpong *pp = &imapc->pp;
1277
498
  const char *ptr = curlx_dyn_ptr(&imapc->pp.recvbuf);
1278
498
  size_t len = imapc->pp.nfinal;
1279
498
  bool parsed = FALSE;
1280
498
  curl_off_t size = 0;
1281
1282
498
  (void)instate; /* no use for this yet */
1283
1284
498
  if(imapcode != '*') {
1285
1
    Curl_pgrsSetDownloadSize(data, -1);
1286
1
    imap_state(data, imapc, IMAP_STOP);
1287
1
    return CURLE_REMOTE_FILE_NOT_FOUND;
1288
1
  }
1289
1290
  /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
1291
     the continuation data contained within the curly brackets */
1292
497
  ptr = memchr(ptr, '{', len);
1293
497
  if(ptr) {
1294
495
    ptr++;
1295
495
    if(!curlx_str_number(&ptr, &size, CURL_OFF_T_MAX) &&
1296
495
       !curlx_str_single(&ptr, '}'))
1297
492
      parsed = TRUE;
1298
495
  }
1299
1300
497
  if(parsed) {
1301
492
    infof(data, "Found %" FMT_OFF_T " bytes to download", size);
1302
492
    Curl_pgrsSetDownloadSize(data, size);
1303
1304
492
    if(pp->overflow) {
1305
      /* At this point there is a data in the receive buffer that is body
1306
         content, send it as body and then skip it. Do note that there may
1307
         even be additional "headers" after the body. */
1308
455
      size_t chunk = pp->overflow;
1309
1310
      /* keep only the overflow */
1311
455
      curlx_dyn_tail(&pp->recvbuf, chunk);
1312
455
      pp->nfinal = 0; /* done */
1313
1314
455
      if(chunk > (size_t)size)
1315
        /* The conversion from curl_off_t to size_t is always fine here */
1316
16
        chunk = (size_t)size;
1317
1318
455
      if(!chunk) {
1319
        /* no size, we are done with the data */
1320
3
        imap_state(data, imapc, IMAP_STOP);
1321
3
        return CURLE_OK;
1322
3
      }
1323
452
      result = Curl_client_write(data, CLIENTWRITE_BODY,
1324
452
                                 curlx_dyn_ptr(&pp->recvbuf), chunk);
1325
452
      if(result)
1326
3
        return result;
1327
1328
449
      infof(data, "Written %zu bytes, %" FMT_OFF_TU
1329
449
            " bytes are left for transfer", chunk, size - chunk);
1330
1331
      /* Have we used the entire overflow or just part of it?*/
1332
449
      if(pp->overflow > chunk) {
1333
        /* remember the remaining trailing overflow data */
1334
11
        pp->overflow -= chunk;
1335
11
        curlx_dyn_tail(&pp->recvbuf, pp->overflow);
1336
11
      }
1337
438
      else {
1338
438
        pp->overflow = 0; /* handled */
1339
        /* Free the cache */
1340
438
        curlx_dyn_reset(&pp->recvbuf);
1341
438
      }
1342
449
    }
1343
1344
486
    if(data->req.bytecount == size)
1345
      /* The entire data is already transferred! */
1346
12
      Curl_xfer_setup_nop(data);
1347
474
    else {
1348
      /* IMAP download */
1349
474
      data->req.maxdownload = size;
1350
474
      Curl_xfer_setup1(data, CURL_XFER_RECV, size, FALSE);
1351
474
    }
1352
486
  }
1353
5
  else {
1354
    /* We do not know how to parse this line */
1355
5
    failf(data, "Failed to parse FETCH response.");
1356
5
    result = CURLE_WEIRD_SERVER_REPLY;
1357
5
  }
1358
1359
  /* End of DO phase */
1360
491
  imap_state(data, imapc, IMAP_STOP);
1361
1362
491
  return result;
1363
497
}
1364
1365
/* For final FETCH responses performed after the download */
1366
static CURLcode imap_state_fetch_final_resp(struct Curl_easy *data,
1367
                                            struct imap_conn *imapc,
1368
                                            int imapcode,
1369
                                            imapstate instate)
1370
2
{
1371
2
  CURLcode result = CURLE_OK;
1372
1373
2
  (void)instate; /* No use for this yet */
1374
1375
2
  if(imapcode != IMAP_RESP_OK)
1376
1
    result = CURLE_WEIRD_SERVER_REPLY;
1377
1
  else
1378
    /* End of DONE phase */
1379
1
    imap_state(data, imapc, IMAP_STOP);
1380
1381
2
  return result;
1382
2
}
1383
1384
/* For APPEND responses */
1385
static CURLcode imap_state_append_resp(struct Curl_easy *data,
1386
                                       struct imap_conn *imapc,
1387
                                       int imapcode,
1388
                                       imapstate instate)
1389
442
{
1390
442
  CURLcode result = CURLE_OK;
1391
442
  (void)instate; /* No use for this yet */
1392
1393
442
  if(imapcode != '+') {
1394
1
    result = CURLE_UPLOAD_FAILED;
1395
1
  }
1396
441
  else {
1397
    /* Set the progress upload size */
1398
441
    Curl_pgrsSetUploadSize(data, data->state.infilesize);
1399
1400
    /* IMAP upload */
1401
441
    Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
1402
1403
    /* End of DO phase */
1404
441
    imap_state(data, imapc, IMAP_STOP);
1405
441
  }
1406
1407
442
  return result;
1408
442
}
1409
1410
/* For final APPEND responses performed after the upload */
1411
static CURLcode imap_state_append_final_resp(struct Curl_easy *data,
1412
                                             struct imap_conn *imapc,
1413
                                             int imapcode,
1414
                                             imapstate instate)
1415
8
{
1416
8
  CURLcode result = CURLE_OK;
1417
1418
8
  (void)instate; /* No use for this yet */
1419
1420
8
  if(imapcode != IMAP_RESP_OK)
1421
3
    result = CURLE_UPLOAD_FAILED;
1422
5
  else
1423
    /* End of DONE phase */
1424
5
    imap_state(data, imapc, IMAP_STOP);
1425
1426
8
  return result;
1427
8
}
1428
1429
static CURLcode imap_pp_statemachine(struct Curl_easy *data,
1430
                                     struct connectdata *conn)
1431
5.59M
{
1432
5.59M
  CURLcode result = CURLE_OK;
1433
5.59M
  int imapcode;
1434
5.59M
  struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN);
1435
5.59M
  struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY);
1436
5.59M
  struct pingpong *pp;
1437
5.59M
  size_t nread = 0;
1438
1439
5.59M
  (void)data;
1440
5.59M
  if(!imapc || !imap)
1441
1.02k
    return CURLE_FAILED_INIT;
1442
5.59M
  pp = &imapc->pp;
1443
  /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
1444
5.59M
upgrade_tls:
1445
5.59M
  if(imapc->state == IMAP_UPGRADETLS) {
1446
0
    result = imap_perform_upgrade_tls(data, imapc, conn);
1447
0
    if(result || (imapc->state == IMAP_UPGRADETLS))
1448
0
      return result;
1449
0
  }
1450
1451
  /* Flush any data that needs to be sent */
1452
5.59M
  if(pp->sendleft)
1453
5.58M
    return Curl_pp_flushsend(data, pp);
1454
1455
21.3k
  do {
1456
    /* Read the response from the server */
1457
21.3k
    result = Curl_pp_readresp(data, FIRSTSOCKET, pp, &imapcode, &nread);
1458
21.3k
    if(result)
1459
1.59k
      return result;
1460
1461
    /* Was there an error parsing the response line? */
1462
19.7k
    if(imapcode == -1)
1463
42
      return CURLE_WEIRD_SERVER_REPLY;
1464
1465
19.6k
    if(!imapcode)
1466
1.94k
      break;
1467
1468
    /* We have now received a full IMAP server response */
1469
17.7k
    switch(imapc->state) {
1470
1.77k
    case IMAP_SERVERGREET:
1471
1.77k
      result = imap_state_servergreet_resp(data, imapc,
1472
1.77k
                                           imapcode, imapc->state);
1473
1.77k
      break;
1474
1475
1.63k
    case IMAP_CAPABILITY:
1476
1.63k
      result = imap_state_capability_resp(data, imapc, imapcode, imapc->state);
1477
1.63k
      break;
1478
1479
0
    case IMAP_STARTTLS:
1480
0
      result = imap_state_starttls_resp(data, imapc, imapcode, imapc->state);
1481
      /* During UPGRADETLS, leave the read loop as we need to connect
1482
       * (e.g. TLS handshake) before we continue sending/receiving. */
1483
0
      if(!result && (imapc->state == IMAP_UPGRADETLS))
1484
0
        goto upgrade_tls;
1485
0
      break;
1486
1487
0
    case IMAP_AUTHENTICATE:
1488
0
      result = imap_state_auth_resp(data, imapc, imapcode, imapc->state);
1489
0
      break;
1490
1491
17
    case IMAP_LOGIN:
1492
17
      result = imap_state_login_resp(data, imapc, imapcode, imapc->state);
1493
17
      break;
1494
1495
6.65k
    case IMAP_LIST:
1496
9.51k
    case IMAP_SEARCH:
1497
9.51k
      result = imap_state_listsearch_resp(data, imapc, imapcode, imapc->state);
1498
9.51k
      break;
1499
1500
3.83k
    case IMAP_SELECT:
1501
3.83k
      result = imap_state_select_resp(data, imapc, imap,
1502
3.83k
                                      imapcode, imapc->state);
1503
3.83k
      break;
1504
1505
498
    case IMAP_FETCH:
1506
498
      result = imap_state_fetch_resp(data, imapc, imapcode, imapc->state);
1507
498
      break;
1508
1509
2
    case IMAP_FETCH_FINAL:
1510
2
      result = imap_state_fetch_final_resp(data, imapc,
1511
2
                                           imapcode, imapc->state);
1512
2
      break;
1513
1514
442
    case IMAP_APPEND:
1515
442
      result = imap_state_append_resp(data, imapc, imapcode, imapc->state);
1516
442
      break;
1517
1518
8
    case IMAP_APPEND_FINAL:
1519
8
      result = imap_state_append_final_resp(data, imapc,
1520
8
                                            imapcode, imapc->state);
1521
8
      break;
1522
1523
0
    case IMAP_LOGOUT:
1524
0
    default:
1525
      /* internal error */
1526
0
      imap_state(data, imapc, IMAP_STOP);
1527
0
      break;
1528
17.7k
    }
1529
17.7k
  } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
1530
1531
4.73k
  return result;
1532
6.37k
}
1533
1534
/* Called repeatedly until done from multi.c */
1535
static CURLcode imap_multi_statemach(struct Curl_easy *data, bool *done)
1536
5.92k
{
1537
5.92k
  CURLcode result = CURLE_OK;
1538
5.92k
  struct imap_conn *imapc =
1539
5.92k
    Curl_conn_meta_get(data->conn, CURL_META_IMAP_CONN);
1540
1541
5.92k
  *done = FALSE;
1542
5.92k
  if(!imapc)
1543
0
    return CURLE_FAILED_INIT;
1544
5.92k
  result = Curl_pp_statemach(data, &imapc->pp, FALSE, FALSE);
1545
5.92k
  *done = (imapc->state == IMAP_STOP);
1546
1547
5.92k
  return result;
1548
5.92k
}
1549
1550
static CURLcode imap_block_statemach(struct Curl_easy *data,
1551
                                     struct imap_conn *imapc,
1552
                                     bool disconnecting)
1553
1.56k
{
1554
1.56k
  CURLcode result = CURLE_OK;
1555
1556
5.58M
  while(imapc->state != IMAP_STOP && !result)
1557
5.58M
    result = Curl_pp_statemach(data, &imapc->pp, TRUE, disconnecting);
1558
1559
1.56k
  return result;
1560
1.56k
}
1561
1562
/* For the IMAP "protocol connect" and "doing" phases only */
1563
static int imap_getsock(struct Curl_easy *data,
1564
                        struct connectdata *conn,
1565
                        curl_socket_t *socks)
1566
1.06k
{
1567
1.06k
  struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN);
1568
1.06k
  return imapc ?
1569
1.06k
         Curl_pp_getsock(data, &imapc->pp, socks) : GETSOCK_BLANK;
1570
1.06k
}
1571
1572
/***********************************************************************
1573
 *
1574
 * imap_connect()
1575
 *
1576
 * This function should do everything that is to be considered a part of the
1577
 * connection phase.
1578
 *
1579
 * The variable 'done' points to will be TRUE if the protocol-layer connect
1580
 * phase is done when this function returns, or FALSE if not.
1581
 */
1582
static CURLcode imap_connect(struct Curl_easy *data, bool *done)
1583
2.42k
{
1584
2.42k
  struct imap_conn *imapc =
1585
2.42k
    Curl_conn_meta_get(data->conn, CURL_META_IMAP_CONN);
1586
2.42k
  CURLcode result = CURLE_OK;
1587
1588
2.42k
  *done = FALSE; /* default to not done yet */
1589
2.42k
  if(!imapc)
1590
0
    return CURLE_FAILED_INIT;
1591
1592
  /* We always support persistent connections in IMAP */
1593
2.42k
  connkeep(data->conn, "IMAP default");
1594
1595
  /* Parse the URL options */
1596
2.42k
  result = imap_parse_url_options(data->conn, imapc);
1597
2.42k
  if(result)
1598
83
    return result;
1599
1600
  /* Start off waiting for the server greeting response */
1601
2.34k
  imap_state(data, imapc, IMAP_SERVERGREET);
1602
1603
  /* Start off with an response id of '*' */
1604
2.34k
  strcpy(imapc->resptag, "*");
1605
1606
2.34k
  result = imap_multi_statemach(data, done);
1607
1608
2.34k
  return result;
1609
2.42k
}
1610
1611
/***********************************************************************
1612
 *
1613
 * imap_done()
1614
 *
1615
 * The DONE function. This does what needs to be done after a single DO has
1616
 * performed.
1617
 *
1618
 * Input argument is already checked for validity.
1619
 */
1620
static CURLcode imap_done(struct Curl_easy *data, CURLcode status,
1621
                          bool premature)
1622
2.42k
{
1623
2.42k
  CURLcode result = CURLE_OK;
1624
2.42k
  struct connectdata *conn = data->conn;
1625
2.42k
  struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN);
1626
2.42k
  struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY);
1627
1628
2.42k
  (void)premature;
1629
1630
2.42k
  if(!imapc)
1631
0
    return CURLE_FAILED_INIT;
1632
2.42k
  if(!imap)
1633
0
    return CURLE_OK;
1634
1635
2.42k
  if(status) {
1636
1.86k
    connclose(conn, "IMAP done with bad status"); /* marked for closure */
1637
1.86k
    result = status;         /* use the already set error code */
1638
1.86k
  }
1639
558
  else if(!data->set.connect_only && !imap->custom &&
1640
558
          (imap->uid || imap->mindex || data->state.upload ||
1641
549
          IS_MIME_POST(data))) {
1642
    /* Handle responses after FETCH or APPEND transfer has finished */
1643
1644
509
    if(!data->state.upload && !IS_MIME_POST(data))
1645
66
      imap_state(data, imapc, IMAP_FETCH_FINAL);
1646
443
    else {
1647
      /* End the APPEND command first by sending an empty line */
1648
443
      result = Curl_pp_sendf(data, &imapc->pp, "%s", "");
1649
443
      if(!result)
1650
443
        imap_state(data, imapc, IMAP_APPEND_FINAL);
1651
443
    }
1652
1653
    /* Run the state-machine */
1654
509
    if(!result)
1655
509
      result = imap_block_statemach(data, imapc, FALSE);
1656
509
  }
1657
1658
2.42k
  imap_easy_reset(imap);
1659
2.42k
  return result;
1660
2.42k
}
1661
1662
/***********************************************************************
1663
 *
1664
 * imap_perform()
1665
 *
1666
 * This is the actual DO function for IMAP. Fetch or append a message, or do
1667
 * other things according to the options previously setup.
1668
 */
1669
static CURLcode imap_perform(struct Curl_easy *data, bool *connected,
1670
                             bool *dophase_done)
1671
1.55k
{
1672
  /* This is IMAP and no proxy */
1673
1.55k
  CURLcode result = CURLE_OK;
1674
1.55k
  struct connectdata *conn = data->conn;
1675
1.55k
  struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN);
1676
1.55k
  struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY);
1677
1.55k
  bool selected = FALSE;
1678
1679
1.55k
  DEBUGF(infof(data, "DO phase starts"));
1680
1.55k
  if(!imapc || !imap)
1681
0
    return CURLE_FAILED_INIT;
1682
1683
1.55k
  if(data->req.no_body) {
1684
    /* Requested no body means no transfer */
1685
6
    imap->transfer = PPTRANSFER_INFO;
1686
6
  }
1687
1688
1.55k
  *dophase_done = FALSE; /* not done yet */
1689
1690
  /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
1691
     has already been selected on this connection */
1692
1.55k
  if(imap->mailbox && imapc->mailbox &&
1693
1.55k
     curl_strequal(imap->mailbox, imapc->mailbox) &&
1694
1.55k
     (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
1695
0
      curl_strequal(imap->uidvalidity, imapc->mailbox_uidvalidity)))
1696
0
    selected = TRUE;
1697
1698
  /* Start the first command in the DO phase */
1699
1.55k
  if(data->state.upload || IS_MIME_POST(data))
1700
    /* APPEND can be executed directly */
1701
534
    result = imap_perform_append(data, imapc, imap);
1702
1.02k
  else if(imap->custom && (selected || !imap->mailbox))
1703
    /* Custom command using the same mailbox or no mailbox */
1704
115
    result = imap_perform_list(data, imapc, imap);
1705
910
  else if(!imap->custom && selected && (imap->uid || imap->mindex))
1706
    /* FETCH from the same mailbox */
1707
0
    result = imap_perform_fetch(data, imapc, imap);
1708
910
  else if(!imap->custom && selected && imap->query)
1709
    /* SEARCH the current mailbox */
1710
0
    result = imap_perform_search(data, imapc, imap);
1711
910
  else if(imap->mailbox && !selected &&
1712
910
         (imap->custom || imap->uid || imap->mindex || imap->query))
1713
    /* SELECT the mailbox */
1714
725
    result = imap_perform_select(data, imapc, imap);
1715
185
  else
1716
    /* LIST */
1717
185
    result = imap_perform_list(data, imapc, imap);
1718
1719
1.55k
  if(result)
1720
9
    return result;
1721
1722
  /* Run the state-machine */
1723
1.55k
  result = imap_multi_statemach(data, dophase_done);
1724
1725
1.55k
  *connected = Curl_conn_is_connected(conn, FIRSTSOCKET);
1726
1727
1.55k
  if(*dophase_done)
1728
842
    DEBUGF(infof(data, "DO phase is complete"));
1729
1730
1.55k
  return result;
1731
1.55k
}
1732
1733
/***********************************************************************
1734
 *
1735
 * imap_do()
1736
 *
1737
 * This function is registered as 'curl_do' function. It decodes the path
1738
 * parts etc as a wrapper to the actual DO function (imap_perform).
1739
 *
1740
 * The input argument is already checked for validity.
1741
 */
1742
static CURLcode imap_do(struct Curl_easy *data, bool *done)
1743
1.59k
{
1744
1.59k
  struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY);
1745
1.59k
  CURLcode result = CURLE_OK;
1746
1.59k
  *done = FALSE; /* default to false */
1747
1748
1.59k
  if(!imap)
1749
0
    return CURLE_FAILED_INIT;
1750
  /* Parse the URL path */
1751
1.59k
  result = imap_parse_url_path(data, imap);
1752
1.59k
  if(result)
1753
36
    return result;
1754
1755
  /* Parse the custom request */
1756
1.56k
  result = imap_parse_custom_request(data, imap);
1757
1.56k
  if(result)
1758
2
    return result;
1759
1760
1.55k
  result = imap_regular_transfer(data, imap, done);
1761
1762
1.55k
  return result;
1763
1.56k
}
1764
1765
/***********************************************************************
1766
 *
1767
 * imap_disconnect()
1768
 *
1769
 * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
1770
 * resources. BLOCKING.
1771
 */
1772
static CURLcode imap_disconnect(struct Curl_easy *data,
1773
                                struct connectdata *conn, bool dead_connection)
1774
6.42k
{
1775
6.42k
  struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN);
1776
1777
6.42k
  (void)data;
1778
6.42k
  if(imapc) {
1779
    /* We cannot send quit unconditionally. If this connection is stale or
1780
       bad in any way (pingpong has pending data to send),
1781
       sending quit and waiting around here will make the
1782
       disconnect wait in vain and cause more problems than we need to. */
1783
6.40k
    if(!dead_connection && conn->bits.protoconnstart &&
1784
6.40k
       !Curl_pp_needs_flush(data, &imapc->pp)) {
1785
1.05k
      if(!imap_perform_logout(data, imapc))
1786
1.05k
        (void)imap_block_statemach(data, imapc, TRUE); /* ignore errors */
1787
1.05k
    }
1788
6.40k
  }
1789
6.42k
  return CURLE_OK;
1790
6.42k
}
1791
1792
/* Call this when the DO phase has completed */
1793
static CURLcode imap_dophase_done(struct Curl_easy *data,
1794
                                  struct IMAP *imap,
1795
                                  bool connected)
1796
948
{
1797
948
  (void)connected;
1798
1799
948
  if(imap->transfer != PPTRANSFER_BODY)
1800
    /* no data to transfer */
1801
4
    Curl_xfer_setup_nop(data);
1802
1803
948
  return CURLE_OK;
1804
948
}
1805
1806
/* Called from multi.c while DOing */
1807
static CURLcode imap_doing(struct Curl_easy *data, bool *dophase_done)
1808
1.36k
{
1809
1.36k
  struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY);
1810
1.36k
  CURLcode result;
1811
1812
1.36k
  if(!imap)
1813
0
    return CURLE_FAILED_INIT;
1814
1815
1.36k
  result = imap_multi_statemach(data, dophase_done);
1816
1.36k
  if(result)
1817
469
    DEBUGF(infof(data, "DO phase failed"));
1818
894
  else if(*dophase_done) {
1819
112
    result = imap_dophase_done(data, imap, FALSE /* not connected */);
1820
1821
112
    DEBUGF(infof(data, "DO phase is complete"));
1822
112
  }
1823
1824
1.36k
  return result;
1825
1.36k
}
1826
1827
/***********************************************************************
1828
 *
1829
 * imap_regular_transfer()
1830
 *
1831
 * The input argument is already checked for validity.
1832
 *
1833
 * Performs all commands done before a regular transfer between a local and a
1834
 * remote host.
1835
 */
1836
static CURLcode imap_regular_transfer(struct Curl_easy *data,
1837
                                      struct IMAP *imap,
1838
                                      bool *dophase_done)
1839
1.55k
{
1840
1.55k
  CURLcode result = CURLE_OK;
1841
1.55k
  bool connected = FALSE;
1842
1843
  /* Make sure size is unknown at this point */
1844
1.55k
  data->req.size = -1;
1845
1846
  /* Set the progress data */
1847
1.55k
  Curl_pgrsSetUploadCounter(data, 0);
1848
1.55k
  Curl_pgrsSetDownloadCounter(data, 0);
1849
1.55k
  Curl_pgrsSetUploadSize(data, -1);
1850
1.55k
  Curl_pgrsSetDownloadSize(data, -1);
1851
1852
  /* Carry out the perform */
1853
1.55k
  result = imap_perform(data, &connected, dophase_done);
1854
1855
  /* Perform post DO phase operations if necessary */
1856
1.55k
  if(!result && *dophase_done)
1857
836
    result = imap_dophase_done(data, imap, connected);
1858
1859
1.55k
  return result;
1860
1.55k
}
1861
1862
static void imap_easy_reset(struct IMAP *imap)
1863
8.82k
{
1864
8.82k
  Curl_safefree(imap->mailbox);
1865
8.82k
  Curl_safefree(imap->uidvalidity);
1866
8.82k
  Curl_safefree(imap->uid);
1867
8.82k
  Curl_safefree(imap->mindex);
1868
8.82k
  Curl_safefree(imap->section);
1869
8.82k
  Curl_safefree(imap->partial);
1870
8.82k
  Curl_safefree(imap->query);
1871
8.82k
  Curl_safefree(imap->custom);
1872
8.82k
  Curl_safefree(imap->custom_params);
1873
  /* Clear the transfer mode for the next request */
1874
8.82k
  imap->transfer = PPTRANSFER_BODY;
1875
8.82k
}
1876
1877
static void imap_easy_dtor(void *key, size_t klen, void *entry)
1878
6.40k
{
1879
6.40k
  struct IMAP *imap = entry;
1880
6.40k
  (void)key;
1881
6.40k
  (void)klen;
1882
6.40k
  imap_easy_reset(imap);
1883
6.40k
  free(imap);
1884
6.40k
}
1885
1886
static void imap_conn_dtor(void *key, size_t klen, void *entry)
1887
6.40k
{
1888
6.40k
  struct imap_conn *imapc = entry;
1889
6.40k
  (void)key;
1890
6.40k
  (void)klen;
1891
6.40k
  Curl_pp_disconnect(&imapc->pp);
1892
6.40k
  curlx_dyn_free(&imapc->dyn);
1893
6.40k
  Curl_safefree(imapc->mailbox);
1894
6.40k
  Curl_safefree(imapc->mailbox_uidvalidity);
1895
6.40k
  free(imapc);
1896
6.40k
}
1897
1898
static CURLcode imap_setup_connection(struct Curl_easy *data,
1899
                                      struct connectdata *conn)
1900
6.40k
{
1901
6.40k
  struct imap_conn *imapc;
1902
6.40k
  struct pingpong *pp;
1903
6.40k
  struct IMAP *imap;
1904
1905
6.40k
  imapc = calloc(1, sizeof(*imapc));
1906
6.40k
  if(!imapc)
1907
0
    return CURLE_OUT_OF_MEMORY;
1908
1909
6.40k
  pp = &imapc->pp;
1910
6.40k
  PINGPONG_SETUP(pp, imap_pp_statemachine, imap_endofresp);
1911
1912
  /* Set the default preferred authentication type and mechanism */
1913
6.40k
  imapc->preftype = IMAP_TYPE_ANY;
1914
6.40k
  Curl_sasl_init(&imapc->sasl, data, &saslimap);
1915
1916
6.40k
  curlx_dyn_init(&imapc->dyn, DYN_IMAP_CMD);
1917
6.40k
  Curl_pp_init(pp);
1918
1919
6.40k
  if(Curl_conn_meta_set(conn, CURL_META_IMAP_CONN, imapc, imap_conn_dtor))
1920
0
    return CURLE_OUT_OF_MEMORY;
1921
1922
6.40k
  imap = calloc(1, sizeof(struct IMAP));
1923
6.40k
  if(!imap ||
1924
6.40k
     Curl_meta_set(data, CURL_META_IMAP_EASY, imap, imap_easy_dtor))
1925
0
    return CURLE_OUT_OF_MEMORY;
1926
1927
6.40k
  return CURLE_OK;
1928
6.40k
}
1929
1930
/***********************************************************************
1931
 *
1932
 * imap_sendf()
1933
 *
1934
 * Sends the formatted string as an IMAP command to the server.
1935
 *
1936
 * Designed to never block.
1937
 */
1938
static CURLcode imap_sendf(struct Curl_easy *data,
1939
                           struct imap_conn *imapc,
1940
                           const char *fmt, ...)
1941
5.03k
{
1942
5.03k
  CURLcode result = CURLE_OK;
1943
1944
5.03k
  DEBUGASSERT(fmt);
1945
1946
  /* Calculate the tag based on the connection ID and command ID */
1947
5.03k
  msnprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
1948
5.03k
            'A' + curlx_sltosi((long)(data->conn->connection_id % 26)),
1949
5.03k
            ++imapc->cmdid);
1950
1951
  /* start with a blank buffer */
1952
5.03k
  curlx_dyn_reset(&imapc->dyn);
1953
1954
  /* append tag + space + fmt */
1955
5.03k
  result = curlx_dyn_addf(&imapc->dyn, "%s %s", imapc->resptag, fmt);
1956
5.03k
  if(!result) {
1957
5.03k
    va_list ap;
1958
5.03k
    va_start(ap, fmt);
1959
5.03k
#ifdef __clang__
1960
5.03k
#pragma clang diagnostic push
1961
5.03k
#pragma clang diagnostic ignored "-Wformat-nonliteral"
1962
5.03k
#endif
1963
5.03k
    result = Curl_pp_vsendf(data, &imapc->pp, curlx_dyn_ptr(&imapc->dyn), ap);
1964
5.03k
#ifdef __clang__
1965
5.03k
#pragma clang diagnostic pop
1966
5.03k
#endif
1967
5.03k
    va_end(ap);
1968
5.03k
  }
1969
5.03k
  return result;
1970
5.03k
}
1971
1972
/***********************************************************************
1973
 *
1974
 * imap_atom()
1975
 *
1976
 * Checks the input string for characters that need escaping and returns an
1977
 * atom ready for sending to the server.
1978
 *
1979
 * The returned string needs to be freed.
1980
 *
1981
 */
1982
static char *imap_atom(const char *str, bool escape_only)
1983
1.50k
{
1984
1.50k
  struct dynbuf line;
1985
1.50k
  size_t nclean;
1986
1.50k
  size_t len;
1987
1988
1.50k
  if(!str)
1989
0
    return NULL;
1990
1991
1.50k
  len = strlen(str);
1992
1.50k
  nclean = strcspn(str, "() {%*]\\\"");
1993
1.50k
  if(len == nclean)
1994
    /* nothing to escape, return a strdup */
1995
1.11k
    return strdup(str);
1996
1997
391
  curlx_dyn_init(&line, 2000);
1998
1999
391
  if(!escape_only && curlx_dyn_addn(&line, "\"", 1))
2000
0
    return NULL;
2001
2002
44.4k
  while(*str) {
2003
44.0k
    if((*str == '\\' || *str == '"') &&
2004
44.0k
       curlx_dyn_addn(&line, "\\", 1))
2005
1
      return NULL;
2006
44.0k
    if(curlx_dyn_addn(&line, str, 1))
2007
13
      return NULL;
2008
44.0k
    str++;
2009
44.0k
  }
2010
2011
377
  if(!escape_only && curlx_dyn_addn(&line, "\"", 1))
2012
1
    return NULL;
2013
2014
376
  return curlx_dyn_ptr(&line);
2015
377
}
2016
2017
/***********************************************************************
2018
 *
2019
 * imap_is_bchar()
2020
 *
2021
 * Portable test of whether the specified char is a "bchar" as defined in the
2022
 * grammar of RFC-5092.
2023
 */
2024
static bool imap_is_bchar(char ch)
2025
139k
{
2026
  /* Performing the alnum check with this macro is faster because of ASCII
2027
     arithmetic */
2028
139k
  if(ISALNUM(ch))
2029
76.0k
    return TRUE;
2030
2031
63.8k
  switch(ch) {
2032
    /* bchar */
2033
7.94k
    case ':': case '@': case '/':
2034
    /* bchar -> achar */
2035
9.30k
    case '&': case '=':
2036
    /* bchar -> achar -> uchar -> unreserved (without alphanumeric) */
2037
15.3k
    case '-': case '.': case '_': case '~':
2038
    /* bchar -> achar -> uchar -> sub-delims-sh */
2039
17.7k
    case '!': case '$': case '\'': case '(': case ')': case '*':
2040
18.7k
    case '+': case ',':
2041
    /* bchar -> achar -> uchar -> pct-encoded */
2042
61.3k
    case '%': /* HEXDIG chars are already included above */
2043
61.3k
      return TRUE;
2044
2045
2.48k
    default:
2046
2.48k
      return FALSE;
2047
63.8k
  }
2048
63.8k
}
2049
2050
/***********************************************************************
2051
 *
2052
 * imap_parse_url_options()
2053
 *
2054
 * Parse the URL login options.
2055
 */
2056
static CURLcode imap_parse_url_options(struct connectdata *conn,
2057
                                       struct imap_conn *imapc)
2058
2.42k
{
2059
2.42k
  CURLcode result = CURLE_OK;
2060
2.42k
  const char *ptr = conn->options;
2061
2.42k
  bool prefer_login = FALSE;
2062
2063
2.52k
  while(!result && ptr && *ptr) {
2064
98
    const char *key = ptr;
2065
98
    const char *value;
2066
2067
520k
    while(*ptr && *ptr != '=')
2068
520k
      ptr++;
2069
2070
98
    value = ptr + 1;
2071
2072
1.33k
    while(*ptr && *ptr != ';')
2073
1.23k
      ptr++;
2074
2075
98
    if(curl_strnequal(key, "AUTH=+LOGIN", 11)) {
2076
      /* User prefers plaintext LOGIN over any SASL, including SASL LOGIN */
2077
0
      prefer_login = TRUE;
2078
0
      imapc->sasl.prefmech = SASL_AUTH_NONE;
2079
0
    }
2080
98
    else if(curl_strnequal(key, "AUTH=", 5)) {
2081
66
      prefer_login = FALSE;
2082
66
      result = Curl_sasl_parse_url_auth_option(&imapc->sasl,
2083
66
                                               value, ptr - value);
2084
66
    }
2085
32
    else {
2086
32
      prefer_login = FALSE;
2087
32
      result = CURLE_URL_MALFORMAT;
2088
32
    }
2089
2090
98
    if(*ptr == ';')
2091
18
      ptr++;
2092
98
  }
2093
2094
2.42k
  if(prefer_login)
2095
0
    imapc->preftype = IMAP_TYPE_CLEARTEXT;
2096
2.42k
  else {
2097
2.42k
    switch(imapc->sasl.prefmech) {
2098
50
    case SASL_AUTH_NONE:
2099
50
      imapc->preftype = IMAP_TYPE_NONE;
2100
50
      break;
2101
2.35k
    case SASL_AUTH_DEFAULT:
2102
2.35k
      imapc->preftype = IMAP_TYPE_ANY;
2103
2.35k
      break;
2104
23
    default:
2105
23
      imapc->preftype = IMAP_TYPE_SASL;
2106
23
      break;
2107
2.42k
    }
2108
2.42k
  }
2109
2110
2.42k
  return result;
2111
2.42k
}
2112
2113
/***********************************************************************
2114
 *
2115
 * imap_parse_url_path()
2116
 *
2117
 * Parse the URL path into separate path components.
2118
 *
2119
 */
2120
static CURLcode imap_parse_url_path(struct Curl_easy *data,
2121
                                    struct IMAP *imap)
2122
1.59k
{
2123
  /* The imap struct is already initialised in imap_connect() */
2124
1.59k
  CURLcode result = CURLE_OK;
2125
1.59k
  const char *begin = &data->state.up.path[1]; /* skip leading slash */
2126
1.59k
  const char *ptr = begin;
2127
2128
  /* See how much of the URL is a valid path and decode it */
2129
131k
  while(imap_is_bchar(*ptr))
2130
130k
    ptr++;
2131
2132
1.59k
  if(ptr != begin) {
2133
    /* Remove the trailing slash if present */
2134
1.43k
    const char *end = ptr;
2135
1.43k
    if(end > begin && end[-1] == '/')
2136
79
      end--;
2137
2138
1.43k
    result = Curl_urldecode(begin, end - begin, &imap->mailbox, NULL,
2139
1.43k
                            REJECT_CTRL);
2140
1.43k
    if(result)
2141
1
      return result;
2142
1.43k
  }
2143
166
  else
2144
166
    imap->mailbox = NULL;
2145
2146
  /* There can be any number of parameters in the form ";NAME=VALUE" */
2147
2.47k
  while(*ptr == ';') {
2148
901
    char *name;
2149
901
    char *value;
2150
901
    size_t valuelen;
2151
2152
    /* Find the length of the name parameter */
2153
901
    begin = ++ptr;
2154
3.84k
    while(*ptr && *ptr != '=')
2155
2.94k
      ptr++;
2156
2157
901
    if(!*ptr)
2158
8
      return CURLE_URL_MALFORMAT;
2159
2160
    /* Decode the name parameter */
2161
893
    result = Curl_urldecode(begin, ptr - begin, &name, NULL,
2162
893
                            REJECT_CTRL);
2163
893
    if(result)
2164
1
      return result;
2165
2166
    /* Find the length of the value parameter */
2167
892
    begin = ++ptr;
2168
7.89k
    while(imap_is_bchar(*ptr))
2169
7.00k
      ptr++;
2170
2171
    /* Decode the value parameter */
2172
892
    result = Curl_urldecode(begin, ptr - begin, &value, &valuelen,
2173
892
                            REJECT_CTRL);
2174
892
    if(result) {
2175
2
      free(name);
2176
2
      return result;
2177
2
    }
2178
2179
890
    DEBUGF(infof(data, "IMAP URL parameter '%s' = '%s'", name, value));
2180
2181
    /* Process the known hierarchical parameters (UIDVALIDITY, UID, SECTION and
2182
       PARTIAL) stripping of the trailing slash character if it is present.
2183
2184
       Note: Unknown parameters trigger a URL_MALFORMAT error. */
2185
890
    if(curl_strequal(name, "UIDVALIDITY") && !imap->uidvalidity) {
2186
0
      if(valuelen > 0 && value[valuelen - 1] == '/')
2187
0
        value[valuelen - 1] = '\0';
2188
2189
0
      imap->uidvalidity = value;
2190
0
      value = NULL;
2191
0
    }
2192
890
    else if(curl_strequal(name, "UID") && !imap->uid) {
2193
866
      if(valuelen > 0 && value[valuelen - 1] == '/')
2194
32
        value[valuelen - 1] = '\0';
2195
2196
866
      imap->uid = value;
2197
866
      value = NULL;
2198
866
    }
2199
24
    else if(curl_strequal(name, "MAILINDEX") && !imap->mindex) {
2200
8
      if(valuelen > 0 && value[valuelen - 1] == '/')
2201
3
        value[valuelen - 1] = '\0';
2202
2203
8
      imap->mindex = value;
2204
8
      value = NULL;
2205
8
    }
2206
16
    else if(curl_strequal(name, "SECTION") && !imap->section) {
2207
0
      if(valuelen > 0 && value[valuelen - 1] == '/')
2208
0
        value[valuelen - 1] = '\0';
2209
2210
0
      imap->section = value;
2211
0
      value = NULL;
2212
0
    }
2213
16
    else if(curl_strequal(name, "PARTIAL") && !imap->partial) {
2214
0
      if(valuelen > 0 && value[valuelen - 1] == '/')
2215
0
        value[valuelen - 1] = '\0';
2216
2217
0
      imap->partial = value;
2218
0
      value = NULL;
2219
0
    }
2220
16
    else {
2221
16
      free(name);
2222
16
      free(value);
2223
2224
16
      return CURLE_URL_MALFORMAT;
2225
16
    }
2226
2227
874
    free(name);
2228
874
    free(value);
2229
874
  }
2230
2231
  /* Does the URL contain a query parameter? Only valid when we have a mailbox
2232
     and no UID as per RFC-5092 */
2233
1.56k
  if(imap->mailbox && !imap->uid && !imap->mindex) {
2234
    /* Get the query parameter, URL decoded */
2235
548
    (void)curl_url_get(data->state.uh, CURLUPART_QUERY, &imap->query,
2236
548
                       CURLU_URLDECODE);
2237
548
  }
2238
2239
  /* Any extra stuff at the end of the URL is an error */
2240
1.56k
  if(*ptr)
2241
8
    return CURLE_URL_MALFORMAT;
2242
2243
1.56k
  return CURLE_OK;
2244
1.56k
}
2245
2246
/***********************************************************************
2247
 *
2248
 * imap_parse_custom_request()
2249
 *
2250
 * Parse the custom request.
2251
 */
2252
static CURLcode imap_parse_custom_request(struct Curl_easy *data,
2253
                                          struct IMAP *imap)
2254
1.56k
{
2255
1.56k
  CURLcode result = CURLE_OK;
2256
1.56k
  const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2257
2258
1.56k
  if(custom) {
2259
    /* URL decode the custom request */
2260
160
    result = Curl_urldecode(custom, 0, &imap->custom, NULL, REJECT_CTRL);
2261
2262
    /* Extract the parameters if specified */
2263
160
    if(!result) {
2264
158
      const char *params = imap->custom;
2265
2266
812
      while(*params && *params != ' ')
2267
654
        params++;
2268
2269
158
      if(*params) {
2270
21
        imap->custom_params = strdup(params);
2271
21
        imap->custom[params - imap->custom] = '\0';
2272
2273
21
        if(!imap->custom_params)
2274
0
          result = CURLE_OUT_OF_MEMORY;
2275
21
      }
2276
158
    }
2277
160
  }
2278
2279
1.56k
  return result;
2280
1.56k
}
2281
2282
#endif /* CURL_DISABLE_IMAP */