Coverage Report

Created: 2025-06-09 07:02

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