Coverage Report

Created: 2024-02-25 06:14

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