Coverage Report

Created: 2023-06-07 07:02

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